github.com/mweagle/Sparta@v1.15.0/resources/describe/cytoscape.js/dist/cytoscape.cjs.js (about)

     1  /**
     2   * Copyright (c) 2016-2020, The Cytoscape Consortium.
     3   *
     4   * Permission is hereby granted, free of charge, to any person obtaining a copy of
     5   * this software and associated documentation files (the “Software”), to deal in
     6   * the Software without restriction, including without limitation the rights to
     7   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
     8   * of the Software, and to permit persons to whom the Software is furnished to do
     9   * so, subject to the following conditions:
    10   *
    11   * The above copyright notice and this permission notice shall be included in all
    12   * copies or substantial portions of the Software.
    13   *
    14   * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   * SOFTWARE.
    21   */
    22  
    23  'use strict';
    24  
    25  function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
    26  
    27  var util = _interopDefault(require('lodash.debounce'));
    28  var Heap = _interopDefault(require('heap'));
    29  
    30  function _typeof(obj) {
    31    if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
    32      _typeof = function (obj) {
    33        return typeof obj;
    34      };
    35    } else {
    36      _typeof = function (obj) {
    37        return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
    38      };
    39    }
    40  
    41    return _typeof(obj);
    42  }
    43  
    44  function _classCallCheck(instance, Constructor) {
    45    if (!(instance instanceof Constructor)) {
    46      throw new TypeError("Cannot call a class as a function");
    47    }
    48  }
    49  
    50  function _defineProperties(target, props) {
    51    for (var i = 0; i < props.length; i++) {
    52      var descriptor = props[i];
    53      descriptor.enumerable = descriptor.enumerable || false;
    54      descriptor.configurable = true;
    55      if ("value" in descriptor) descriptor.writable = true;
    56      Object.defineProperty(target, descriptor.key, descriptor);
    57    }
    58  }
    59  
    60  function _createClass(Constructor, protoProps, staticProps) {
    61    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    62    if (staticProps) _defineProperties(Constructor, staticProps);
    63    return Constructor;
    64  }
    65  
    66  function _defineProperty(obj, key, value) {
    67    if (key in obj) {
    68      Object.defineProperty(obj, key, {
    69        value: value,
    70        enumerable: true,
    71        configurable: true,
    72        writable: true
    73      });
    74    } else {
    75      obj[key] = value;
    76    }
    77  
    78    return obj;
    79  }
    80  
    81  function _slicedToArray(arr, i) {
    82    return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
    83  }
    84  
    85  function _arrayWithHoles(arr) {
    86    if (Array.isArray(arr)) return arr;
    87  }
    88  
    89  function _iterableToArrayLimit(arr, i) {
    90    var _arr = [];
    91    var _n = true;
    92    var _d = false;
    93    var _e = undefined;
    94  
    95    try {
    96      for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
    97        _arr.push(_s.value);
    98  
    99        if (i && _arr.length === i) break;
   100      }
   101    } catch (err) {
   102      _d = true;
   103      _e = err;
   104    } finally {
   105      try {
   106        if (!_n && _i["return"] != null) _i["return"]();
   107      } finally {
   108        if (_d) throw _e;
   109      }
   110    }
   111  
   112    return _arr;
   113  }
   114  
   115  function _nonIterableRest() {
   116    throw new TypeError("Invalid attempt to destructure non-iterable instance");
   117  }
   118  
   119  var window$1 = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
   120  
   121  var navigator = window$1 ? window$1.navigator : null;
   122  var document$1 = window$1 ? window$1.document : null;
   123  
   124  var typeofstr = _typeof('');
   125  
   126  var typeofobj = _typeof({});
   127  
   128  var typeoffn = _typeof(function () {});
   129  
   130  var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
   131  
   132  var instanceStr = function instanceStr(obj) {
   133    return obj && obj.instanceString && fn(obj.instanceString) ? obj.instanceString() : null;
   134  };
   135  
   136  var string = function string(obj) {
   137    return obj != null && _typeof(obj) == typeofstr;
   138  };
   139  var fn = function fn(obj) {
   140    return obj != null && _typeof(obj) === typeoffn;
   141  };
   142  var array = function array(obj) {
   143    return Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array;
   144  };
   145  var plainObject = function plainObject(obj) {
   146    return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
   147  };
   148  var object = function object(obj) {
   149    return obj != null && _typeof(obj) === typeofobj;
   150  };
   151  var number = function number(obj) {
   152    return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
   153  };
   154  var integer = function integer(obj) {
   155    return number(obj) && Math.floor(obj) === obj;
   156  };
   157  var htmlElement = function htmlElement(obj) {
   158    if ('undefined' === typeofhtmlele) {
   159      return undefined;
   160    } else {
   161      return null != obj && obj instanceof HTMLElement;
   162    }
   163  };
   164  var elementOrCollection = function elementOrCollection(obj) {
   165    return element(obj) || collection(obj);
   166  };
   167  var element = function element(obj) {
   168    return instanceStr(obj) === 'collection' && obj._private.single;
   169  };
   170  var collection = function collection(obj) {
   171    return instanceStr(obj) === 'collection' && !obj._private.single;
   172  };
   173  var core = function core(obj) {
   174    return instanceStr(obj) === 'core';
   175  };
   176  var stylesheet = function stylesheet(obj) {
   177    return instanceStr(obj) === 'stylesheet';
   178  };
   179  var event = function event(obj) {
   180    return instanceStr(obj) === 'event';
   181  };
   182  var emptyString = function emptyString(obj) {
   183    if (obj === undefined || obj === null) {
   184      // null is empty
   185      return true;
   186    } else if (obj === '' || obj.match(/^\s+$/)) {
   187      return true; // empty string is empty
   188    }
   189  
   190    return false; // otherwise, we don't know what we've got
   191  };
   192  var domElement = function domElement(obj) {
   193    if (typeof HTMLElement === 'undefined') {
   194      return false; // we're not in a browser so it doesn't matter
   195    } else {
   196      return obj instanceof HTMLElement;
   197    }
   198  };
   199  var boundingBox = function boundingBox(obj) {
   200    return plainObject(obj) && number(obj.x1) && number(obj.x2) && number(obj.y1) && number(obj.y2);
   201  };
   202  var promise = function promise(obj) {
   203    return object(obj) && fn(obj.then);
   204  };
   205  var ms = function ms() {
   206    return navigator && navigator.userAgent.match(/msie|trident|edge/i);
   207  }; // probably a better way to detect this...
   208  
   209  var memoize = function memoize(fn, keyFn) {
   210    if (!keyFn) {
   211      keyFn = function keyFn() {
   212        if (arguments.length === 1) {
   213          return arguments[0];
   214        } else if (arguments.length === 0) {
   215          return 'undefined';
   216        }
   217  
   218        var args = [];
   219  
   220        for (var i = 0; i < arguments.length; i++) {
   221          args.push(arguments[i]);
   222        }
   223  
   224        return args.join('$');
   225      };
   226    }
   227  
   228    var memoizedFn = function memoizedFn() {
   229      var self = this;
   230      var args = arguments;
   231      var ret;
   232      var k = keyFn.apply(self, args);
   233      var cache = memoizedFn.cache;
   234  
   235      if (!(ret = cache[k])) {
   236        ret = cache[k] = fn.apply(self, args);
   237      }
   238  
   239      return ret;
   240    };
   241  
   242    memoizedFn.cache = {};
   243    return memoizedFn;
   244  };
   245  
   246  var camel2dash = memoize(function (str) {
   247    return str.replace(/([A-Z])/g, function (v) {
   248      return '-' + v.toLowerCase();
   249    });
   250  });
   251  var dash2camel = memoize(function (str) {
   252    return str.replace(/(-\w)/g, function (v) {
   253      return v[1].toUpperCase();
   254    });
   255  });
   256  var prependCamel = memoize(function (prefix, str) {
   257    return prefix + str[0].toUpperCase() + str.substring(1);
   258  }, function (prefix, str) {
   259    return prefix + '$' + str;
   260  });
   261  var capitalize = function capitalize(str) {
   262    if (emptyString(str)) {
   263      return str;
   264    }
   265  
   266    return str.charAt(0).toUpperCase() + str.substring(1);
   267  };
   268  
   269  var number$1 = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
   270  var rgba = 'rgb[a]?\\((' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)(?:\\s*,\\s*(' + number$1 + '))?\\)';
   271  var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
   272  var hsla = 'hsl[a]?\\((' + number$1 + ')\\s*,\\s*(' + number$1 + '[%])\\s*,\\s*(' + number$1 + '[%])(?:\\s*,\\s*(' + number$1 + '))?\\)';
   273  var hslaNoBackRefs = 'hsl[a]?\\((?:' + number$1 + ')\\s*,\\s*(?:' + number$1 + '[%])\\s*,\\s*(?:' + number$1 + '[%])(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
   274  var hex3 = '\\#[0-9a-fA-F]{3}';
   275  var hex6 = '\\#[0-9a-fA-F]{6}';
   276  
   277  var ascending = function ascending(a, b) {
   278    if (a < b) {
   279      return -1;
   280    } else if (a > b) {
   281      return 1;
   282    } else {
   283      return 0;
   284    }
   285  };
   286  var descending = function descending(a, b) {
   287    return -1 * ascending(a, b);
   288  };
   289  
   290  var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
   291    var args = arguments;
   292  
   293    for (var i = 1; i < args.length; i++) {
   294      var obj = args[i];
   295  
   296      if (obj == null) {
   297        continue;
   298      }
   299  
   300      var keys = Object.keys(obj);
   301  
   302      for (var j = 0; j < keys.length; j++) {
   303        var k = keys[j];
   304        tgt[k] = obj[k];
   305      }
   306    }
   307  
   308    return tgt;
   309  };
   310  
   311  var hex2tuple = function hex2tuple(hex) {
   312    if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
   313      return;
   314    }
   315  
   316    var shortHex = hex.length === 4;
   317    var r, g, b;
   318    var base = 16;
   319  
   320    if (shortHex) {
   321      r = parseInt(hex[1] + hex[1], base);
   322      g = parseInt(hex[2] + hex[2], base);
   323      b = parseInt(hex[3] + hex[3], base);
   324    } else {
   325      r = parseInt(hex[1] + hex[2], base);
   326      g = parseInt(hex[3] + hex[4], base);
   327      b = parseInt(hex[5] + hex[6], base);
   328    }
   329  
   330    return [r, g, b];
   331  }; // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
   332  
   333  var hsl2tuple = function hsl2tuple(hsl) {
   334    var ret;
   335    var h, s, l, a, r, g, b;
   336  
   337    function hue2rgb(p, q, t) {
   338      if (t < 0) t += 1;
   339      if (t > 1) t -= 1;
   340      if (t < 1 / 6) return p + (q - p) * 6 * t;
   341      if (t < 1 / 2) return q;
   342      if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
   343      return p;
   344    }
   345  
   346    var m = new RegExp('^' + hsla + '$').exec(hsl);
   347  
   348    if (m) {
   349      // get hue
   350      h = parseInt(m[1]);
   351  
   352      if (h < 0) {
   353        h = (360 - -1 * h % 360) % 360;
   354      } else if (h > 360) {
   355        h = h % 360;
   356      }
   357  
   358      h /= 360; // normalise on [0, 1]
   359  
   360      s = parseFloat(m[2]);
   361  
   362      if (s < 0 || s > 100) {
   363        return;
   364      } // saturation is [0, 100]
   365  
   366  
   367      s = s / 100; // normalise on [0, 1]
   368  
   369      l = parseFloat(m[3]);
   370  
   371      if (l < 0 || l > 100) {
   372        return;
   373      } // lightness is [0, 100]
   374  
   375  
   376      l = l / 100; // normalise on [0, 1]
   377  
   378      a = m[4];
   379  
   380      if (a !== undefined) {
   381        a = parseFloat(a);
   382  
   383        if (a < 0 || a > 1) {
   384          return;
   385        } // alpha is [0, 1]
   386  
   387      } // now, convert to rgb
   388      // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
   389  
   390  
   391      if (s === 0) {
   392        r = g = b = Math.round(l * 255); // achromatic
   393      } else {
   394        var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
   395        var p = 2 * l - q;
   396        r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
   397        g = Math.round(255 * hue2rgb(p, q, h));
   398        b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
   399      }
   400  
   401      ret = [r, g, b, a];
   402    }
   403  
   404    return ret;
   405  }; // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
   406  
   407  var rgb2tuple = function rgb2tuple(rgb) {
   408    var ret;
   409    var m = new RegExp('^' + rgba + '$').exec(rgb);
   410  
   411    if (m) {
   412      ret = [];
   413      var isPct = [];
   414  
   415      for (var i = 1; i <= 3; i++) {
   416        var channel = m[i];
   417  
   418        if (channel[channel.length - 1] === '%') {
   419          isPct[i] = true;
   420        }
   421  
   422        channel = parseFloat(channel);
   423  
   424        if (isPct[i]) {
   425          channel = channel / 100 * 255; // normalise to [0, 255]
   426        }
   427  
   428        if (channel < 0 || channel > 255) {
   429          return;
   430        } // invalid channel value
   431  
   432  
   433        ret.push(Math.floor(channel));
   434      }
   435  
   436      var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
   437      var allArePct = isPct[1] && isPct[2] && isPct[3];
   438  
   439      if (atLeastOneIsPct && !allArePct) {
   440        return;
   441      } // must all be percent values if one is
   442  
   443  
   444      var alpha = m[4];
   445  
   446      if (alpha !== undefined) {
   447        alpha = parseFloat(alpha);
   448  
   449        if (alpha < 0 || alpha > 1) {
   450          return;
   451        } // invalid alpha value
   452  
   453  
   454        ret.push(alpha);
   455      }
   456    }
   457  
   458    return ret;
   459  };
   460  var colorname2tuple = function colorname2tuple(color) {
   461    return colors[color.toLowerCase()];
   462  };
   463  var color2tuple = function color2tuple(color) {
   464    return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
   465  };
   466  var colors = {
   467    // special colour names
   468    transparent: [0, 0, 0, 0],
   469    // NB alpha === 0
   470    // regular colours
   471    aliceblue: [240, 248, 255],
   472    antiquewhite: [250, 235, 215],
   473    aqua: [0, 255, 255],
   474    aquamarine: [127, 255, 212],
   475    azure: [240, 255, 255],
   476    beige: [245, 245, 220],
   477    bisque: [255, 228, 196],
   478    black: [0, 0, 0],
   479    blanchedalmond: [255, 235, 205],
   480    blue: [0, 0, 255],
   481    blueviolet: [138, 43, 226],
   482    brown: [165, 42, 42],
   483    burlywood: [222, 184, 135],
   484    cadetblue: [95, 158, 160],
   485    chartreuse: [127, 255, 0],
   486    chocolate: [210, 105, 30],
   487    coral: [255, 127, 80],
   488    cornflowerblue: [100, 149, 237],
   489    cornsilk: [255, 248, 220],
   490    crimson: [220, 20, 60],
   491    cyan: [0, 255, 255],
   492    darkblue: [0, 0, 139],
   493    darkcyan: [0, 139, 139],
   494    darkgoldenrod: [184, 134, 11],
   495    darkgray: [169, 169, 169],
   496    darkgreen: [0, 100, 0],
   497    darkgrey: [169, 169, 169],
   498    darkkhaki: [189, 183, 107],
   499    darkmagenta: [139, 0, 139],
   500    darkolivegreen: [85, 107, 47],
   501    darkorange: [255, 140, 0],
   502    darkorchid: [153, 50, 204],
   503    darkred: [139, 0, 0],
   504    darksalmon: [233, 150, 122],
   505    darkseagreen: [143, 188, 143],
   506    darkslateblue: [72, 61, 139],
   507    darkslategray: [47, 79, 79],
   508    darkslategrey: [47, 79, 79],
   509    darkturquoise: [0, 206, 209],
   510    darkviolet: [148, 0, 211],
   511    deeppink: [255, 20, 147],
   512    deepskyblue: [0, 191, 255],
   513    dimgray: [105, 105, 105],
   514    dimgrey: [105, 105, 105],
   515    dodgerblue: [30, 144, 255],
   516    firebrick: [178, 34, 34],
   517    floralwhite: [255, 250, 240],
   518    forestgreen: [34, 139, 34],
   519    fuchsia: [255, 0, 255],
   520    gainsboro: [220, 220, 220],
   521    ghostwhite: [248, 248, 255],
   522    gold: [255, 215, 0],
   523    goldenrod: [218, 165, 32],
   524    gray: [128, 128, 128],
   525    grey: [128, 128, 128],
   526    green: [0, 128, 0],
   527    greenyellow: [173, 255, 47],
   528    honeydew: [240, 255, 240],
   529    hotpink: [255, 105, 180],
   530    indianred: [205, 92, 92],
   531    indigo: [75, 0, 130],
   532    ivory: [255, 255, 240],
   533    khaki: [240, 230, 140],
   534    lavender: [230, 230, 250],
   535    lavenderblush: [255, 240, 245],
   536    lawngreen: [124, 252, 0],
   537    lemonchiffon: [255, 250, 205],
   538    lightblue: [173, 216, 230],
   539    lightcoral: [240, 128, 128],
   540    lightcyan: [224, 255, 255],
   541    lightgoldenrodyellow: [250, 250, 210],
   542    lightgray: [211, 211, 211],
   543    lightgreen: [144, 238, 144],
   544    lightgrey: [211, 211, 211],
   545    lightpink: [255, 182, 193],
   546    lightsalmon: [255, 160, 122],
   547    lightseagreen: [32, 178, 170],
   548    lightskyblue: [135, 206, 250],
   549    lightslategray: [119, 136, 153],
   550    lightslategrey: [119, 136, 153],
   551    lightsteelblue: [176, 196, 222],
   552    lightyellow: [255, 255, 224],
   553    lime: [0, 255, 0],
   554    limegreen: [50, 205, 50],
   555    linen: [250, 240, 230],
   556    magenta: [255, 0, 255],
   557    maroon: [128, 0, 0],
   558    mediumaquamarine: [102, 205, 170],
   559    mediumblue: [0, 0, 205],
   560    mediumorchid: [186, 85, 211],
   561    mediumpurple: [147, 112, 219],
   562    mediumseagreen: [60, 179, 113],
   563    mediumslateblue: [123, 104, 238],
   564    mediumspringgreen: [0, 250, 154],
   565    mediumturquoise: [72, 209, 204],
   566    mediumvioletred: [199, 21, 133],
   567    midnightblue: [25, 25, 112],
   568    mintcream: [245, 255, 250],
   569    mistyrose: [255, 228, 225],
   570    moccasin: [255, 228, 181],
   571    navajowhite: [255, 222, 173],
   572    navy: [0, 0, 128],
   573    oldlace: [253, 245, 230],
   574    olive: [128, 128, 0],
   575    olivedrab: [107, 142, 35],
   576    orange: [255, 165, 0],
   577    orangered: [255, 69, 0],
   578    orchid: [218, 112, 214],
   579    palegoldenrod: [238, 232, 170],
   580    palegreen: [152, 251, 152],
   581    paleturquoise: [175, 238, 238],
   582    palevioletred: [219, 112, 147],
   583    papayawhip: [255, 239, 213],
   584    peachpuff: [255, 218, 185],
   585    peru: [205, 133, 63],
   586    pink: [255, 192, 203],
   587    plum: [221, 160, 221],
   588    powderblue: [176, 224, 230],
   589    purple: [128, 0, 128],
   590    red: [255, 0, 0],
   591    rosybrown: [188, 143, 143],
   592    royalblue: [65, 105, 225],
   593    saddlebrown: [139, 69, 19],
   594    salmon: [250, 128, 114],
   595    sandybrown: [244, 164, 96],
   596    seagreen: [46, 139, 87],
   597    seashell: [255, 245, 238],
   598    sienna: [160, 82, 45],
   599    silver: [192, 192, 192],
   600    skyblue: [135, 206, 235],
   601    slateblue: [106, 90, 205],
   602    slategray: [112, 128, 144],
   603    slategrey: [112, 128, 144],
   604    snow: [255, 250, 250],
   605    springgreen: [0, 255, 127],
   606    steelblue: [70, 130, 180],
   607    tan: [210, 180, 140],
   608    teal: [0, 128, 128],
   609    thistle: [216, 191, 216],
   610    tomato: [255, 99, 71],
   611    turquoise: [64, 224, 208],
   612    violet: [238, 130, 238],
   613    wheat: [245, 222, 179],
   614    white: [255, 255, 255],
   615    whitesmoke: [245, 245, 245],
   616    yellow: [255, 255, 0],
   617    yellowgreen: [154, 205, 50]
   618  };
   619  
   620  var setMap = function setMap(options) {
   621    var obj = options.map;
   622    var keys = options.keys;
   623    var l = keys.length;
   624  
   625    for (var i = 0; i < l; i++) {
   626      var key = keys[i];
   627  
   628      if (plainObject(key)) {
   629        throw Error('Tried to set map with object key');
   630      }
   631  
   632      if (i < keys.length - 1) {
   633        // extend the map if necessary
   634        if (obj[key] == null) {
   635          obj[key] = {};
   636        }
   637  
   638        obj = obj[key];
   639      } else {
   640        // set the value
   641        obj[key] = options.value;
   642      }
   643    }
   644  }; // gets the value in a map even if it's not built in places
   645  
   646  var getMap = function getMap(options) {
   647    var obj = options.map;
   648    var keys = options.keys;
   649    var l = keys.length;
   650  
   651    for (var i = 0; i < l; i++) {
   652      var key = keys[i];
   653  
   654      if (plainObject(key)) {
   655        throw Error('Tried to get map with object key');
   656      }
   657  
   658      obj = obj[key];
   659  
   660      if (obj == null) {
   661        return obj;
   662      }
   663    }
   664  
   665    return obj;
   666  }; // deletes the entry in the map
   667  
   668  var performance = window$1 ? window$1.performance : null;
   669  var pnow = performance && performance.now ? function () {
   670    return performance.now();
   671  } : function () {
   672    return Date.now();
   673  };
   674  
   675  var raf = function () {
   676    if (window$1) {
   677      if (window$1.requestAnimationFrame) {
   678        return function (fn) {
   679          window$1.requestAnimationFrame(fn);
   680        };
   681      } else if (window$1.mozRequestAnimationFrame) {
   682        return function (fn) {
   683          window$1.mozRequestAnimationFrame(fn);
   684        };
   685      } else if (window$1.webkitRequestAnimationFrame) {
   686        return function (fn) {
   687          window$1.webkitRequestAnimationFrame(fn);
   688        };
   689      } else if (window$1.msRequestAnimationFrame) {
   690        return function (fn) {
   691          window$1.msRequestAnimationFrame(fn);
   692        };
   693      }
   694    }
   695  
   696    return function (fn) {
   697      if (fn) {
   698        setTimeout(function () {
   699          fn(pnow());
   700        }, 1000 / 60);
   701      }
   702    };
   703  }();
   704  
   705  var requestAnimationFrame = function requestAnimationFrame(fn) {
   706    return raf(fn);
   707  };
   708  var performanceNow = pnow;
   709  
   710  var DEFAULT_SEED = 5381;
   711  var hashIterableInts = function hashIterableInts(iterator) {
   712    var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_SEED;
   713    // djb2/string-hash
   714    var hash = seed;
   715    var entry;
   716  
   717    for (;;) {
   718      entry = iterator.next();
   719  
   720      if (entry.done) {
   721        break;
   722      }
   723  
   724      hash = (hash << 5) + hash + entry.value | 0;
   725    }
   726  
   727    return hash;
   728  };
   729  var hashInt = function hashInt(num) {
   730    var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_SEED;
   731    // djb2/string-hash
   732    return (seed << 5) + seed + num | 0;
   733  };
   734  var hashIntsArray = function hashIntsArray(ints, seed) {
   735    var entry = {
   736      value: 0,
   737      done: false
   738    };
   739    var i = 0;
   740    var length = ints.length;
   741    var iterator = {
   742      next: function next() {
   743        if (i < length) {
   744          entry.value = ints[i++];
   745        } else {
   746          entry.done = true;
   747        }
   748  
   749        return entry;
   750      }
   751    };
   752    return hashIterableInts(iterator, seed);
   753  };
   754  var hashString = function hashString(str, seed) {
   755    var entry = {
   756      value: 0,
   757      done: false
   758    };
   759    var i = 0;
   760    var length = str.length;
   761    var iterator = {
   762      next: function next() {
   763        if (i < length) {
   764          entry.value = str.charCodeAt(i++);
   765        } else {
   766          entry.done = true;
   767        }
   768  
   769        return entry;
   770      }
   771    };
   772    return hashIterableInts(iterator, seed);
   773  };
   774  var hashStrings = function hashStrings() {
   775    return hashStringsArray(arguments);
   776  };
   777  var hashStringsArray = function hashStringsArray(strs) {
   778    var hash;
   779  
   780    for (var i = 0; i < strs.length; i++) {
   781      var str = strs[i];
   782  
   783      if (i === 0) {
   784        hash = hashString(str);
   785      } else {
   786        hash = hashString(str, hash);
   787      }
   788    }
   789  
   790    return hash;
   791  };
   792  
   793  /*global console */
   794  var warningsEnabled = true;
   795  var warnSupported = console.warn != null; // eslint-disable-line no-console
   796  
   797  var traceSupported = console.trace != null; // eslint-disable-line no-console
   798  
   799  var MAX_INT = Number.MAX_SAFE_INTEGER || 9007199254740991;
   800  var trueify = function trueify() {
   801    return true;
   802  };
   803  var falsify = function falsify() {
   804    return false;
   805  };
   806  var zeroify = function zeroify() {
   807    return 0;
   808  };
   809  var noop = function noop() {};
   810  var error = function error(msg) {
   811    throw new Error(msg);
   812  };
   813  var warnings = function warnings(enabled) {
   814    if (enabled !== undefined) {
   815      warningsEnabled = !!enabled;
   816    } else {
   817      return warningsEnabled;
   818    }
   819  };
   820  var warn = function warn(msg) {
   821    /* eslint-disable no-console */
   822    if (!warnings()) {
   823      return;
   824    }
   825  
   826    if (warnSupported) {
   827      console.warn(msg);
   828    } else {
   829      console.log(msg);
   830  
   831      if (traceSupported) {
   832        console.trace();
   833      }
   834    }
   835  };
   836  /* eslint-enable */
   837  
   838  var clone = function clone(obj) {
   839    return extend({}, obj);
   840  }; // gets a shallow copy of the argument
   841  
   842  var copy = function copy(obj) {
   843    if (obj == null) {
   844      return obj;
   845    }
   846  
   847    if (array(obj)) {
   848      return obj.slice();
   849    } else if (plainObject(obj)) {
   850      return clone(obj);
   851    } else {
   852      return obj;
   853    }
   854  };
   855  var copyArray = function copyArray(arr) {
   856    return arr.slice();
   857  };
   858  var uuid = function uuid(a, b
   859  /* placeholders */
   860  ) {
   861    for ( // loop :)
   862    b = a = ''; // b - result , a - numeric letiable
   863    a++ < 36; //
   864    b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
   865    ? //  return a random number or 4
   866    (a ^ 15 // if "a" is not 15
   867    ? // genetate a random number from 0 to 15
   868    8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
   869    : 4 //  otherwise 4
   870    ).toString(16) : '-' //  in other cases (if "a" is 9,14,19,24) insert "-"
   871    ) {
   872    }
   873  
   874    return b;
   875  };
   876  var _staticEmptyObject = {};
   877  var staticEmptyObject = function staticEmptyObject() {
   878    return _staticEmptyObject;
   879  };
   880  var defaults = function defaults(_defaults) {
   881    var keys = Object.keys(_defaults);
   882    return function (opts) {
   883      var filledOpts = {};
   884  
   885      for (var i = 0; i < keys.length; i++) {
   886        var key = keys[i];
   887        var optVal = opts == null ? undefined : opts[key];
   888        filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
   889      }
   890  
   891      return filledOpts;
   892    };
   893  };
   894  var removeFromArray = function removeFromArray(arr, ele, manyCopies) {
   895    for (var i = arr.length; i >= 0; i--) {
   896      if (arr[i] === ele) {
   897        arr.splice(i, 1);
   898  
   899        if (!manyCopies) {
   900          break;
   901        }
   902      }
   903    }
   904  };
   905  var clearArray = function clearArray(arr) {
   906    arr.splice(0, arr.length);
   907  };
   908  var push = function push(arr, otherArr) {
   909    for (var i = 0; i < otherArr.length; i++) {
   910      var el = otherArr[i];
   911      arr.push(el);
   912    }
   913  };
   914  var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
   915    if (prefix) {
   916      propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
   917    }
   918  
   919    return obj[propName];
   920  };
   921  var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
   922    if (prefix) {
   923      propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
   924    }
   925  
   926    obj[propName] = value;
   927  };
   928  
   929  /* global Map */
   930  var ObjectMap =
   931  /*#__PURE__*/
   932  function () {
   933    function ObjectMap() {
   934      _classCallCheck(this, ObjectMap);
   935  
   936      this._obj = {};
   937    }
   938  
   939    _createClass(ObjectMap, [{
   940      key: "set",
   941      value: function set(key, val) {
   942        this._obj[key] = val;
   943        return this;
   944      }
   945    }, {
   946      key: "delete",
   947      value: function _delete(key) {
   948        this._obj[key] = undefined;
   949        return this;
   950      }
   951    }, {
   952      key: "clear",
   953      value: function clear() {
   954        this._obj = {};
   955      }
   956    }, {
   957      key: "has",
   958      value: function has(key) {
   959        return this._obj[key] !== undefined;
   960      }
   961    }, {
   962      key: "get",
   963      value: function get(key) {
   964        return this._obj[key];
   965      }
   966    }]);
   967  
   968    return ObjectMap;
   969  }();
   970  
   971  var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
   972  
   973  /* global Set */
   974  var undef =  "undefined" ;
   975  
   976  var ObjectSet =
   977  /*#__PURE__*/
   978  function () {
   979    function ObjectSet(arrayOrObjectSet) {
   980      _classCallCheck(this, ObjectSet);
   981  
   982      this._obj = Object.create(null);
   983      this.size = 0;
   984  
   985      if (arrayOrObjectSet != null) {
   986        var arr;
   987  
   988        if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
   989          arr = arrayOrObjectSet.toArray();
   990        } else {
   991          arr = arrayOrObjectSet;
   992        }
   993  
   994        for (var i = 0; i < arr.length; i++) {
   995          this.add(arr[i]);
   996        }
   997      }
   998    }
   999  
  1000    _createClass(ObjectSet, [{
  1001      key: "instanceString",
  1002      value: function instanceString() {
  1003        return 'set';
  1004      }
  1005    }, {
  1006      key: "add",
  1007      value: function add(val) {
  1008        var o = this._obj;
  1009  
  1010        if (o[val] !== 1) {
  1011          o[val] = 1;
  1012          this.size++;
  1013        }
  1014      }
  1015    }, {
  1016      key: "delete",
  1017      value: function _delete(val) {
  1018        var o = this._obj;
  1019  
  1020        if (o[val] === 1) {
  1021          o[val] = 0;
  1022          this.size--;
  1023        }
  1024      }
  1025    }, {
  1026      key: "clear",
  1027      value: function clear() {
  1028        this._obj = Object.create(null);
  1029      }
  1030    }, {
  1031      key: "has",
  1032      value: function has(val) {
  1033        return this._obj[val] === 1;
  1034      }
  1035    }, {
  1036      key: "toArray",
  1037      value: function toArray() {
  1038        var _this = this;
  1039  
  1040        return Object.keys(this._obj).filter(function (key) {
  1041          return _this.has(key);
  1042        });
  1043      }
  1044    }, {
  1045      key: "forEach",
  1046      value: function forEach(callback, thisArg) {
  1047        return this.toArray().forEach(callback, thisArg);
  1048      }
  1049    }]);
  1050  
  1051    return ObjectSet;
  1052  }();
  1053  
  1054  var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
  1055  
  1056  var Element = function Element(cy, params, restore) {
  1057    restore = restore === undefined || restore ? true : false;
  1058  
  1059    if (cy === undefined || params === undefined || !core(cy)) {
  1060      error('An element must have a core reference and parameters set');
  1061      return;
  1062    }
  1063  
  1064    var group = params.group; // try to automatically infer the group if unspecified
  1065  
  1066    if (group == null) {
  1067      if (params.data && params.data.source != null && params.data.target != null) {
  1068        group = 'edges';
  1069      } else {
  1070        group = 'nodes';
  1071      }
  1072    } // validate group
  1073  
  1074  
  1075    if (group !== 'nodes' && group !== 'edges') {
  1076      error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
  1077      return;
  1078    } // make the element array-like, just like a collection
  1079  
  1080  
  1081    this.length = 1;
  1082    this[0] = this; // NOTE: when something is added here, add also to ele.json()
  1083  
  1084    var _p = this._private = {
  1085      cy: cy,
  1086      single: true,
  1087      // indicates this is an element
  1088      data: params.data || {},
  1089      // data object
  1090      position: params.position || {
  1091        x: 0,
  1092        y: 0
  1093      },
  1094      // (x, y) position pair
  1095      autoWidth: undefined,
  1096      // width and height of nodes calculated by the renderer when set to special 'auto' value
  1097      autoHeight: undefined,
  1098      autoPadding: undefined,
  1099      compoundBoundsClean: false,
  1100      // whether the compound dimensions need to be recalculated the next time dimensions are read
  1101      listeners: [],
  1102      // array of bound listeners
  1103      group: group,
  1104      // string; 'nodes' or 'edges'
  1105      style: {},
  1106      // properties as set by the style
  1107      rstyle: {},
  1108      // properties for style sent from the renderer to the core
  1109      styleCxts: [],
  1110      // applied style contexts from the styler
  1111      styleKeys: {},
  1112      // per-group keys of style property values
  1113      removed: true,
  1114      // whether it's inside the vis; true if removed (set true here since we call restore)
  1115      selected: params.selected ? true : false,
  1116      // whether it's selected
  1117      selectable: params.selectable === undefined ? true : params.selectable ? true : false,
  1118      // whether it's selectable
  1119      locked: params.locked ? true : false,
  1120      // whether the element is locked (cannot be moved)
  1121      grabbed: false,
  1122      // whether the element is grabbed by the mouse; renderer sets this privately
  1123      grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
  1124      // whether the element can be grabbed
  1125      pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
  1126      // whether the element has passthrough panning enabled
  1127      active: false,
  1128      // whether the element is active from user interaction
  1129      classes: new Set$1(),
  1130      // map ( className => true )
  1131      animation: {
  1132        // object for currently-running animations
  1133        current: [],
  1134        queue: []
  1135      },
  1136      rscratch: {},
  1137      // object in which the renderer can store information
  1138      scratch: params.scratch || {},
  1139      // scratch objects
  1140      edges: [],
  1141      // array of connected edges
  1142      children: [],
  1143      // array of children
  1144      parent: null,
  1145      // parent ref
  1146      traversalCache: {},
  1147      // cache of output of traversal functions
  1148      backgrounding: false,
  1149      // whether background images are loading
  1150      bbCache: null,
  1151      // cache of the current bounding box
  1152      bbCacheShift: {
  1153        x: 0,
  1154        y: 0
  1155      },
  1156      // shift applied to cached bb to be applied on next get
  1157      bodyBounds: null,
  1158      // bounds cache of element body, w/o overlay
  1159      overlayBounds: null,
  1160      // bounds cache of element body, including overlay
  1161      labelBounds: {
  1162        // bounds cache of labels
  1163        all: null,
  1164        source: null,
  1165        target: null,
  1166        main: null
  1167      },
  1168      arrowBounds: {
  1169        // bounds cache of edge arrows
  1170        source: null,
  1171        target: null,
  1172        'mid-source': null,
  1173        'mid-target': null
  1174      }
  1175    };
  1176  
  1177    if (_p.position.x == null) {
  1178      _p.position.x = 0;
  1179    }
  1180  
  1181    if (_p.position.y == null) {
  1182      _p.position.y = 0;
  1183    } // renderedPosition overrides if specified
  1184  
  1185  
  1186    if (params.renderedPosition) {
  1187      var rpos = params.renderedPosition;
  1188      var pan = cy.pan();
  1189      var zoom = cy.zoom();
  1190      _p.position = {
  1191        x: (rpos.x - pan.x) / zoom,
  1192        y: (rpos.y - pan.y) / zoom
  1193      };
  1194    }
  1195  
  1196    var classes = [];
  1197  
  1198    if (array(params.classes)) {
  1199      classes = params.classes;
  1200    } else if (string(params.classes)) {
  1201      classes = params.classes.split(/\s+/);
  1202    }
  1203  
  1204    for (var i = 0, l = classes.length; i < l; i++) {
  1205      var cls = classes[i];
  1206  
  1207      if (!cls || cls === '') {
  1208        continue;
  1209      }
  1210  
  1211      _p.classes.add(cls);
  1212    }
  1213  
  1214    this.createEmitter();
  1215    var bypass = params.style || params.css;
  1216  
  1217    if (bypass) {
  1218      warn('Setting a `style` bypass at element creation is deprecated');
  1219      this.style(bypass);
  1220    }
  1221  
  1222    if (restore === undefined || restore) {
  1223      this.restore();
  1224    }
  1225  };
  1226  
  1227  var defineSearch = function defineSearch(params) {
  1228    params = {
  1229      bfs: params.bfs || !params.dfs,
  1230      dfs: params.dfs || !params.bfs
  1231    }; // from pseudocode on wikipedia
  1232  
  1233    return function searchFn(roots, fn$1, directed) {
  1234      var options;
  1235  
  1236      if (plainObject(roots) && !elementOrCollection(roots)) {
  1237        options = roots;
  1238        roots = options.roots || options.root;
  1239        fn$1 = options.visit;
  1240        directed = options.directed;
  1241      }
  1242  
  1243      directed = arguments.length === 2 && !fn(fn$1) ? fn$1 : directed;
  1244      fn$1 = fn(fn$1) ? fn$1 : function () {};
  1245      var cy = this._private.cy;
  1246      var v = roots = string(roots) ? this.filter(roots) : roots;
  1247      var Q = [];
  1248      var connectedNodes = [];
  1249      var connectedBy = {};
  1250      var id2depth = {};
  1251      var V = {};
  1252      var j = 0;
  1253      var found;
  1254  
  1255      var _this$byGroup = this.byGroup(),
  1256          nodes = _this$byGroup.nodes,
  1257          edges = _this$byGroup.edges; // enqueue v
  1258  
  1259  
  1260      for (var i = 0; i < v.length; i++) {
  1261        var vi = v[i];
  1262        var viId = vi.id();
  1263  
  1264        if (vi.isNode()) {
  1265          Q.unshift(vi);
  1266  
  1267          if (params.bfs) {
  1268            V[viId] = true;
  1269            connectedNodes.push(vi);
  1270          }
  1271  
  1272          id2depth[viId] = 0;
  1273        }
  1274      }
  1275  
  1276      var _loop2 = function _loop2() {
  1277        var v = params.bfs ? Q.shift() : Q.pop();
  1278        var vId = v.id();
  1279  
  1280        if (params.dfs) {
  1281          if (V[vId]) {
  1282            return "continue";
  1283          }
  1284  
  1285          V[vId] = true;
  1286          connectedNodes.push(v);
  1287        }
  1288  
  1289        var depth = id2depth[vId];
  1290        var prevEdge = connectedBy[vId];
  1291        var src = prevEdge != null ? prevEdge.source() : null;
  1292        var tgt = prevEdge != null ? prevEdge.target() : null;
  1293        var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
  1294        var ret = void 0;
  1295        ret = fn$1(v, prevEdge, prevNode, j++, depth);
  1296  
  1297        if (ret === true) {
  1298          found = v;
  1299          return "break";
  1300        }
  1301  
  1302        if (ret === false) {
  1303          return "break";
  1304        }
  1305  
  1306        var vwEdges = v.connectedEdges().filter(function (e) {
  1307          return (!directed || e.source().same(v)) && edges.has(e);
  1308        });
  1309  
  1310        for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
  1311          var e = vwEdges[_i2];
  1312          var w = e.connectedNodes().filter(function (n) {
  1313            return !n.same(v) && nodes.has(n);
  1314          });
  1315          var wId = w.id();
  1316  
  1317          if (w.length !== 0 && !V[wId]) {
  1318            w = w[0];
  1319            Q.push(w);
  1320  
  1321            if (params.bfs) {
  1322              V[wId] = true;
  1323              connectedNodes.push(w);
  1324            }
  1325  
  1326            connectedBy[wId] = e;
  1327            id2depth[wId] = id2depth[vId] + 1;
  1328          }
  1329        }
  1330      };
  1331  
  1332      _loop: while (Q.length !== 0) {
  1333        var _ret = _loop2();
  1334  
  1335        switch (_ret) {
  1336          case "continue":
  1337            continue;
  1338  
  1339          case "break":
  1340            break _loop;
  1341        }
  1342      }
  1343  
  1344      var connectedEles = cy.collection();
  1345  
  1346      for (var _i = 0; _i < connectedNodes.length; _i++) {
  1347        var node = connectedNodes[_i];
  1348        var edge = connectedBy[node.id()];
  1349  
  1350        if (edge != null) {
  1351          connectedEles.merge(edge);
  1352        }
  1353  
  1354        connectedEles.merge(node);
  1355      }
  1356  
  1357      return {
  1358        path: cy.collection(connectedEles),
  1359        found: cy.collection(found)
  1360      };
  1361    };
  1362  }; // search, spanning trees, etc
  1363  
  1364  
  1365  var elesfn = {
  1366    breadthFirstSearch: defineSearch({
  1367      bfs: true
  1368    }),
  1369    depthFirstSearch: defineSearch({
  1370      dfs: true
  1371    })
  1372  }; // nice, short mathemathical alias
  1373  
  1374  elesfn.bfs = elesfn.breadthFirstSearch;
  1375  elesfn.dfs = elesfn.depthFirstSearch;
  1376  
  1377  var dijkstraDefaults = defaults({
  1378    root: null,
  1379    weight: function weight(edge) {
  1380      return 1;
  1381    },
  1382    directed: false
  1383  });
  1384  var elesfn$1 = {
  1385    dijkstra: function dijkstra(options) {
  1386      if (!plainObject(options)) {
  1387        var args = arguments;
  1388        options = {
  1389          root: args[0],
  1390          weight: args[1],
  1391          directed: args[2]
  1392        };
  1393      }
  1394  
  1395      var _dijkstraDefaults = dijkstraDefaults(options),
  1396          root = _dijkstraDefaults.root,
  1397          weight = _dijkstraDefaults.weight,
  1398          directed = _dijkstraDefaults.directed;
  1399  
  1400      var eles = this;
  1401      var weightFn = weight;
  1402      var source = string(root) ? this.filter(root)[0] : root[0];
  1403      var dist = {};
  1404      var prev = {};
  1405      var knownDist = {};
  1406  
  1407      var _this$byGroup = this.byGroup(),
  1408          nodes = _this$byGroup.nodes,
  1409          edges = _this$byGroup.edges;
  1410  
  1411      edges.unmergeBy(function (ele) {
  1412        return ele.isLoop();
  1413      });
  1414  
  1415      var getDist = function getDist(node) {
  1416        return dist[node.id()];
  1417      };
  1418  
  1419      var setDist = function setDist(node, d) {
  1420        dist[node.id()] = d;
  1421        Q.updateItem(node);
  1422      };
  1423  
  1424      var Q = new Heap(function (a, b) {
  1425        return getDist(a) - getDist(b);
  1426      });
  1427  
  1428      for (var i = 0; i < nodes.length; i++) {
  1429        var node = nodes[i];
  1430        dist[node.id()] = node.same(source) ? 0 : Infinity;
  1431        Q.push(node);
  1432      }
  1433  
  1434      var distBetween = function distBetween(u, v) {
  1435        var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
  1436        var smallestDistance = Infinity;
  1437        var smallestEdge;
  1438  
  1439        for (var _i = 0; _i < uvs.length; _i++) {
  1440          var edge = uvs[_i];
  1441  
  1442          var _weight = weightFn(edge);
  1443  
  1444          if (_weight < smallestDistance || !smallestEdge) {
  1445            smallestDistance = _weight;
  1446            smallestEdge = edge;
  1447          }
  1448        }
  1449  
  1450        return {
  1451          edge: smallestEdge,
  1452          dist: smallestDistance
  1453        };
  1454      };
  1455  
  1456      while (Q.size() > 0) {
  1457        var u = Q.pop();
  1458        var smalletsDist = getDist(u);
  1459        var uid = u.id();
  1460        knownDist[uid] = smalletsDist;
  1461  
  1462        if (smalletsDist === Infinity) {
  1463          continue;
  1464        }
  1465  
  1466        var neighbors = u.neighborhood().intersect(nodes);
  1467  
  1468        for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
  1469          var v = neighbors[_i2];
  1470          var vid = v.id();
  1471          var vDist = distBetween(u, v);
  1472          var alt = smalletsDist + vDist.dist;
  1473  
  1474          if (alt < getDist(v)) {
  1475            setDist(v, alt);
  1476            prev[vid] = {
  1477              node: u,
  1478              edge: vDist.edge
  1479            };
  1480          }
  1481        } // for
  1482  
  1483      } // while
  1484  
  1485  
  1486      return {
  1487        distanceTo: function distanceTo(node) {
  1488          var target = string(node) ? nodes.filter(node)[0] : node[0];
  1489          return knownDist[target.id()];
  1490        },
  1491        pathTo: function pathTo(node) {
  1492          var target = string(node) ? nodes.filter(node)[0] : node[0];
  1493          var S = [];
  1494          var u = target;
  1495          var uid = u.id();
  1496  
  1497          if (target.length > 0) {
  1498            S.unshift(target);
  1499  
  1500            while (prev[uid]) {
  1501              var p = prev[uid];
  1502              S.unshift(p.edge);
  1503              S.unshift(p.node);
  1504              u = p.node;
  1505              uid = u.id();
  1506            }
  1507          }
  1508  
  1509          return eles.spawn(S);
  1510        }
  1511      };
  1512    }
  1513  };
  1514  
  1515  var elesfn$2 = {
  1516    // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
  1517    // implemented from pseudocode from wikipedia
  1518    kruskal: function kruskal(weightFn) {
  1519      weightFn = weightFn || function (edge) {
  1520        return 1;
  1521      };
  1522  
  1523      var _this$byGroup = this.byGroup(),
  1524          nodes = _this$byGroup.nodes,
  1525          edges = _this$byGroup.edges;
  1526  
  1527      var numNodes = nodes.length;
  1528      var forest = new Array(numNodes);
  1529      var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
  1530  
  1531      var findSetIndex = function findSetIndex(ele) {
  1532        for (var i = 0; i < forest.length; i++) {
  1533          var eles = forest[i];
  1534  
  1535          if (eles.has(ele)) {
  1536            return i;
  1537          }
  1538        }
  1539      }; // start with one forest per node
  1540  
  1541  
  1542      for (var i = 0; i < numNodes; i++) {
  1543        forest[i] = this.spawn(nodes[i]);
  1544      }
  1545  
  1546      var S = edges.sort(function (a, b) {
  1547        return weightFn(a) - weightFn(b);
  1548      });
  1549  
  1550      for (var _i = 0; _i < S.length; _i++) {
  1551        var edge = S[_i];
  1552        var u = edge.source()[0];
  1553        var v = edge.target()[0];
  1554        var setUIndex = findSetIndex(u);
  1555        var setVIndex = findSetIndex(v);
  1556        var setU = forest[setUIndex];
  1557        var setV = forest[setVIndex];
  1558  
  1559        if (setUIndex !== setVIndex) {
  1560          A.merge(edge); // combine forests for u and v
  1561  
  1562          setU.merge(setV);
  1563          forest.splice(setVIndex, 1);
  1564        }
  1565      }
  1566  
  1567      return A;
  1568    }
  1569  };
  1570  
  1571  var aStarDefaults = defaults({
  1572    root: null,
  1573    goal: null,
  1574    weight: function weight(edge) {
  1575      return 1;
  1576    },
  1577    heuristic: function heuristic(edge) {
  1578      return 0;
  1579    },
  1580    directed: false
  1581  });
  1582  var elesfn$3 = {
  1583    // Implemented from pseudocode from wikipedia
  1584    aStar: function aStar(options) {
  1585      var cy = this.cy();
  1586  
  1587      var _aStarDefaults = aStarDefaults(options),
  1588          root = _aStarDefaults.root,
  1589          goal = _aStarDefaults.goal,
  1590          heuristic = _aStarDefaults.heuristic,
  1591          directed = _aStarDefaults.directed,
  1592          weight = _aStarDefaults.weight;
  1593  
  1594      root = cy.collection(root)[0];
  1595      goal = cy.collection(goal)[0];
  1596      var sid = root.id();
  1597      var tid = goal.id();
  1598      var gScore = {};
  1599      var fScore = {};
  1600      var closedSetIds = {};
  1601      var openSet = new Heap(function (a, b) {
  1602        return fScore[a.id()] - fScore[b.id()];
  1603      });
  1604      var openSetIds = new Set$1();
  1605      var cameFrom = {};
  1606      var cameFromEdge = {};
  1607  
  1608      var addToOpenSet = function addToOpenSet(ele, id) {
  1609        openSet.push(ele);
  1610        openSetIds.add(id);
  1611      };
  1612  
  1613      var cMin, cMinId;
  1614  
  1615      var popFromOpenSet = function popFromOpenSet() {
  1616        cMin = openSet.pop();
  1617        cMinId = cMin.id();
  1618        openSetIds["delete"](cMinId);
  1619      };
  1620  
  1621      var isInOpenSet = function isInOpenSet(id) {
  1622        return openSetIds.has(id);
  1623      };
  1624  
  1625      addToOpenSet(root, sid);
  1626      gScore[sid] = 0;
  1627      fScore[sid] = heuristic(root); // Counter
  1628  
  1629      var steps = 0; // Main loop
  1630  
  1631      while (openSet.size() > 0) {
  1632        popFromOpenSet();
  1633        steps++; // If we've found our goal, then we are done
  1634  
  1635        if (cMinId === tid) {
  1636          var path = [];
  1637          var pathNode = goal;
  1638          var pathNodeId = tid;
  1639          var pathEdge = cameFromEdge[pathNodeId];
  1640  
  1641          for (;;) {
  1642            path.unshift(pathNode);
  1643  
  1644            if (pathEdge != null) {
  1645              path.unshift(pathEdge);
  1646            }
  1647  
  1648            pathNode = cameFrom[pathNodeId];
  1649  
  1650            if (pathNode == null) {
  1651              break;
  1652            }
  1653  
  1654            pathNodeId = pathNode.id();
  1655            pathEdge = cameFromEdge[pathNodeId];
  1656          }
  1657  
  1658          return {
  1659            found: true,
  1660            distance: gScore[cMinId],
  1661            path: this.spawn(path),
  1662            steps: steps
  1663          };
  1664        } // Add cMin to processed nodes
  1665  
  1666  
  1667        closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
  1668        // Take into account if graph is directed or not
  1669  
  1670        var vwEdges = cMin._private.edges;
  1671  
  1672        for (var i = 0; i < vwEdges.length; i++) {
  1673          var e = vwEdges[i]; // edge must be in set of calling eles
  1674  
  1675          if (!this.hasElementWithId(e.id())) {
  1676            continue;
  1677          } // cMin must be the source of edge if directed
  1678  
  1679  
  1680          if (directed && e.data('source') !== cMinId) {
  1681            continue;
  1682          }
  1683  
  1684          var wSrc = e.source();
  1685          var wTgt = e.target();
  1686          var w = wSrc.id() !== cMinId ? wSrc : wTgt;
  1687          var wid = w.id(); // node must be in set of calling eles
  1688  
  1689          if (!this.hasElementWithId(wid)) {
  1690            continue;
  1691          } // if node is in closedSet, ignore it
  1692  
  1693  
  1694          if (closedSetIds[wid]) {
  1695            continue;
  1696          } // New tentative score for node w
  1697  
  1698  
  1699          var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
  1700          //   w not present in openSet
  1701          // OR
  1702          //   tentative gScore is less than previous value
  1703          // w not in openSet
  1704  
  1705          if (!isInOpenSet(wid)) {
  1706            gScore[wid] = tempScore;
  1707            fScore[wid] = tempScore + heuristic(w);
  1708            addToOpenSet(w, wid);
  1709            cameFrom[wid] = cMin;
  1710            cameFromEdge[wid] = e;
  1711            continue;
  1712          } // w already in openSet, but with greater gScore
  1713  
  1714  
  1715          if (tempScore < gScore[wid]) {
  1716            gScore[wid] = tempScore;
  1717            fScore[wid] = tempScore + heuristic(w);
  1718            cameFrom[wid] = cMin;
  1719          }
  1720        } // End of neighbors update
  1721  
  1722      } // End of main loop
  1723      // If we've reached here, then we've not reached our goal
  1724  
  1725  
  1726      return {
  1727        found: false,
  1728        distance: undefined,
  1729        path: undefined,
  1730        steps: steps
  1731      };
  1732    }
  1733  }; // elesfn
  1734  
  1735  var floydWarshallDefaults = defaults({
  1736    weight: function weight(edge) {
  1737      return 1;
  1738    },
  1739    directed: false
  1740  });
  1741  var elesfn$4 = {
  1742    // Implemented from pseudocode from wikipedia
  1743    floydWarshall: function floydWarshall(options) {
  1744      var cy = this.cy();
  1745  
  1746      var _floydWarshallDefault = floydWarshallDefaults(options),
  1747          weight = _floydWarshallDefault.weight,
  1748          directed = _floydWarshallDefault.directed;
  1749  
  1750      var weightFn = weight;
  1751  
  1752      var _this$byGroup = this.byGroup(),
  1753          nodes = _this$byGroup.nodes,
  1754          edges = _this$byGroup.edges;
  1755  
  1756      var N = nodes.length;
  1757      var Nsq = N * N;
  1758  
  1759      var indexOf = function indexOf(node) {
  1760        return nodes.indexOf(node);
  1761      };
  1762  
  1763      var atIndex = function atIndex(i) {
  1764        return nodes[i];
  1765      }; // Initialize distance matrix
  1766  
  1767  
  1768      var dist = new Array(Nsq);
  1769  
  1770      for (var n = 0; n < Nsq; n++) {
  1771        var j = n % N;
  1772        var i = (n - j) / N;
  1773  
  1774        if (i === j) {
  1775          dist[n] = 0;
  1776        } else {
  1777          dist[n] = Infinity;
  1778        }
  1779      } // Initialize matrix used for path reconstruction
  1780      // Initialize distance matrix
  1781  
  1782  
  1783      var next = new Array(Nsq);
  1784      var edgeNext = new Array(Nsq); // Process edges
  1785  
  1786      for (var _i = 0; _i < edges.length; _i++) {
  1787        var edge = edges[_i];
  1788        var src = edge.source()[0];
  1789        var tgt = edge.target()[0];
  1790  
  1791        if (src === tgt) {
  1792          continue;
  1793        } // exclude loops
  1794  
  1795  
  1796        var s = indexOf(src);
  1797        var t = indexOf(tgt);
  1798        var st = s * N + t; // source to target index
  1799  
  1800        var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
  1801  
  1802  
  1803        if (dist[st] > _weight) {
  1804          dist[st] = _weight;
  1805          next[st] = t;
  1806          edgeNext[st] = edge;
  1807        } // If undirected graph, process 'reversed' edge
  1808  
  1809  
  1810        if (!directed) {
  1811          var ts = t * N + s; // target to source index
  1812  
  1813          if (!directed && dist[ts] > _weight) {
  1814            dist[ts] = _weight;
  1815            next[ts] = s;
  1816            edgeNext[ts] = edge;
  1817          }
  1818        }
  1819      } // Main loop
  1820  
  1821  
  1822      for (var k = 0; k < N; k++) {
  1823        for (var _i2 = 0; _i2 < N; _i2++) {
  1824          var ik = _i2 * N + k;
  1825  
  1826          for (var _j = 0; _j < N; _j++) {
  1827            var ij = _i2 * N + _j;
  1828            var kj = k * N + _j;
  1829  
  1830            if (dist[ik] + dist[kj] < dist[ij]) {
  1831              dist[ij] = dist[ik] + dist[kj];
  1832              next[ij] = next[ik];
  1833            }
  1834          }
  1835        }
  1836      }
  1837  
  1838      var getArgEle = function getArgEle(ele) {
  1839        return (string(ele) ? cy.filter(ele) : ele)[0];
  1840      };
  1841  
  1842      var indexOfArgEle = function indexOfArgEle(ele) {
  1843        return indexOf(getArgEle(ele));
  1844      };
  1845  
  1846      var res = {
  1847        distance: function distance(from, to) {
  1848          var i = indexOfArgEle(from);
  1849          var j = indexOfArgEle(to);
  1850          return dist[i * N + j];
  1851        },
  1852        path: function path(from, to) {
  1853          var i = indexOfArgEle(from);
  1854          var j = indexOfArgEle(to);
  1855          var fromNode = atIndex(i);
  1856  
  1857          if (i === j) {
  1858            return fromNode.collection();
  1859          }
  1860  
  1861          if (next[i * N + j] == null) {
  1862            return cy.collection();
  1863          }
  1864  
  1865          var path = cy.collection();
  1866          var prev = i;
  1867          var edge;
  1868          path.merge(fromNode);
  1869  
  1870          while (i !== j) {
  1871            prev = i;
  1872            i = next[i * N + j];
  1873            edge = edgeNext[prev * N + i];
  1874            path.merge(edge);
  1875            path.merge(atIndex(i));
  1876          }
  1877  
  1878          return path;
  1879        }
  1880      };
  1881      return res;
  1882    } // floydWarshall
  1883  
  1884  }; // elesfn
  1885  
  1886  var bellmanFordDefaults = defaults({
  1887    weight: function weight(edge) {
  1888      return 1;
  1889    },
  1890    directed: false,
  1891    root: null
  1892  });
  1893  var elesfn$5 = {
  1894    // Implemented from pseudocode from wikipedia
  1895    bellmanFord: function bellmanFord(options) {
  1896      var _this = this;
  1897  
  1898      var _bellmanFordDefaults = bellmanFordDefaults(options),
  1899          weight = _bellmanFordDefaults.weight,
  1900          directed = _bellmanFordDefaults.directed,
  1901          root = _bellmanFordDefaults.root;
  1902  
  1903      var weightFn = weight;
  1904      var eles = this;
  1905      var cy = this.cy();
  1906  
  1907      var _this$byGroup = this.byGroup(),
  1908          edges = _this$byGroup.edges,
  1909          nodes = _this$byGroup.nodes;
  1910  
  1911      var numNodes = nodes.length;
  1912      var infoMap = new Map$1();
  1913      var hasNegativeWeightCycle = false;
  1914      var negativeWeightCycles = [];
  1915      root = cy.collection(root)[0]; // in case selector passed
  1916  
  1917      edges.unmergeBy(function (edge) {
  1918        return edge.isLoop();
  1919      });
  1920      var numEdges = edges.length;
  1921  
  1922      var getInfo = function getInfo(node) {
  1923        var obj = infoMap.get(node.id());
  1924  
  1925        if (!obj) {
  1926          obj = {};
  1927          infoMap.set(node.id(), obj);
  1928        }
  1929  
  1930        return obj;
  1931      };
  1932  
  1933      var getNodeFromTo = function getNodeFromTo(to) {
  1934        return (string(to) ? cy.$(to) : to)[0];
  1935      };
  1936  
  1937      var distanceTo = function distanceTo(to) {
  1938        return getInfo(getNodeFromTo(to)).dist;
  1939      };
  1940  
  1941      var pathTo = function pathTo(to) {
  1942        var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
  1943        var end = getNodeFromTo(to);
  1944        var path = [];
  1945        var node = end;
  1946  
  1947        for (;;) {
  1948          if (node == null) {
  1949            return _this.spawn();
  1950          }
  1951  
  1952          var _getInfo = getInfo(node),
  1953              edge = _getInfo.edge,
  1954              pred = _getInfo.pred;
  1955  
  1956          path.unshift(node[0]);
  1957  
  1958          if (node.same(thisStart) && path.length > 0) {
  1959            break;
  1960          }
  1961  
  1962          if (edge != null) {
  1963            path.unshift(edge);
  1964          }
  1965  
  1966          node = pred;
  1967        }
  1968  
  1969        return eles.spawn(path);
  1970      }; // Initializations { dist, pred, edge }
  1971  
  1972  
  1973      for (var i = 0; i < numNodes; i++) {
  1974        var node = nodes[i];
  1975        var info = getInfo(node);
  1976  
  1977        if (node.same(root)) {
  1978          info.dist = 0;
  1979        } else {
  1980          info.dist = Infinity;
  1981        }
  1982  
  1983        info.pred = null;
  1984        info.edge = null;
  1985      } // Edges relaxation
  1986  
  1987  
  1988      var replacedEdge = false;
  1989  
  1990      var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
  1991        var dist = info1.dist + weight;
  1992  
  1993        if (dist < info2.dist && !edge.same(info1.edge)) {
  1994          info2.dist = dist;
  1995          info2.pred = node1;
  1996          info2.edge = edge;
  1997          replacedEdge = true;
  1998        }
  1999      };
  2000  
  2001      for (var _i = 1; _i < numNodes; _i++) {
  2002        replacedEdge = false;
  2003  
  2004        for (var e = 0; e < numEdges; e++) {
  2005          var edge = edges[e];
  2006          var src = edge.source();
  2007          var tgt = edge.target();
  2008  
  2009          var _weight = weightFn(edge);
  2010  
  2011          var srcInfo = getInfo(src);
  2012          var tgtInfo = getInfo(tgt);
  2013          checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
  2014  
  2015          if (!directed) {
  2016            checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
  2017          }
  2018        }
  2019  
  2020        if (!replacedEdge) {
  2021          break;
  2022        }
  2023      }
  2024  
  2025      if (replacedEdge) {
  2026        // Check for negative weight cycles
  2027        for (var _e = 0; _e < numEdges; _e++) {
  2028          var _edge = edges[_e];
  2029  
  2030          var _src = _edge.source();
  2031  
  2032          var _tgt = _edge.target();
  2033  
  2034          var _weight2 = weightFn(_edge);
  2035  
  2036          var srcDist = getInfo(_src).dist;
  2037          var tgtDist = getInfo(_tgt).dist;
  2038  
  2039          if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
  2040            warn('Graph contains a negative weight cycle for Bellman-Ford');
  2041            hasNegativeWeightCycle = true;
  2042            break;
  2043          }
  2044        }
  2045      }
  2046  
  2047      return {
  2048        distanceTo: distanceTo,
  2049        pathTo: pathTo,
  2050        hasNegativeWeightCycle: hasNegativeWeightCycle,
  2051        negativeWeightCycles: negativeWeightCycles
  2052      };
  2053    } // bellmanFord
  2054  
  2055  }; // elesfn
  2056  
  2057  var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
  2058  // Updates the remaining edge lists
  2059  // Receives as a paramater the edge which causes the collapse
  2060  
  2061  var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
  2062    if (remainingEdges.length === 0) {
  2063      error("Karger-Stein must be run on a connected (sub)graph");
  2064    }
  2065  
  2066    var edgeInfo = remainingEdges[edgeIndex];
  2067    var sourceIn = edgeInfo[1];
  2068    var targetIn = edgeInfo[2];
  2069    var partition1 = nodeMap[sourceIn];
  2070    var partition2 = nodeMap[targetIn];
  2071    var newEdges = remainingEdges; // re-use array
  2072    // Delete all edges between partition1 and partition2
  2073  
  2074    for (var i = newEdges.length - 1; i >= 0; i--) {
  2075      var edge = newEdges[i];
  2076      var src = edge[1];
  2077      var tgt = edge[2];
  2078  
  2079      if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
  2080        newEdges.splice(i, 1);
  2081      }
  2082    } // All edges pointing to partition2 should now point to partition1
  2083  
  2084  
  2085    for (var _i = 0; _i < newEdges.length; _i++) {
  2086      var _edge = newEdges[_i];
  2087  
  2088      if (_edge[1] === partition2) {
  2089        // Check source
  2090        newEdges[_i] = _edge.slice(); // copy
  2091  
  2092        newEdges[_i][1] = partition1;
  2093      } else if (_edge[2] === partition2) {
  2094        // Check target
  2095        newEdges[_i] = _edge.slice(); // copy
  2096  
  2097        newEdges[_i][2] = partition1;
  2098      }
  2099    } // Move all nodes from partition2 to partition1
  2100  
  2101  
  2102    for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
  2103      if (nodeMap[_i2] === partition2) {
  2104        nodeMap[_i2] = partition1;
  2105      }
  2106    }
  2107  
  2108    return newEdges;
  2109  }; // Contracts a graph until we reach a certain number of meta nodes
  2110  
  2111  
  2112  var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
  2113    while (size > sizeLimit) {
  2114      // Choose an edge randomly
  2115      var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
  2116  
  2117      remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
  2118      size--;
  2119    }
  2120  
  2121    return remainingEdges;
  2122  };
  2123  
  2124  var elesfn$6 = {
  2125    // Computes the minimum cut of an undirected graph
  2126    // Returns the correct answer with high probability
  2127    kargerStein: function kargerStein() {
  2128      var _this = this;
  2129  
  2130      var _this$byGroup = this.byGroup(),
  2131          nodes = _this$byGroup.nodes,
  2132          edges = _this$byGroup.edges;
  2133  
  2134      edges.unmergeBy(function (edge) {
  2135        return edge.isLoop();
  2136      });
  2137      var numNodes = nodes.length;
  2138      var numEdges = edges.length;
  2139      var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
  2140      var stopSize = Math.floor(numNodes / sqrt2);
  2141  
  2142      if (numNodes < 2) {
  2143        error('At least 2 nodes are required for Karger-Stein algorithm');
  2144        return undefined;
  2145      } // Now store edge destination as indexes
  2146      // Format for each edge (edge index, source node index, target node index)
  2147  
  2148  
  2149      var edgeIndexes = [];
  2150  
  2151      for (var i = 0; i < numEdges; i++) {
  2152        var e = edges[i];
  2153        edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
  2154      } // We will store the best cut found here
  2155  
  2156  
  2157      var minCutSize = Infinity;
  2158      var minCutEdgeIndexes = [];
  2159      var minCutNodeMap = new Array(numNodes); // Initial meta node partition
  2160  
  2161      var metaNodeMap = new Array(numNodes);
  2162      var metaNodeMap2 = new Array(numNodes);
  2163  
  2164      var copyNodesMap = function copyNodesMap(from, to) {
  2165        for (var _i3 = 0; _i3 < numNodes; _i3++) {
  2166          to[_i3] = from[_i3];
  2167        }
  2168      }; // Main loop
  2169  
  2170  
  2171      for (var iter = 0; iter <= numIter; iter++) {
  2172        // Reset meta node partition
  2173        for (var _i4 = 0; _i4 < numNodes; _i4++) {
  2174          metaNodeMap[_i4] = _i4;
  2175        } // Contract until stop point (stopSize nodes)
  2176  
  2177  
  2178        var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
  2179        var edgesState2 = edgesState.slice(); // copy
  2180        // Create a copy of the colapsed nodes state
  2181  
  2182        copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
  2183  
  2184        var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
  2185        var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
  2186  
  2187        if (res1.length <= res2.length && res1.length < minCutSize) {
  2188          minCutSize = res1.length;
  2189          minCutEdgeIndexes = res1;
  2190          copyNodesMap(metaNodeMap, minCutNodeMap);
  2191        } else if (res2.length <= res1.length && res2.length < minCutSize) {
  2192          minCutSize = res2.length;
  2193          minCutEdgeIndexes = res2;
  2194          copyNodesMap(metaNodeMap2, minCutNodeMap);
  2195        }
  2196      } // end of main loop
  2197      // Construct result
  2198  
  2199  
  2200      var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
  2201        return edges[e[0]];
  2202      }));
  2203      var partition1 = this.spawn();
  2204      var partition2 = this.spawn(); // traverse metaNodeMap for best cut
  2205  
  2206      var witnessNodePartition = minCutNodeMap[0];
  2207  
  2208      for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
  2209        var partitionId = minCutNodeMap[_i5];
  2210        var node = nodes[_i5];
  2211  
  2212        if (partitionId === witnessNodePartition) {
  2213          partition1.merge(node);
  2214        } else {
  2215          partition2.merge(node);
  2216        }
  2217      } // construct components corresponding to each disjoint subset of nodes
  2218  
  2219  
  2220      var constructComponent = function constructComponent(subset) {
  2221        var component = _this.spawn();
  2222  
  2223        subset.forEach(function (node) {
  2224          component.merge(node);
  2225          node.connectedEdges().forEach(function (edge) {
  2226            // ensure edge is within calling collection and edge is not in cut
  2227            if (_this.contains(edge) && !cut.contains(edge)) {
  2228              component.merge(edge);
  2229            }
  2230          });
  2231        });
  2232        return component;
  2233      };
  2234  
  2235      var components = [constructComponent(partition1), constructComponent(partition2)];
  2236      var ret = {
  2237        cut: cut,
  2238        components: components,
  2239        // n.b. partitions are included to be compatible with the old api spec
  2240        // (could be removed in a future major version)
  2241        partition1: partition1,
  2242        partition2: partition2
  2243      };
  2244      return ret;
  2245    }
  2246  }; // elesfn
  2247  
  2248  var copyPosition = function copyPosition(p) {
  2249    return {
  2250      x: p.x,
  2251      y: p.y
  2252    };
  2253  };
  2254  var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
  2255    return {
  2256      x: p.x * zoom + pan.x,
  2257      y: p.y * zoom + pan.y
  2258    };
  2259  };
  2260  var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
  2261    return {
  2262      x: (p.x - pan.x) / zoom,
  2263      y: (p.y - pan.y) / zoom
  2264    };
  2265  };
  2266  var array2point = function array2point(arr) {
  2267    return {
  2268      x: arr[0],
  2269      y: arr[1]
  2270    };
  2271  };
  2272  var min = function min(arr) {
  2273    var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  2274    var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
  2275    var min = Infinity;
  2276  
  2277    for (var i = begin; i < end; i++) {
  2278      var val = arr[i];
  2279  
  2280      if (isFinite(val)) {
  2281        min = Math.min(val, min);
  2282      }
  2283    }
  2284  
  2285    return min;
  2286  };
  2287  var max = function max(arr) {
  2288    var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  2289    var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
  2290    var max = -Infinity;
  2291  
  2292    for (var i = begin; i < end; i++) {
  2293      var val = arr[i];
  2294  
  2295      if (isFinite(val)) {
  2296        max = Math.max(val, max);
  2297      }
  2298    }
  2299  
  2300    return max;
  2301  };
  2302  var mean = function mean(arr) {
  2303    var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  2304    var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
  2305    var total = 0;
  2306    var n = 0;
  2307  
  2308    for (var i = begin; i < end; i++) {
  2309      var val = arr[i];
  2310  
  2311      if (isFinite(val)) {
  2312        total += val;
  2313        n++;
  2314      }
  2315    }
  2316  
  2317    return total / n;
  2318  };
  2319  var median = function median(arr) {
  2320    var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  2321    var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
  2322    var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
  2323    var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
  2324    var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
  2325  
  2326    if (copy) {
  2327      arr = arr.slice(begin, end);
  2328    } else {
  2329      if (end < arr.length) {
  2330        arr.splice(end, arr.length - end);
  2331      }
  2332  
  2333      if (begin > 0) {
  2334        arr.splice(0, begin);
  2335      }
  2336    } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
  2337  
  2338  
  2339    var off = 0; // offset from non-finite values
  2340  
  2341    for (var i = arr.length - 1; i >= 0; i--) {
  2342      var v = arr[i];
  2343  
  2344      if (includeHoles) {
  2345        if (!isFinite(v)) {
  2346          arr[i] = -Infinity;
  2347          off++;
  2348        }
  2349      } else {
  2350        // just remove it if we don't want to consider holes
  2351        arr.splice(i, 1);
  2352      }
  2353    }
  2354  
  2355    if (sort) {
  2356      arr.sort(function (a, b) {
  2357        return a - b;
  2358      }); // requires copy = true if you don't want to change the orig
  2359    }
  2360  
  2361    var len = arr.length;
  2362    var mid = Math.floor(len / 2);
  2363  
  2364    if (len % 2 !== 0) {
  2365      return arr[mid + 1 + off];
  2366    } else {
  2367      return (arr[mid - 1 + off] + arr[mid + off]) / 2;
  2368    }
  2369  };
  2370  var deg2rad = function deg2rad(deg) {
  2371    return Math.PI * deg / 180;
  2372  };
  2373  var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
  2374    return Math.atan2(dispY, dispX) - Math.PI / 2;
  2375  };
  2376  var log2 = Math.log2 || function (n) {
  2377    return Math.log(n) / Math.log(2);
  2378  };
  2379  var signum = function signum(x) {
  2380    if (x > 0) {
  2381      return 1;
  2382    } else if (x < 0) {
  2383      return -1;
  2384    } else {
  2385      return 0;
  2386    }
  2387  };
  2388  var dist = function dist(p1, p2) {
  2389    return Math.sqrt(sqdist(p1, p2));
  2390  };
  2391  var sqdist = function sqdist(p1, p2) {
  2392    var dx = p2.x - p1.x;
  2393    var dy = p2.y - p1.y;
  2394    return dx * dx + dy * dy;
  2395  };
  2396  var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
  2397    var length = v.length; // First, get sum of all elements
  2398  
  2399    var total = 0;
  2400  
  2401    for (var i = 0; i < length; i++) {
  2402      total += v[i];
  2403    } // Now, divide each by the sum of all elements
  2404  
  2405  
  2406    for (var _i = 0; _i < length; _i++) {
  2407      v[_i] = v[_i] / total;
  2408    }
  2409  
  2410    return v;
  2411  };
  2412  
  2413  var qbezierAt = function qbezierAt(p0, p1, p2, t) {
  2414    return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
  2415  };
  2416  var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
  2417    return {
  2418      x: qbezierAt(p0.x, p1.x, p2.x, t),
  2419      y: qbezierAt(p0.y, p1.y, p2.y, t)
  2420    };
  2421  };
  2422  var lineAt = function lineAt(p0, p1, t, d) {
  2423    var vec = {
  2424      x: p1.x - p0.x,
  2425      y: p1.y - p0.y
  2426    };
  2427    var vecDist = dist(p0, p1);
  2428    var normVec = {
  2429      x: vec.x / vecDist,
  2430      y: vec.y / vecDist
  2431    };
  2432    t = t == null ? 0 : t;
  2433    d = d != null ? d : t * vecDist;
  2434    return {
  2435      x: p0.x + normVec.x * d,
  2436      y: p0.y + normVec.y * d
  2437    };
  2438  };
  2439  var bound = function bound(min, val, max) {
  2440    return Math.max(min, Math.min(max, val));
  2441  }; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
  2442  
  2443  var makeBoundingBox = function makeBoundingBox(bb) {
  2444    if (bb == null) {
  2445      return {
  2446        x1: Infinity,
  2447        y1: Infinity,
  2448        x2: -Infinity,
  2449        y2: -Infinity,
  2450        w: 0,
  2451        h: 0
  2452      };
  2453    } else if (bb.x1 != null && bb.y1 != null) {
  2454      if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
  2455        return {
  2456          x1: bb.x1,
  2457          y1: bb.y1,
  2458          x2: bb.x2,
  2459          y2: bb.y2,
  2460          w: bb.x2 - bb.x1,
  2461          h: bb.y2 - bb.y1
  2462        };
  2463      } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
  2464        return {
  2465          x1: bb.x1,
  2466          y1: bb.y1,
  2467          x2: bb.x1 + bb.w,
  2468          y2: bb.y1 + bb.h,
  2469          w: bb.w,
  2470          h: bb.h
  2471        };
  2472      }
  2473    }
  2474  };
  2475  var copyBoundingBox = function copyBoundingBox(bb) {
  2476    return {
  2477      x1: bb.x1,
  2478      x2: bb.x2,
  2479      w: bb.w,
  2480      y1: bb.y1,
  2481      y2: bb.y2,
  2482      h: bb.h
  2483    };
  2484  };
  2485  var clearBoundingBox = function clearBoundingBox(bb) {
  2486    bb.x1 = Infinity;
  2487    bb.y1 = Infinity;
  2488    bb.x2 = -Infinity;
  2489    bb.y2 = -Infinity;
  2490    bb.w = 0;
  2491    bb.h = 0;
  2492  };
  2493  var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
  2494    // update bb1 with bb2 bounds
  2495    bb1.x1 = Math.min(bb1.x1, bb2.x1);
  2496    bb1.x2 = Math.max(bb1.x2, bb2.x2);
  2497    bb1.w = bb1.x2 - bb1.x1;
  2498    bb1.y1 = Math.min(bb1.y1, bb2.y1);
  2499    bb1.y2 = Math.max(bb1.y2, bb2.y2);
  2500    bb1.h = bb1.y2 - bb1.y1;
  2501  };
  2502  var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
  2503    bb.x1 = Math.min(bb.x1, x);
  2504    bb.x2 = Math.max(bb.x2, x);
  2505    bb.w = bb.x2 - bb.x1;
  2506    bb.y1 = Math.min(bb.y1, y);
  2507    bb.y2 = Math.max(bb.y2, y);
  2508    bb.h = bb.y2 - bb.y1;
  2509  };
  2510  var expandBoundingBox = function expandBoundingBox(bb) {
  2511    var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  2512    bb.x1 -= padding;
  2513    bb.x2 += padding;
  2514    bb.y1 -= padding;
  2515    bb.y2 += padding;
  2516    bb.w = bb.x2 - bb.x1;
  2517    bb.h = bb.y2 - bb.y1;
  2518    return bb;
  2519  };
  2520  var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
  2521    var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
  2522    var top, right, bottom, left;
  2523  
  2524    if (padding.length === 1) {
  2525      top = right = bottom = left = padding[0];
  2526    } else if (padding.length === 2) {
  2527      top = bottom = padding[0];
  2528      left = right = padding[1];
  2529    } else if (padding.length === 4) {
  2530      var _padding = _slicedToArray(padding, 4);
  2531  
  2532      top = _padding[0];
  2533      right = _padding[1];
  2534      bottom = _padding[2];
  2535      left = _padding[3];
  2536    }
  2537  
  2538    bb.x1 -= left;
  2539    bb.x2 += right;
  2540    bb.y1 -= top;
  2541    bb.y2 += bottom;
  2542    bb.w = bb.x2 - bb.x1;
  2543    bb.h = bb.y2 - bb.y1;
  2544    return bb;
  2545  };
  2546  
  2547  var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
  2548    bb1.x1 = bb2.x1;
  2549    bb1.y1 = bb2.y1;
  2550    bb1.x2 = bb2.x2;
  2551    bb1.y2 = bb2.y2;
  2552    bb1.w = bb1.x2 - bb1.x1;
  2553    bb1.h = bb1.y2 - bb1.y1;
  2554  };
  2555  var assignShiftToBoundingBox = function assignShiftToBoundingBox(bb, delta) {
  2556    bb.x1 += delta.x;
  2557    bb.x2 += delta.x;
  2558    bb.y1 += delta.y;
  2559    bb.y2 += delta.y;
  2560  };
  2561  var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
  2562    // case: one bb to right of other
  2563    if (bb1.x1 > bb2.x2) {
  2564      return false;
  2565    }
  2566  
  2567    if (bb2.x1 > bb1.x2) {
  2568      return false;
  2569    } // case: one bb to left of other
  2570  
  2571  
  2572    if (bb1.x2 < bb2.x1) {
  2573      return false;
  2574    }
  2575  
  2576    if (bb2.x2 < bb1.x1) {
  2577      return false;
  2578    } // case: one bb above other
  2579  
  2580  
  2581    if (bb1.y2 < bb2.y1) {
  2582      return false;
  2583    }
  2584  
  2585    if (bb2.y2 < bb1.y1) {
  2586      return false;
  2587    } // case: one bb below other
  2588  
  2589  
  2590    if (bb1.y1 > bb2.y2) {
  2591      return false;
  2592    }
  2593  
  2594    if (bb2.y1 > bb1.y2) {
  2595      return false;
  2596    } // otherwise, must have some overlap
  2597  
  2598  
  2599    return true;
  2600  };
  2601  var inBoundingBox = function inBoundingBox(bb, x, y) {
  2602    return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
  2603  };
  2604  var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
  2605    return inBoundingBox(bb, pt.x, pt.y);
  2606  };
  2607  var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
  2608    return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
  2609  };
  2610  var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
  2611    var cornerRadius = getRoundRectangleRadius(width, height);
  2612    var halfWidth = width / 2;
  2613    var halfHeight = height / 2; // Check intersections with straight line segments
  2614  
  2615    var straightLineIntersections; // Top segment, left to right
  2616  
  2617    {
  2618      var topStartX = nodeX - halfWidth + cornerRadius - padding;
  2619      var topStartY = nodeY - halfHeight - padding;
  2620      var topEndX = nodeX + halfWidth - cornerRadius + padding;
  2621      var topEndY = topStartY;
  2622      straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
  2623  
  2624      if (straightLineIntersections.length > 0) {
  2625        return straightLineIntersections;
  2626      }
  2627    } // Right segment, top to bottom
  2628  
  2629    {
  2630      var rightStartX = nodeX + halfWidth + padding;
  2631      var rightStartY = nodeY - halfHeight + cornerRadius - padding;
  2632      var rightEndX = rightStartX;
  2633      var rightEndY = nodeY + halfHeight - cornerRadius + padding;
  2634      straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
  2635  
  2636      if (straightLineIntersections.length > 0) {
  2637        return straightLineIntersections;
  2638      }
  2639    } // Bottom segment, left to right
  2640  
  2641    {
  2642      var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
  2643      var bottomStartY = nodeY + halfHeight + padding;
  2644      var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
  2645      var bottomEndY = bottomStartY;
  2646      straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
  2647  
  2648      if (straightLineIntersections.length > 0) {
  2649        return straightLineIntersections;
  2650      }
  2651    } // Left segment, top to bottom
  2652  
  2653    {
  2654      var leftStartX = nodeX - halfWidth - padding;
  2655      var leftStartY = nodeY - halfHeight + cornerRadius - padding;
  2656      var leftEndX = leftStartX;
  2657      var leftEndY = nodeY + halfHeight - cornerRadius + padding;
  2658      straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
  2659  
  2660      if (straightLineIntersections.length > 0) {
  2661        return straightLineIntersections;
  2662      }
  2663    } // Check intersections with arc segments
  2664  
  2665    var arcIntersections; // Top Left
  2666  
  2667    {
  2668      var topLeftCenterX = nodeX - halfWidth + cornerRadius;
  2669      var topLeftCenterY = nodeY - halfHeight + cornerRadius;
  2670      arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
  2671  
  2672      if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
  2673        return [arcIntersections[0], arcIntersections[1]];
  2674      }
  2675    } // Top Right
  2676  
  2677    {
  2678      var topRightCenterX = nodeX + halfWidth - cornerRadius;
  2679      var topRightCenterY = nodeY - halfHeight + cornerRadius;
  2680      arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
  2681  
  2682      if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
  2683        return [arcIntersections[0], arcIntersections[1]];
  2684      }
  2685    } // Bottom Right
  2686  
  2687    {
  2688      var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
  2689      var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
  2690      arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
  2691  
  2692      if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
  2693        return [arcIntersections[0], arcIntersections[1]];
  2694      }
  2695    } // Bottom Left
  2696  
  2697    {
  2698      var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
  2699      var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
  2700      arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
  2701  
  2702      if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
  2703        return [arcIntersections[0], arcIntersections[1]];
  2704      }
  2705    }
  2706    return []; // if nothing
  2707  };
  2708  var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
  2709    var t = tolerance;
  2710    var x1 = Math.min(lx1, lx2);
  2711    var x2 = Math.max(lx1, lx2);
  2712    var y1 = Math.min(ly1, ly2);
  2713    var y2 = Math.max(ly1, ly2);
  2714    return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
  2715  };
  2716  var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
  2717    var bb = {
  2718      x1: Math.min(x1, x3, x2) - tolerance,
  2719      x2: Math.max(x1, x3, x2) + tolerance,
  2720      y1: Math.min(y1, y3, y2) - tolerance,
  2721      y2: Math.max(y1, y3, y2) + tolerance
  2722    }; // if outside the rough bounding box for the bezier, then it can't be a hit
  2723  
  2724    if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
  2725      // console.log('bezier out of rough bb')
  2726      return false;
  2727    } else {
  2728      // console.log('do more expensive check');
  2729      return true;
  2730    }
  2731  };
  2732  var solveQuadratic = function solveQuadratic(a, b, c, val) {
  2733    c -= val;
  2734    var r = b * b - 4 * a * c;
  2735  
  2736    if (r < 0) {
  2737      return [];
  2738    }
  2739  
  2740    var sqrtR = Math.sqrt(r);
  2741    var denom = 2 * a;
  2742    var root1 = (-b + sqrtR) / denom;
  2743    var root2 = (-b - sqrtR) / denom;
  2744    return [root1, root2];
  2745  };
  2746  var solveCubic = function solveCubic(a, b, c, d, result) {
  2747    // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
  2748    // r is the real component, i is the imaginary component
  2749    // An implementation of the Cardano method from the year 1545
  2750    // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
  2751    var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
  2752  
  2753    if (a === 0) {
  2754      a = epsilon;
  2755    }
  2756  
  2757    b /= a;
  2758    c /= a;
  2759    d /= a;
  2760    var discriminant, q, r, dum1, s, t, term1, r13;
  2761    q = (3.0 * c - b * b) / 9.0;
  2762    r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
  2763    r /= 54.0;
  2764    discriminant = q * q * q + r * r;
  2765    result[1] = 0;
  2766    term1 = b / 3.0;
  2767  
  2768    if (discriminant > 0) {
  2769      s = r + Math.sqrt(discriminant);
  2770      s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
  2771      t = r - Math.sqrt(discriminant);
  2772      t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
  2773      result[0] = -term1 + s + t;
  2774      term1 += (s + t) / 2.0;
  2775      result[4] = result[2] = -term1;
  2776      term1 = Math.sqrt(3.0) * (-t + s) / 2;
  2777      result[3] = term1;
  2778      result[5] = -term1;
  2779      return;
  2780    }
  2781  
  2782    result[5] = result[3] = 0;
  2783  
  2784    if (discriminant === 0) {
  2785      r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
  2786      result[0] = -term1 + 2.0 * r13;
  2787      result[4] = result[2] = -(r13 + term1);
  2788      return;
  2789    }
  2790  
  2791    q = -q;
  2792    dum1 = q * q * q;
  2793    dum1 = Math.acos(r / Math.sqrt(dum1));
  2794    r13 = 2.0 * Math.sqrt(q);
  2795    result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
  2796    result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
  2797    result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
  2798    return;
  2799  };
  2800  var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
  2801    // Find minimum distance by using the minimum of the distance
  2802    // function between the given point and the curve
  2803    // This gives the coefficients of the resulting cubic equation
  2804    // whose roots tell us where a possible minimum is
  2805    // (Coefficients are divided by 4)
  2806    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;
  2807    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;
  2808    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;
  2809    var d = 1.0 * x1 * x2 - x1 * x1 + x1 * x - x2 * x + y1 * y2 - y1 * y1 + y1 * y - y2 * y; // debug("coefficients: " + a / a + ", " + b / a + ", " + c / a + ", " + d / a);
  2810  
  2811    var roots = []; // Use the cubic solving algorithm
  2812  
  2813    solveCubic(a, b, c, d, roots);
  2814    var zeroThreshold = 0.0000001;
  2815    var params = [];
  2816  
  2817    for (var index = 0; index < 6; index += 2) {
  2818      if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
  2819        params.push(roots[index]);
  2820      }
  2821    }
  2822  
  2823    params.push(1.0);
  2824    params.push(0.0);
  2825    var minDistanceSquared = -1;
  2826    var curX, curY, distSquared;
  2827  
  2828    for (var i = 0; i < params.length; i++) {
  2829      curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
  2830      curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
  2831      distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
  2832  
  2833      if (minDistanceSquared >= 0) {
  2834        if (distSquared < minDistanceSquared) {
  2835          minDistanceSquared = distSquared;
  2836        }
  2837      } else {
  2838        minDistanceSquared = distSquared;
  2839      }
  2840    }
  2841  
  2842    return minDistanceSquared;
  2843  };
  2844  var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
  2845    var offset = [x - x1, y - y1];
  2846    var line = [x2 - x1, y2 - y1];
  2847    var lineSq = line[0] * line[0] + line[1] * line[1];
  2848    var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
  2849    var dotProduct = offset[0] * line[0] + offset[1] * line[1];
  2850    var adjSq = dotProduct * dotProduct / lineSq;
  2851  
  2852    if (dotProduct < 0) {
  2853      return hypSq;
  2854    }
  2855  
  2856    if (adjSq > lineSq) {
  2857      return (x - x2) * (x - x2) + (y - y2) * (y - y2);
  2858    }
  2859  
  2860    return hypSq - adjSq;
  2861  };
  2862  var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
  2863    var x1, y1, x2, y2;
  2864    var y3; // Intersect with vertical line through (x, y)
  2865  
  2866    var up = 0; // let down = 0;
  2867  
  2868    for (var i = 0; i < points.length / 2; i++) {
  2869      x1 = points[i * 2];
  2870      y1 = points[i * 2 + 1];
  2871  
  2872      if (i + 1 < points.length / 2) {
  2873        x2 = points[(i + 1) * 2];
  2874        y2 = points[(i + 1) * 2 + 1];
  2875      } else {
  2876        x2 = points[(i + 1 - points.length / 2) * 2];
  2877        y2 = points[(i + 1 - points.length / 2) * 2 + 1];
  2878      }
  2879  
  2880      if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
  2881        y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
  2882  
  2883        if (y3 > y) {
  2884          up++;
  2885        } // if( y3 < y ){
  2886        // down++;
  2887        // }
  2888  
  2889      } else {
  2890        continue;
  2891      }
  2892    }
  2893  
  2894    if (up % 2 === 0) {
  2895      return false;
  2896    } else {
  2897      return true;
  2898    }
  2899  };
  2900  var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
  2901    var transformedPoints = new Array(basePoints.length); // Gives negative angle
  2902  
  2903    var angle;
  2904  
  2905    if (direction[0] != null) {
  2906      angle = Math.atan(direction[1] / direction[0]);
  2907  
  2908      if (direction[0] < 0) {
  2909        angle = angle + Math.PI / 2;
  2910      } else {
  2911        angle = -angle - Math.PI / 2;
  2912      }
  2913    } else {
  2914      angle = direction;
  2915    }
  2916  
  2917    var cos = Math.cos(-angle);
  2918    var sin = Math.sin(-angle); //    console.log("base: " + basePoints);
  2919  
  2920    for (var i = 0; i < transformedPoints.length / 2; i++) {
  2921      transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
  2922      transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
  2923      transformedPoints[i * 2] += centerX;
  2924      transformedPoints[i * 2 + 1] += centerY;
  2925    }
  2926  
  2927    var points;
  2928  
  2929    if (padding > 0) {
  2930      var expandedLineSet = expandPolygon(transformedPoints, -padding);
  2931      points = joinLines(expandedLineSet);
  2932    } else {
  2933      points = transformedPoints;
  2934    }
  2935  
  2936    return pointInsidePolygonPoints(x, y, points);
  2937  };
  2938  var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
  2939    var cutPolygonPoints = new Array(basePoints.length);
  2940    var halfW = width / 2;
  2941    var halfH = height / 2;
  2942    var cornerRadius = getRoundPolygonRadius(width, height);
  2943    var squaredCornerRadius = cornerRadius * cornerRadius;
  2944  
  2945    for (var i = 0; i < basePoints.length / 4; i++) {
  2946      var sourceUv = void 0,
  2947          destUv = void 0;
  2948  
  2949      if (i === 0) {
  2950        sourceUv = basePoints.length - 2;
  2951      } else {
  2952        sourceUv = i * 4 - 2;
  2953      }
  2954  
  2955      destUv = i * 4 + 2;
  2956      var px = centerX + halfW * basePoints[i * 4];
  2957      var py = centerY + halfH * basePoints[i * 4 + 1];
  2958      var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
  2959      var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
  2960      var cp0x = px - offset * basePoints[sourceUv];
  2961      var cp0y = py - offset * basePoints[sourceUv + 1];
  2962      var cp1x = px + offset * basePoints[destUv];
  2963      var cp1y = py + offset * basePoints[destUv + 1];
  2964      cutPolygonPoints[i * 4] = cp0x;
  2965      cutPolygonPoints[i * 4 + 1] = cp0y;
  2966      cutPolygonPoints[i * 4 + 2] = cp1x;
  2967      cutPolygonPoints[i * 4 + 3] = cp1y;
  2968      var orthx = basePoints[sourceUv + 1];
  2969      var orthy = -basePoints[sourceUv];
  2970      var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
  2971  
  2972      if (cosAlpha < 0) {
  2973        orthx *= -1;
  2974        orthy *= -1;
  2975      }
  2976  
  2977      var cx = cp0x + orthx * cornerRadius;
  2978      var cy = cp0y + orthy * cornerRadius;
  2979      var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
  2980  
  2981      if (squaredDistance <= squaredCornerRadius) {
  2982        return true;
  2983      }
  2984    }
  2985  
  2986    return pointInsidePolygonPoints(x, y, cutPolygonPoints);
  2987  };
  2988  var joinLines = function joinLines(lineSet) {
  2989    var vertices = new Array(lineSet.length / 2);
  2990    var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
  2991    var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
  2992  
  2993    for (var i = 0; i < lineSet.length / 4; i++) {
  2994      currentLineStartX = lineSet[i * 4];
  2995      currentLineStartY = lineSet[i * 4 + 1];
  2996      currentLineEndX = lineSet[i * 4 + 2];
  2997      currentLineEndY = lineSet[i * 4 + 3];
  2998  
  2999      if (i < lineSet.length / 4 - 1) {
  3000        nextLineStartX = lineSet[(i + 1) * 4];
  3001        nextLineStartY = lineSet[(i + 1) * 4 + 1];
  3002        nextLineEndX = lineSet[(i + 1) * 4 + 2];
  3003        nextLineEndY = lineSet[(i + 1) * 4 + 3];
  3004      } else {
  3005        nextLineStartX = lineSet[0];
  3006        nextLineStartY = lineSet[1];
  3007        nextLineEndX = lineSet[2];
  3008        nextLineEndY = lineSet[3];
  3009      }
  3010  
  3011      var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
  3012      vertices[i * 2] = intersection[0];
  3013      vertices[i * 2 + 1] = intersection[1];
  3014    }
  3015  
  3016    return vertices;
  3017  };
  3018  var expandPolygon = function expandPolygon(points, pad) {
  3019    var expandedLineSet = new Array(points.length * 2);
  3020    var currentPointX, currentPointY, nextPointX, nextPointY;
  3021  
  3022    for (var i = 0; i < points.length / 2; i++) {
  3023      currentPointX = points[i * 2];
  3024      currentPointY = points[i * 2 + 1];
  3025  
  3026      if (i < points.length / 2 - 1) {
  3027        nextPointX = points[(i + 1) * 2];
  3028        nextPointY = points[(i + 1) * 2 + 1];
  3029      } else {
  3030        nextPointX = points[0];
  3031        nextPointY = points[1];
  3032      } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
  3033      // Assume CCW polygon winding
  3034  
  3035  
  3036      var offsetX = nextPointY - currentPointY;
  3037      var offsetY = -(nextPointX - currentPointX); // Normalize
  3038  
  3039      var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
  3040      var normalizedOffsetX = offsetX / offsetLength;
  3041      var normalizedOffsetY = offsetY / offsetLength;
  3042      expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
  3043      expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
  3044      expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
  3045      expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
  3046    }
  3047  
  3048    return expandedLineSet;
  3049  };
  3050  var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
  3051    var dispX = centerX - x;
  3052    var dispY = centerY - y;
  3053    dispX /= ellipseWradius;
  3054    dispY /= ellipseHradius;
  3055    var len = Math.sqrt(dispX * dispX + dispY * dispY);
  3056    var newLength = len - 1;
  3057  
  3058    if (newLength < 0) {
  3059      return [];
  3060    }
  3061  
  3062    var lenProportion = newLength / len;
  3063    return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
  3064  };
  3065  var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
  3066    x -= centerX;
  3067    y -= centerY;
  3068    x /= width / 2 + padding;
  3069    y /= height / 2 + padding;
  3070    return x * x + y * y <= 1;
  3071  }; // Returns intersections of increasing distance from line's start point
  3072  
  3073  var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
  3074    // Calculate d, direction vector of line
  3075    var d = [x2 - x1, y2 - y1]; // Direction vector of line
  3076  
  3077    var f = [x1 - centerX, y1 - centerY];
  3078    var a = d[0] * d[0] + d[1] * d[1];
  3079    var b = 2 * (f[0] * d[0] + f[1] * d[1]);
  3080    var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
  3081    var discriminant = b * b - 4 * a * c;
  3082  
  3083    if (discriminant < 0) {
  3084      return [];
  3085    }
  3086  
  3087    var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
  3088    var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
  3089    var tMin = Math.min(t1, t2);
  3090    var tMax = Math.max(t1, t2);
  3091    var inRangeParams = [];
  3092  
  3093    if (tMin >= 0 && tMin <= 1) {
  3094      inRangeParams.push(tMin);
  3095    }
  3096  
  3097    if (tMax >= 0 && tMax <= 1) {
  3098      inRangeParams.push(tMax);
  3099    }
  3100  
  3101    if (inRangeParams.length === 0) {
  3102      return [];
  3103    }
  3104  
  3105    var nearIntersectionX = inRangeParams[0] * d[0] + x1;
  3106    var nearIntersectionY = inRangeParams[0] * d[1] + y1;
  3107  
  3108    if (inRangeParams.length > 1) {
  3109      if (inRangeParams[0] == inRangeParams[1]) {
  3110        return [nearIntersectionX, nearIntersectionY];
  3111      } else {
  3112        var farIntersectionX = inRangeParams[1] * d[0] + x1;
  3113        var farIntersectionY = inRangeParams[1] * d[1] + y1;
  3114        return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
  3115      }
  3116    } else {
  3117      return [nearIntersectionX, nearIntersectionY];
  3118    }
  3119  };
  3120  var midOfThree = function midOfThree(a, b, c) {
  3121    if (b <= a && a <= c || c <= a && a <= b) {
  3122      return a;
  3123    } else if (a <= b && b <= c || c <= b && b <= a) {
  3124      return b;
  3125    } else {
  3126      return c;
  3127    }
  3128  }; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
  3129  
  3130  var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
  3131    var dx13 = x1 - x3;
  3132    var dx21 = x2 - x1;
  3133    var dx43 = x4 - x3;
  3134    var dy13 = y1 - y3;
  3135    var dy21 = y2 - y1;
  3136    var dy43 = y4 - y3;
  3137    var ua_t = dx43 * dy13 - dy43 * dx13;
  3138    var ub_t = dx21 * dy13 - dy21 * dx13;
  3139    var u_b = dy43 * dx21 - dx43 * dy21;
  3140  
  3141    if (u_b !== 0) {
  3142      var ua = ua_t / u_b;
  3143      var ub = ub_t / u_b;
  3144      var flptThreshold = 0.001;
  3145  
  3146      var _min = 0 - flptThreshold;
  3147  
  3148      var _max = 1 + flptThreshold;
  3149  
  3150      if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
  3151        return [x1 + ua * dx21, y1 + ua * dy21];
  3152      } else {
  3153        if (!infiniteLines) {
  3154          return [];
  3155        } else {
  3156          return [x1 + ua * dx21, y1 + ua * dy21];
  3157        }
  3158      }
  3159    } else {
  3160      if (ua_t === 0 || ub_t === 0) {
  3161        // Parallel, coincident lines. Check if overlap
  3162        // Check endpoint of second line
  3163        if (midOfThree(x1, x2, x4) === x4) {
  3164          return [x4, y4];
  3165        } // Check start point of second line
  3166  
  3167  
  3168        if (midOfThree(x1, x2, x3) === x3) {
  3169          return [x3, y3];
  3170        } // Endpoint of first line
  3171  
  3172  
  3173        if (midOfThree(x3, x4, x2) === x2) {
  3174          return [x2, y2];
  3175        }
  3176  
  3177        return [];
  3178      } else {
  3179        // Parallel, non-coincident
  3180        return [];
  3181      }
  3182    }
  3183  }; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
  3184  // intersect a node polygon (pts transformed)
  3185  //
  3186  // math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
  3187  // intersect the points (no transform)
  3188  
  3189  var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
  3190    var intersections = [];
  3191    var intersection;
  3192    var transformedPoints = new Array(basePoints.length);
  3193    var doTransform = true;
  3194  
  3195    if (width == null) {
  3196      doTransform = false;
  3197    }
  3198  
  3199    var points;
  3200  
  3201    if (doTransform) {
  3202      for (var i = 0; i < transformedPoints.length / 2; i++) {
  3203        transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
  3204        transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
  3205      }
  3206  
  3207      if (padding > 0) {
  3208        var expandedLineSet = expandPolygon(transformedPoints, -padding);
  3209        points = joinLines(expandedLineSet);
  3210      } else {
  3211        points = transformedPoints;
  3212      }
  3213    } else {
  3214      points = basePoints;
  3215    }
  3216  
  3217    var currentX, currentY, nextX, nextY;
  3218  
  3219    for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
  3220      currentX = points[_i2 * 2];
  3221      currentY = points[_i2 * 2 + 1];
  3222  
  3223      if (_i2 < points.length / 2 - 1) {
  3224        nextX = points[(_i2 + 1) * 2];
  3225        nextY = points[(_i2 + 1) * 2 + 1];
  3226      } else {
  3227        nextX = points[0];
  3228        nextY = points[1];
  3229      }
  3230  
  3231      intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
  3232  
  3233      if (intersection.length !== 0) {
  3234        intersections.push(intersection[0], intersection[1]);
  3235      }
  3236    }
  3237  
  3238    return intersections;
  3239  };
  3240  var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
  3241    var intersections = [];
  3242    var intersection;
  3243    var lines = new Array(basePoints.length);
  3244    var halfW = width / 2;
  3245    var halfH = height / 2;
  3246    var cornerRadius = getRoundPolygonRadius(width, height);
  3247  
  3248    for (var i = 0; i < basePoints.length / 4; i++) {
  3249      var sourceUv = void 0,
  3250          destUv = void 0;
  3251  
  3252      if (i === 0) {
  3253        sourceUv = basePoints.length - 2;
  3254      } else {
  3255        sourceUv = i * 4 - 2;
  3256      }
  3257  
  3258      destUv = i * 4 + 2;
  3259      var px = centerX + halfW * basePoints[i * 4];
  3260      var py = centerY + halfH * basePoints[i * 4 + 1];
  3261      var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
  3262      var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
  3263      var cp0x = px - offset * basePoints[sourceUv];
  3264      var cp0y = py - offset * basePoints[sourceUv + 1];
  3265      var cp1x = px + offset * basePoints[destUv];
  3266      var cp1y = py + offset * basePoints[destUv + 1];
  3267  
  3268      if (i === 0) {
  3269        lines[basePoints.length - 2] = cp0x;
  3270        lines[basePoints.length - 1] = cp0y;
  3271      } else {
  3272        lines[i * 4 - 2] = cp0x;
  3273        lines[i * 4 - 1] = cp0y;
  3274      }
  3275  
  3276      lines[i * 4] = cp1x;
  3277      lines[i * 4 + 1] = cp1y;
  3278      var orthx = basePoints[sourceUv + 1];
  3279      var orthy = -basePoints[sourceUv];
  3280      var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
  3281  
  3282      if (cosAlpha < 0) {
  3283        orthx *= -1;
  3284        orthy *= -1;
  3285      }
  3286  
  3287      var cx = cp0x + orthx * cornerRadius;
  3288      var cy = cp0y + orthy * cornerRadius;
  3289      intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
  3290  
  3291      if (intersection.length !== 0) {
  3292        intersections.push(intersection[0], intersection[1]);
  3293      }
  3294    }
  3295  
  3296    for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
  3297      intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
  3298  
  3299      if (intersection.length !== 0) {
  3300        intersections.push(intersection[0], intersection[1]);
  3301      }
  3302    }
  3303  
  3304    if (intersections.length > 2) {
  3305      var lowestIntersection = [intersections[0], intersections[1]];
  3306      var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
  3307  
  3308      for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
  3309        var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
  3310  
  3311        if (squaredDistance <= lowestSquaredDistance) {
  3312          lowestIntersection[0] = intersections[_i4 * 2];
  3313          lowestIntersection[1] = intersections[_i4 * 2 + 1];
  3314          lowestSquaredDistance = squaredDistance;
  3315        }
  3316      }
  3317  
  3318      return lowestIntersection;
  3319    }
  3320  
  3321    return intersections;
  3322  };
  3323  var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
  3324    var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
  3325    var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
  3326    var lenRatio = (length - amount) / length;
  3327  
  3328    if (lenRatio < 0) {
  3329      lenRatio = 0.00001;
  3330    }
  3331  
  3332    return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
  3333  };
  3334  var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
  3335    var points = generateUnitNgonPoints(sides, rotationRadians);
  3336    points = fitPolygonToSquare(points);
  3337    return points;
  3338  };
  3339  var fitPolygonToSquare = function fitPolygonToSquare(points) {
  3340    var x, y;
  3341    var sides = points.length / 2;
  3342    var minX = Infinity,
  3343        minY = Infinity,
  3344        maxX = -Infinity,
  3345        maxY = -Infinity;
  3346  
  3347    for (var i = 0; i < sides; i++) {
  3348      x = points[2 * i];
  3349      y = points[2 * i + 1];
  3350      minX = Math.min(minX, x);
  3351      maxX = Math.max(maxX, x);
  3352      minY = Math.min(minY, y);
  3353      maxY = Math.max(maxY, y);
  3354    } // stretch factors
  3355  
  3356  
  3357    var sx = 2 / (maxX - minX);
  3358    var sy = 2 / (maxY - minY);
  3359  
  3360    for (var _i5 = 0; _i5 < sides; _i5++) {
  3361      x = points[2 * _i5] = points[2 * _i5] * sx;
  3362      y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
  3363      minX = Math.min(minX, x);
  3364      maxX = Math.max(maxX, x);
  3365      minY = Math.min(minY, y);
  3366      maxY = Math.max(maxY, y);
  3367    }
  3368  
  3369    if (minY < -1) {
  3370      for (var _i6 = 0; _i6 < sides; _i6++) {
  3371        y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
  3372      }
  3373    }
  3374  
  3375    return points;
  3376  };
  3377  var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
  3378    var increment = 1.0 / sides * 2 * Math.PI;
  3379    var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
  3380    startAngle += rotationRadians;
  3381    var points = new Array(sides * 2);
  3382    var currentAngle;
  3383  
  3384    for (var i = 0; i < sides; i++) {
  3385      currentAngle = i * increment + startAngle;
  3386      points[2 * i] = Math.cos(currentAngle); // x
  3387  
  3388      points[2 * i + 1] = Math.sin(-currentAngle); // y
  3389    }
  3390  
  3391    return points;
  3392  }; // Set the default radius, unless half of width or height is smaller than default
  3393  
  3394  var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
  3395    return Math.min(width / 4, height / 4, 8);
  3396  }; // Set the default radius
  3397  
  3398  var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
  3399    return Math.min(width / 10, height / 10, 8);
  3400  };
  3401  var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
  3402    return 8;
  3403  };
  3404  var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
  3405    return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
  3406  }; // get curve width, height, and control point position offsets as a percentage of node height / width
  3407  
  3408  var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
  3409    return {
  3410      heightOffset: Math.min(15, 0.05 * height),
  3411      widthOffset: Math.min(100, 0.25 * width),
  3412      ctrlPtOffsetPct: 0.05
  3413    };
  3414  };
  3415  
  3416  var pageRankDefaults = defaults({
  3417    dampingFactor: 0.8,
  3418    precision: 0.000001,
  3419    iterations: 200,
  3420    weight: function weight(edge) {
  3421      return 1;
  3422    }
  3423  });
  3424  var elesfn$7 = {
  3425    pageRank: function pageRank(options) {
  3426      var _pageRankDefaults = pageRankDefaults(options),
  3427          dampingFactor = _pageRankDefaults.dampingFactor,
  3428          precision = _pageRankDefaults.precision,
  3429          iterations = _pageRankDefaults.iterations,
  3430          weight = _pageRankDefaults.weight;
  3431  
  3432      var cy = this._private.cy;
  3433  
  3434      var _this$byGroup = this.byGroup(),
  3435          nodes = _this$byGroup.nodes,
  3436          edges = _this$byGroup.edges;
  3437  
  3438      var numNodes = nodes.length;
  3439      var numNodesSqd = numNodes * numNodes;
  3440      var numEdges = edges.length; // Construct transposed adjacency matrix
  3441      // First lets have a zeroed matrix of the right size
  3442      // We'll also keep track of the sum of each column
  3443  
  3444      var matrix = new Array(numNodesSqd);
  3445      var columnSum = new Array(numNodes);
  3446      var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
  3447  
  3448      for (var i = 0; i < numNodes; i++) {
  3449        for (var j = 0; j < numNodes; j++) {
  3450          var n = i * numNodes + j;
  3451          matrix[n] = 0;
  3452        }
  3453  
  3454        columnSum[i] = 0;
  3455      } // Now, process edges
  3456  
  3457  
  3458      for (var _i = 0; _i < numEdges; _i++) {
  3459        var edge = edges[_i];
  3460        var srcId = edge.data('source');
  3461        var tgtId = edge.data('target'); // Don't include loops in the matrix
  3462  
  3463        if (srcId === tgtId) {
  3464          continue;
  3465        }
  3466  
  3467        var s = nodes.indexOfId(srcId);
  3468        var t = nodes.indexOfId(tgtId);
  3469        var w = weight(edge);
  3470  
  3471        var _n = t * numNodes + s; // Update matrix
  3472  
  3473  
  3474        matrix[_n] += w; // Update column sum
  3475  
  3476        columnSum[s] += w;
  3477      } // Add additional probability based on damping factor
  3478      // Also, take into account columns that have sum = 0
  3479  
  3480  
  3481      var p = 1.0 / numNodes + additionalProb; // Shorthand
  3482      // Traverse matrix, column by column
  3483  
  3484      for (var _j = 0; _j < numNodes; _j++) {
  3485        if (columnSum[_j] === 0) {
  3486          // No 'links' out from node jth, assume equal probability for each possible node
  3487          for (var _i2 = 0; _i2 < numNodes; _i2++) {
  3488            var _n2 = _i2 * numNodes + _j;
  3489  
  3490            matrix[_n2] = p;
  3491          }
  3492        } else {
  3493          // Node jth has outgoing link, compute normalized probabilities
  3494          for (var _i3 = 0; _i3 < numNodes; _i3++) {
  3495            var _n3 = _i3 * numNodes + _j;
  3496  
  3497            matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
  3498          }
  3499        }
  3500      } // Compute dominant eigenvector using power method
  3501  
  3502  
  3503      var eigenvector = new Array(numNodes);
  3504      var temp = new Array(numNodes);
  3505      var previous; // Start with a vector of all 1's
  3506      // Also, initialize a null vector which will be used as shorthand
  3507  
  3508      for (var _i4 = 0; _i4 < numNodes; _i4++) {
  3509        eigenvector[_i4] = 1;
  3510      }
  3511  
  3512      for (var iter = 0; iter < iterations; iter++) {
  3513        // Temp array with all 0's
  3514        for (var _i5 = 0; _i5 < numNodes; _i5++) {
  3515          temp[_i5] = 0;
  3516        } // Multiply matrix with previous result
  3517  
  3518  
  3519        for (var _i6 = 0; _i6 < numNodes; _i6++) {
  3520          for (var _j2 = 0; _j2 < numNodes; _j2++) {
  3521            var _n4 = _i6 * numNodes + _j2;
  3522  
  3523            temp[_i6] += matrix[_n4] * eigenvector[_j2];
  3524          }
  3525        }
  3526  
  3527        inPlaceSumNormalize(temp);
  3528        previous = eigenvector;
  3529        eigenvector = temp;
  3530        temp = previous;
  3531        var diff = 0; // Compute difference (squared module) of both vectors
  3532  
  3533        for (var _i7 = 0; _i7 < numNodes; _i7++) {
  3534          var delta = previous[_i7] - eigenvector[_i7];
  3535          diff += delta * delta;
  3536        } // If difference is less than the desired threshold, stop iterating
  3537  
  3538  
  3539        if (diff < precision) {
  3540          break;
  3541        }
  3542      } // Construct result
  3543  
  3544  
  3545      var res = {
  3546        rank: function rank(node) {
  3547          node = cy.collection(node)[0];
  3548          return eigenvector[nodes.indexOf(node)];
  3549        }
  3550      };
  3551      return res;
  3552    } // pageRank
  3553  
  3554  }; // elesfn
  3555  
  3556  var defaults$1 = defaults({
  3557    root: null,
  3558    weight: function weight(edge) {
  3559      return 1;
  3560    },
  3561    directed: false,
  3562    alpha: 0
  3563  });
  3564  var elesfn$8 = {
  3565    degreeCentralityNormalized: function degreeCentralityNormalized(options) {
  3566      options = defaults$1(options);
  3567      var cy = this.cy();
  3568      var nodes = this.nodes();
  3569      var numNodes = nodes.length;
  3570  
  3571      if (!options.directed) {
  3572        var degrees = {};
  3573        var maxDegree = 0;
  3574  
  3575        for (var i = 0; i < numNodes; i++) {
  3576          var node = nodes[i]; // add current node to the current options object and call degreeCentrality
  3577  
  3578          options.root = node;
  3579          var currDegree = this.degreeCentrality(options);
  3580  
  3581          if (maxDegree < currDegree.degree) {
  3582            maxDegree = currDegree.degree;
  3583          }
  3584  
  3585          degrees[node.id()] = currDegree.degree;
  3586        }
  3587  
  3588        return {
  3589          degree: function degree(node) {
  3590            if (maxDegree === 0) {
  3591              return 0;
  3592            }
  3593  
  3594            if (string(node)) {
  3595              // from is a selector string
  3596              node = cy.filter(node);
  3597            }
  3598  
  3599            return degrees[node.id()] / maxDegree;
  3600          }
  3601        };
  3602      } else {
  3603        var indegrees = {};
  3604        var outdegrees = {};
  3605        var maxIndegree = 0;
  3606        var maxOutdegree = 0;
  3607  
  3608        for (var _i = 0; _i < numNodes; _i++) {
  3609          var _node = nodes[_i];
  3610  
  3611          var id = _node.id(); // add current node to the current options object and call degreeCentrality
  3612  
  3613  
  3614          options.root = _node;
  3615  
  3616          var _currDegree = this.degreeCentrality(options);
  3617  
  3618          if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
  3619          if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
  3620          indegrees[id] = _currDegree.indegree;
  3621          outdegrees[id] = _currDegree.outdegree;
  3622        }
  3623  
  3624        return {
  3625          indegree: function indegree(node) {
  3626            if (maxIndegree == 0) {
  3627              return 0;
  3628            }
  3629  
  3630            if (string(node)) {
  3631              // from is a selector string
  3632              node = cy.filter(node);
  3633            }
  3634  
  3635            return indegrees[node.id()] / maxIndegree;
  3636          },
  3637          outdegree: function outdegree(node) {
  3638            if (maxOutdegree === 0) {
  3639              return 0;
  3640            }
  3641  
  3642            if (string(node)) {
  3643              // from is a selector string
  3644              node = cy.filter(node);
  3645            }
  3646  
  3647            return outdegrees[node.id()] / maxOutdegree;
  3648          }
  3649        };
  3650      }
  3651    },
  3652    // degreeCentralityNormalized
  3653    // Implemented from the algorithm in Opsahl's paper
  3654    // "Node centrality in weighted networks: Generalizing degree and shortest paths"
  3655    // check the heading 2 "Degree"
  3656    degreeCentrality: function degreeCentrality(options) {
  3657      options = defaults$1(options);
  3658      var cy = this.cy();
  3659      var callingEles = this;
  3660      var _options = options,
  3661          root = _options.root,
  3662          weight = _options.weight,
  3663          directed = _options.directed,
  3664          alpha = _options.alpha;
  3665      root = cy.collection(root)[0];
  3666  
  3667      if (!directed) {
  3668        var connEdges = root.connectedEdges().intersection(callingEles);
  3669        var k = connEdges.length;
  3670        var s = 0; // Now, sum edge weights
  3671  
  3672        for (var i = 0; i < connEdges.length; i++) {
  3673          s += weight(connEdges[i]);
  3674        }
  3675  
  3676        return {
  3677          degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
  3678        };
  3679      } else {
  3680        var edges = root.connectedEdges();
  3681        var incoming = edges.filter(function (edge) {
  3682          return edge.target().same(root) && callingEles.has(edge);
  3683        });
  3684        var outgoing = edges.filter(function (edge) {
  3685          return edge.source().same(root) && callingEles.has(edge);
  3686        });
  3687        var k_in = incoming.length;
  3688        var k_out = outgoing.length;
  3689        var s_in = 0;
  3690        var s_out = 0; // Now, sum incoming edge weights
  3691  
  3692        for (var _i2 = 0; _i2 < incoming.length; _i2++) {
  3693          s_in += weight(incoming[_i2]);
  3694        } // Now, sum outgoing edge weights
  3695  
  3696  
  3697        for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
  3698          s_out += weight(outgoing[_i3]);
  3699        }
  3700  
  3701        return {
  3702          indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
  3703          outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
  3704        };
  3705      }
  3706    } // degreeCentrality
  3707  
  3708  }; // elesfn
  3709  // nice, short mathemathical alias
  3710  
  3711  elesfn$8.dc = elesfn$8.degreeCentrality;
  3712  elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
  3713  
  3714  var defaults$2 = defaults({
  3715    harmonic: true,
  3716    weight: function weight() {
  3717      return 1;
  3718    },
  3719    directed: false,
  3720    root: null
  3721  });
  3722  var elesfn$9 = {
  3723    closenessCentralityNormalized: function closenessCentralityNormalized(options) {
  3724      var _defaults = defaults$2(options),
  3725          harmonic = _defaults.harmonic,
  3726          weight = _defaults.weight,
  3727          directed = _defaults.directed;
  3728  
  3729      var cy = this.cy();
  3730      var closenesses = {};
  3731      var maxCloseness = 0;
  3732      var nodes = this.nodes();
  3733      var fw = this.floydWarshall({
  3734        weight: weight,
  3735        directed: directed
  3736      }); // Compute closeness for every node and find the maximum closeness
  3737  
  3738      for (var i = 0; i < nodes.length; i++) {
  3739        var currCloseness = 0;
  3740        var node_i = nodes[i];
  3741  
  3742        for (var j = 0; j < nodes.length; j++) {
  3743          if (i !== j) {
  3744            var d = fw.distance(node_i, nodes[j]);
  3745  
  3746            if (harmonic) {
  3747              currCloseness += 1 / d;
  3748            } else {
  3749              currCloseness += d;
  3750            }
  3751          }
  3752        }
  3753  
  3754        if (!harmonic) {
  3755          currCloseness = 1 / currCloseness;
  3756        }
  3757  
  3758        if (maxCloseness < currCloseness) {
  3759          maxCloseness = currCloseness;
  3760        }
  3761  
  3762        closenesses[node_i.id()] = currCloseness;
  3763      }
  3764  
  3765      return {
  3766        closeness: function closeness(node) {
  3767          if (maxCloseness == 0) {
  3768            return 0;
  3769          }
  3770  
  3771          if (string(node)) {
  3772            // from is a selector string
  3773            node = cy.filter(node)[0].id();
  3774          } else {
  3775            // from is a node
  3776            node = node.id();
  3777          }
  3778  
  3779          return closenesses[node] / maxCloseness;
  3780        }
  3781      };
  3782    },
  3783    // Implemented from pseudocode from wikipedia
  3784    closenessCentrality: function closenessCentrality(options) {
  3785      var _defaults2 = defaults$2(options),
  3786          root = _defaults2.root,
  3787          weight = _defaults2.weight,
  3788          directed = _defaults2.directed,
  3789          harmonic = _defaults2.harmonic;
  3790  
  3791      root = this.filter(root)[0]; // we need distance from this node to every other node
  3792  
  3793      var dijkstra = this.dijkstra({
  3794        root: root,
  3795        weight: weight,
  3796        directed: directed
  3797      });
  3798      var totalDistance = 0;
  3799      var nodes = this.nodes();
  3800  
  3801      for (var i = 0; i < nodes.length; i++) {
  3802        var n = nodes[i];
  3803  
  3804        if (!n.same(root)) {
  3805          var d = dijkstra.distanceTo(n);
  3806  
  3807          if (harmonic) {
  3808            totalDistance += 1 / d;
  3809          } else {
  3810            totalDistance += d;
  3811          }
  3812        }
  3813      }
  3814  
  3815      return harmonic ? totalDistance : 1 / totalDistance;
  3816    } // closenessCentrality
  3817  
  3818  }; // elesfn
  3819  // nice, short mathemathical alias
  3820  
  3821  elesfn$9.cc = elesfn$9.closenessCentrality;
  3822  elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
  3823  
  3824  var defaults$3 = defaults({
  3825    weight: null,
  3826    directed: false
  3827  });
  3828  var elesfn$a = {
  3829    // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
  3830    betweennessCentrality: function betweennessCentrality(options) {
  3831      var _defaults = defaults$3(options),
  3832          directed = _defaults.directed,
  3833          weight = _defaults.weight;
  3834  
  3835      var weighted = weight != null;
  3836      var cy = this.cy(); // starting
  3837  
  3838      var V = this.nodes();
  3839      var A = {};
  3840      var _C = {};
  3841      var max = 0;
  3842      var C = {
  3843        set: function set(key, val) {
  3844          _C[key] = val;
  3845  
  3846          if (val > max) {
  3847            max = val;
  3848          }
  3849        },
  3850        get: function get(key) {
  3851          return _C[key];
  3852        }
  3853      }; // A contains the neighborhoods of every node
  3854  
  3855      for (var i = 0; i < V.length; i++) {
  3856        var v = V[i];
  3857        var vid = v.id();
  3858  
  3859        if (directed) {
  3860          A[vid] = v.outgoers().nodes(); // get outgoers of every node
  3861        } else {
  3862          A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
  3863        }
  3864  
  3865        C.set(vid, 0);
  3866      }
  3867  
  3868      var _loop = function _loop(s) {
  3869        var sid = V[s].id();
  3870        var S = []; // stack
  3871  
  3872        var P = {};
  3873        var g = {};
  3874        var d = {};
  3875        var Q = new Heap(function (a, b) {
  3876          return d[a] - d[b];
  3877        }); // queue
  3878        // init dictionaries
  3879  
  3880        for (var _i = 0; _i < V.length; _i++) {
  3881          var _vid = V[_i].id();
  3882  
  3883          P[_vid] = [];
  3884          g[_vid] = 0;
  3885          d[_vid] = Infinity;
  3886        }
  3887  
  3888        g[sid] = 1; // sigma
  3889  
  3890        d[sid] = 0; // distance to s
  3891  
  3892        Q.push(sid);
  3893  
  3894        while (!Q.empty()) {
  3895          var _v = Q.pop();
  3896  
  3897          S.push(_v);
  3898  
  3899          if (weighted) {
  3900            for (var j = 0; j < A[_v].length; j++) {
  3901              var w = A[_v][j];
  3902              var vEle = cy.getElementById(_v);
  3903              var edge = void 0;
  3904  
  3905              if (vEle.edgesTo(w).length > 0) {
  3906                edge = vEle.edgesTo(w)[0];
  3907              } else {
  3908                edge = w.edgesTo(vEle)[0];
  3909              }
  3910  
  3911              var edgeWeight = weight(edge);
  3912              w = w.id();
  3913  
  3914              if (d[w] > d[_v] + edgeWeight) {
  3915                d[w] = d[_v] + edgeWeight;
  3916  
  3917                if (Q.nodes.indexOf(w) < 0) {
  3918                  //if w is not in Q
  3919                  Q.push(w);
  3920                } else {
  3921                  // update position if w is in Q
  3922                  Q.updateItem(w);
  3923                }
  3924  
  3925                g[w] = 0;
  3926                P[w] = [];
  3927              }
  3928  
  3929              if (d[w] == d[_v] + edgeWeight) {
  3930                g[w] = g[w] + g[_v];
  3931                P[w].push(_v);
  3932              }
  3933            }
  3934          } else {
  3935            for (var _j = 0; _j < A[_v].length; _j++) {
  3936              var _w = A[_v][_j].id();
  3937  
  3938              if (d[_w] == Infinity) {
  3939                Q.push(_w);
  3940                d[_w] = d[_v] + 1;
  3941              }
  3942  
  3943              if (d[_w] == d[_v] + 1) {
  3944                g[_w] = g[_w] + g[_v];
  3945  
  3946                P[_w].push(_v);
  3947              }
  3948            }
  3949          }
  3950        }
  3951  
  3952        var e = {};
  3953  
  3954        for (var _i2 = 0; _i2 < V.length; _i2++) {
  3955          e[V[_i2].id()] = 0;
  3956        }
  3957  
  3958        while (S.length > 0) {
  3959          var _w2 = S.pop();
  3960  
  3961          for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
  3962            var _v2 = P[_w2][_j2];
  3963            e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
  3964  
  3965            if (_w2 != V[s].id()) {
  3966              C.set(_w2, C.get(_w2) + e[_w2]);
  3967            }
  3968          }
  3969        }
  3970      };
  3971  
  3972      for (var s = 0; s < V.length; s++) {
  3973        _loop(s);
  3974      }
  3975  
  3976      var ret = {
  3977        betweenness: function betweenness(node) {
  3978          var id = cy.collection(node).id();
  3979          return C.get(id);
  3980        },
  3981        betweennessNormalized: function betweennessNormalized(node) {
  3982          if (max == 0) {
  3983            return 0;
  3984          }
  3985  
  3986          var id = cy.collection(node).id();
  3987          return C.get(id) / max;
  3988        }
  3989      }; // alias
  3990  
  3991      ret.betweennessNormalised = ret.betweennessNormalized;
  3992      return ret;
  3993    } // betweennessCentrality
  3994  
  3995  }; // elesfn
  3996  // nice, short mathemathical alias
  3997  
  3998  elesfn$a.bc = elesfn$a.betweennessCentrality;
  3999  
  4000  // Implemented by Zoe Xi @zoexi for GSOC 2016
  4001  /* eslint-disable no-unused-vars */
  4002  
  4003  var defaults$4 = defaults({
  4004    expandFactor: 2,
  4005    // affects time of computation and cluster granularity to some extent: M * M
  4006    inflateFactor: 2,
  4007    // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
  4008    multFactor: 1,
  4009    // optional self loops for each node. Use a neutral value to improve cluster computations.
  4010    maxIterations: 20,
  4011    // maximum number of iterations of the MCL algorithm in a single run
  4012    attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
  4013    function (edge) {
  4014      return 1;
  4015    }]
  4016  });
  4017  /* eslint-enable */
  4018  
  4019  var setOptions = function setOptions(options) {
  4020    return defaults$4(options);
  4021  };
  4022  /* eslint-enable */
  4023  
  4024  
  4025  var getSimilarity = function getSimilarity(edge, attributes) {
  4026    var total = 0;
  4027  
  4028    for (var i = 0; i < attributes.length; i++) {
  4029      total += attributes[i](edge);
  4030    }
  4031  
  4032    return total;
  4033  };
  4034  
  4035  var addLoops = function addLoops(M, n, val) {
  4036    for (var i = 0; i < n; i++) {
  4037      M[i * n + i] = val;
  4038    }
  4039  };
  4040  
  4041  var normalize = function normalize(M, n) {
  4042    var sum;
  4043  
  4044    for (var col = 0; col < n; col++) {
  4045      sum = 0;
  4046  
  4047      for (var row = 0; row < n; row++) {
  4048        sum += M[row * n + col];
  4049      }
  4050  
  4051      for (var _row = 0; _row < n; _row++) {
  4052        M[_row * n + col] = M[_row * n + col] / sum;
  4053      }
  4054    }
  4055  }; // TODO: blocked matrix multiplication?
  4056  
  4057  
  4058  var mmult = function mmult(A, B, n) {
  4059    var C = new Array(n * n);
  4060  
  4061    for (var i = 0; i < n; i++) {
  4062      for (var j = 0; j < n; j++) {
  4063        C[i * n + j] = 0;
  4064      }
  4065  
  4066      for (var k = 0; k < n; k++) {
  4067        for (var _j = 0; _j < n; _j++) {
  4068          C[i * n + _j] += A[i * n + k] * B[k * n + _j];
  4069        }
  4070      }
  4071    }
  4072  
  4073    return C;
  4074  };
  4075  
  4076  var expand = function expand(M, n, expandFactor
  4077  /** power **/
  4078  ) {
  4079    var _M = M.slice(0);
  4080  
  4081    for (var p = 1; p < expandFactor; p++) {
  4082      M = mmult(M, _M, n);
  4083    }
  4084  
  4085    return M;
  4086  };
  4087  
  4088  var inflate = function inflate(M, n, inflateFactor
  4089  /** r **/
  4090  ) {
  4091    var _M = new Array(n * n); // M(i,j) ^ inflatePower
  4092  
  4093  
  4094    for (var i = 0; i < n * n; i++) {
  4095      _M[i] = Math.pow(M[i], inflateFactor);
  4096    }
  4097  
  4098    normalize(_M, n);
  4099    return _M;
  4100  };
  4101  
  4102  var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
  4103    // Check that both matrices have the same elements (i,j)
  4104    for (var i = 0; i < n2; i++) {
  4105      var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
  4106  
  4107      var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
  4108  
  4109      if (v1 !== v2) {
  4110        return false;
  4111      }
  4112    }
  4113  
  4114    return true;
  4115  };
  4116  
  4117  var assign = function assign(M, n, nodes, cy) {
  4118    var clusters = [];
  4119  
  4120    for (var i = 0; i < n; i++) {
  4121      var cluster = [];
  4122  
  4123      for (var j = 0; j < n; j++) {
  4124        // Row-wise attractors and elements that they attract belong in same cluster
  4125        if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
  4126          cluster.push(nodes[j]);
  4127        }
  4128      }
  4129  
  4130      if (cluster.length !== 0) {
  4131        clusters.push(cy.collection(cluster));
  4132      }
  4133    }
  4134  
  4135    return clusters;
  4136  };
  4137  
  4138  var isDuplicate = function isDuplicate(c1, c2) {
  4139    for (var i = 0; i < c1.length; i++) {
  4140      if (!c2[i] || c1[i].id() !== c2[i].id()) {
  4141        return false;
  4142      }
  4143    }
  4144  
  4145    return true;
  4146  };
  4147  
  4148  var removeDuplicates = function removeDuplicates(clusters) {
  4149    for (var i = 0; i < clusters.length; i++) {
  4150      for (var j = 0; j < clusters.length; j++) {
  4151        if (i != j && isDuplicate(clusters[i], clusters[j])) {
  4152          clusters.splice(j, 1);
  4153        }
  4154      }
  4155    }
  4156  
  4157    return clusters;
  4158  };
  4159  
  4160  var markovClustering = function markovClustering(options) {
  4161    var nodes = this.nodes();
  4162    var edges = this.edges();
  4163    var cy = this.cy(); // Set parameters of algorithm:
  4164  
  4165    var opts = setOptions(options); // Map each node to its position in node array
  4166  
  4167    var id2position = {};
  4168  
  4169    for (var i = 0; i < nodes.length; i++) {
  4170      id2position[nodes[i].id()] = i;
  4171    } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
  4172  
  4173  
  4174    var n = nodes.length,
  4175        n2 = n * n;
  4176  
  4177    var M = new Array(n2),
  4178        _M;
  4179  
  4180    for (var _i = 0; _i < n2; _i++) {
  4181      M[_i] = 0;
  4182    }
  4183  
  4184    for (var e = 0; e < edges.length; e++) {
  4185      var edge = edges[e];
  4186      var _i2 = id2position[edge.source().id()];
  4187      var j = id2position[edge.target().id()];
  4188      var sim = getSimilarity(edge, opts.attributes);
  4189      M[_i2 * n + j] += sim; // G should be symmetric and undirected
  4190  
  4191      M[j * n + _i2] += sim;
  4192    } // Begin Markov cluster algorithm
  4193    // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
  4194  
  4195  
  4196    addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
  4197  
  4198    normalize(M, n);
  4199    var isStillMoving = true;
  4200    var iterations = 0;
  4201  
  4202    while (isStillMoving && iterations < opts.maxIterations) {
  4203      isStillMoving = false; // Step 3:
  4204  
  4205      _M = expand(M, n, opts.expandFactor); // Step 4:
  4206  
  4207      M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
  4208  
  4209      if (!hasConverged(M, _M, n2, 4)) {
  4210        isStillMoving = true;
  4211      }
  4212  
  4213      iterations++;
  4214    } // Build clusters from matrix
  4215  
  4216  
  4217    var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
  4218  
  4219    clusters = removeDuplicates(clusters);
  4220    return clusters;
  4221  };
  4222  
  4223  var markovClustering$1 = {
  4224    markovClustering: markovClustering,
  4225    mcl: markovClustering
  4226  };
  4227  
  4228  // Common distance metrics for clustering algorithms
  4229  
  4230  var identity = function identity(x) {
  4231    return x;
  4232  };
  4233  
  4234  var absDiff = function absDiff(p, q) {
  4235    return Math.abs(q - p);
  4236  };
  4237  
  4238  var addAbsDiff = function addAbsDiff(total, p, q) {
  4239    return total + absDiff(p, q);
  4240  };
  4241  
  4242  var addSquaredDiff = function addSquaredDiff(total, p, q) {
  4243    return total + Math.pow(q - p, 2);
  4244  };
  4245  
  4246  var sqrt = function sqrt(x) {
  4247    return Math.sqrt(x);
  4248  };
  4249  
  4250  var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
  4251    return Math.max(currentMax, absDiff(p, q));
  4252  };
  4253  
  4254  var getDistance = function getDistance(length, getP, getQ, init, visit) {
  4255    var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
  4256    var ret = init;
  4257    var p, q;
  4258  
  4259    for (var dim = 0; dim < length; dim++) {
  4260      p = getP(dim);
  4261      q = getQ(dim);
  4262      ret = visit(ret, p, q);
  4263    }
  4264  
  4265    return post(ret);
  4266  };
  4267  
  4268  var distances = {
  4269    euclidean: function euclidean(length, getP, getQ) {
  4270      if (length >= 2) {
  4271        return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
  4272      } else {
  4273        // for single attr case, more efficient to avoid sqrt
  4274        return getDistance(length, getP, getQ, 0, addAbsDiff);
  4275      }
  4276    },
  4277    squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
  4278      return getDistance(length, getP, getQ, 0, addSquaredDiff);
  4279    },
  4280    manhattan: function manhattan(length, getP, getQ) {
  4281      return getDistance(length, getP, getQ, 0, addAbsDiff);
  4282    },
  4283    max: function max(length, getP, getQ) {
  4284      return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
  4285    }
  4286  }; // in case the user accidentally doesn't use camel case
  4287  
  4288  distances['squared-euclidean'] = distances['squaredEuclidean'];
  4289  distances['squaredeuclidean'] = distances['squaredEuclidean'];
  4290  function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
  4291    var impl;
  4292  
  4293    if (fn(method)) {
  4294      impl = method;
  4295    } else {
  4296      impl = distances[method] || distances.euclidean;
  4297    }
  4298  
  4299    if (length === 0 && fn(method)) {
  4300      return impl(nodeP, nodeQ);
  4301    } else {
  4302      return impl(length, getP, getQ, nodeP, nodeQ);
  4303    }
  4304  }
  4305  
  4306  var defaults$5 = defaults({
  4307    k: 2,
  4308    m: 2,
  4309    sensitivityThreshold: 0.0001,
  4310    distance: 'euclidean',
  4311    maxIterations: 10,
  4312    attributes: [],
  4313    testMode: false,
  4314    testCentroids: null
  4315  });
  4316  
  4317  var setOptions$1 = function setOptions(options) {
  4318    return defaults$5(options);
  4319  };
  4320  /* eslint-enable */
  4321  
  4322  
  4323  var getDist = function getDist(type, node, centroid, attributes, mode) {
  4324    var noNodeP = mode !== 'kMedoids';
  4325    var getP = noNodeP ? function (i) {
  4326      return centroid[i];
  4327    } : function (i) {
  4328      return attributes[i](centroid);
  4329    };
  4330  
  4331    var getQ = function getQ(i) {
  4332      return attributes[i](node);
  4333    };
  4334  
  4335    var nodeP = centroid;
  4336    var nodeQ = node;
  4337    return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
  4338  };
  4339  
  4340  var randomCentroids = function randomCentroids(nodes, k, attributes) {
  4341    var ndim = attributes.length;
  4342    var min = new Array(ndim);
  4343    var max = new Array(ndim);
  4344    var centroids = new Array(k);
  4345    var centroid = null; // Find min, max values for each attribute dimension
  4346  
  4347    for (var i = 0; i < ndim; i++) {
  4348      min[i] = nodes.min(attributes[i]).value;
  4349      max[i] = nodes.max(attributes[i]).value;
  4350    } // Build k centroids, each represented as an n-dim feature vector
  4351  
  4352  
  4353    for (var c = 0; c < k; c++) {
  4354      centroid = [];
  4355  
  4356      for (var _i = 0; _i < ndim; _i++) {
  4357        centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
  4358      }
  4359  
  4360      centroids[c] = centroid;
  4361    }
  4362  
  4363    return centroids;
  4364  };
  4365  
  4366  var classify = function classify(node, centroids, distance, attributes, type) {
  4367    var min = Infinity;
  4368    var index = 0;
  4369  
  4370    for (var i = 0; i < centroids.length; i++) {
  4371      var dist = getDist(distance, node, centroids[i], attributes, type);
  4372  
  4373      if (dist < min) {
  4374        min = dist;
  4375        index = i;
  4376      }
  4377    }
  4378  
  4379    return index;
  4380  };
  4381  
  4382  var buildCluster = function buildCluster(centroid, nodes, assignment) {
  4383    var cluster = [];
  4384    var node = null;
  4385  
  4386    for (var n = 0; n < nodes.length; n++) {
  4387      node = nodes[n];
  4388  
  4389      if (assignment[node.id()] === centroid) {
  4390        //console.log("Node " + node.id() + " is associated with medoid #: " + m);
  4391        cluster.push(node);
  4392      }
  4393    }
  4394  
  4395    return cluster;
  4396  };
  4397  
  4398  var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
  4399    return Math.abs(v2 - v1) <= sensitivityThreshold;
  4400  };
  4401  
  4402  var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
  4403    for (var i = 0; i < v1.length; i++) {
  4404      for (var j = 0; j < v1[i].length; j++) {
  4405        var diff = Math.abs(v1[i][j] - v2[i][j]);
  4406  
  4407        if (diff > sensitivityThreshold) {
  4408          return false;
  4409        }
  4410      }
  4411    }
  4412  
  4413    return true;
  4414  };
  4415  
  4416  var seenBefore = function seenBefore(node, medoids, n) {
  4417    for (var i = 0; i < n; i++) {
  4418      if (node === medoids[i]) return true;
  4419    }
  4420  
  4421    return false;
  4422  };
  4423  
  4424  var randomMedoids = function randomMedoids(nodes, k) {
  4425    var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
  4426    // so we need to check to see if we've already seen or chose this node before.
  4427  
  4428    if (nodes.length < 50) {
  4429      // Randomly select k medoids from the n nodes
  4430      for (var i = 0; i < k; i++) {
  4431        var node = nodes[Math.floor(Math.random() * nodes.length)]; // If we've already chosen this node to be a medoid, don't choose it again (for small data sets).
  4432        // Instead choose a different random node.
  4433  
  4434        while (seenBefore(node, medoids, i)) {
  4435          node = nodes[Math.floor(Math.random() * nodes.length)];
  4436        }
  4437  
  4438        medoids[i] = node;
  4439      }
  4440    } else {
  4441      // Relatively large data set, so pretty safe to not check and just select random nodes
  4442      for (var _i2 = 0; _i2 < k; _i2++) {
  4443        medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
  4444      }
  4445    }
  4446  
  4447    return medoids;
  4448  };
  4449  
  4450  var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
  4451    var cost = 0;
  4452  
  4453    for (var n = 0; n < cluster.length; n++) {
  4454      cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
  4455    }
  4456  
  4457    return cost;
  4458  };
  4459  
  4460  var kMeans = function kMeans(options) {
  4461    var cy = this.cy();
  4462    var nodes = this.nodes();
  4463    var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
  4464  
  4465    var opts = setOptions$1(options); // Begin k-means algorithm
  4466  
  4467    var clusters = new Array(opts.k);
  4468    var assignment = {};
  4469    var centroids; // Step 1: Initialize centroid positions
  4470  
  4471    if (opts.testMode) {
  4472      if (typeof opts.testCentroids === 'number') {
  4473        centroids = randomCentroids(nodes, opts.k, opts.attributes);
  4474      } else if (_typeof(opts.testCentroids) === 'object') {
  4475        centroids = opts.testCentroids;
  4476      } else {
  4477        centroids = randomCentroids(nodes, opts.k, opts.attributes);
  4478      }
  4479    } else {
  4480      centroids = randomCentroids(nodes, opts.k, opts.attributes);
  4481    }
  4482  
  4483    var isStillMoving = true;
  4484    var iterations = 0;
  4485  
  4486    while (isStillMoving && iterations < opts.maxIterations) {
  4487      // Step 2: Assign nodes to the nearest centroid
  4488      for (var n = 0; n < nodes.length; n++) {
  4489        node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
  4490  
  4491        assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
  4492      } // Step 3: For each of the k clusters, update its centroid
  4493  
  4494  
  4495      isStillMoving = false;
  4496  
  4497      for (var c = 0; c < opts.k; c++) {
  4498        // Get all nodes that belong to this cluster
  4499        var cluster = buildCluster(c, nodes, assignment);
  4500  
  4501        if (cluster.length === 0) {
  4502          // If cluster is empty, break out early & move to next cluster
  4503          continue;
  4504        } // Update centroids by calculating avg of all nodes within the cluster.
  4505  
  4506  
  4507        var ndim = opts.attributes.length;
  4508        var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
  4509  
  4510        var newCentroid = new Array(ndim);
  4511        var sum = new Array(ndim);
  4512  
  4513        for (var d = 0; d < ndim; d++) {
  4514          sum[d] = 0.0;
  4515  
  4516          for (var i = 0; i < cluster.length; i++) {
  4517            node = cluster[i];
  4518            sum[d] += opts.attributes[d](node);
  4519          }
  4520  
  4521          newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
  4522  
  4523          if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
  4524            isStillMoving = true;
  4525          }
  4526        }
  4527  
  4528        centroids[c] = newCentroid;
  4529        clusters[c] = cy.collection(cluster);
  4530      }
  4531  
  4532      iterations++;
  4533    }
  4534  
  4535    return clusters;
  4536  };
  4537  
  4538  var kMedoids = function kMedoids(options) {
  4539    var cy = this.cy();
  4540    var nodes = this.nodes();
  4541    var node = null;
  4542    var opts = setOptions$1(options); // Begin k-medoids algorithm
  4543  
  4544    var clusters = new Array(opts.k);
  4545    var medoids;
  4546    var assignment = {};
  4547    var curCost;
  4548    var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
  4549    // Step 1: Initialize k medoids
  4550  
  4551    if (opts.testMode) {
  4552      if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
  4553        medoids = opts.testCentroids;
  4554      } else {
  4555        medoids = randomMedoids(nodes, opts.k);
  4556      }
  4557    } else {
  4558      medoids = randomMedoids(nodes, opts.k);
  4559    }
  4560  
  4561    var isStillMoving = true;
  4562    var iterations = 0;
  4563  
  4564    while (isStillMoving && iterations < opts.maxIterations) {
  4565      // Step 2: Assign nodes to the nearest medoid
  4566      for (var n = 0; n < nodes.length; n++) {
  4567        node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
  4568  
  4569        assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
  4570      }
  4571  
  4572      isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
  4573      // select the node with the lowest configuration cost as new medoid.
  4574  
  4575      for (var m = 0; m < medoids.length; m++) {
  4576        // Get all nodes that belong to this medoid
  4577        var cluster = buildCluster(m, nodes, assignment);
  4578  
  4579        if (cluster.length === 0) {
  4580          // If cluster is empty, break out early & move to next cluster
  4581          continue;
  4582        }
  4583  
  4584        minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
  4585        // Select different medoid if its configuration has the lowest cost
  4586  
  4587        for (var _n = 0; _n < cluster.length; _n++) {
  4588          curCost = findCost(cluster[_n], cluster, opts.attributes);
  4589  
  4590          if (curCost < minCosts[m]) {
  4591            minCosts[m] = curCost;
  4592            medoids[m] = cluster[_n];
  4593            isStillMoving = true;
  4594          }
  4595        }
  4596  
  4597        clusters[m] = cy.collection(cluster);
  4598      }
  4599  
  4600      iterations++;
  4601    }
  4602  
  4603    return clusters;
  4604  };
  4605  
  4606  var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
  4607    var numerator, denominator;
  4608  
  4609    for (var n = 0; n < nodes.length; n++) {
  4610      for (var c = 0; c < centroids.length; c++) {
  4611        weight[n][c] = Math.pow(U[n][c], opts.m);
  4612      }
  4613    }
  4614  
  4615    for (var _c = 0; _c < centroids.length; _c++) {
  4616      for (var dim = 0; dim < opts.attributes.length; dim++) {
  4617        numerator = 0;
  4618        denominator = 0;
  4619  
  4620        for (var _n2 = 0; _n2 < nodes.length; _n2++) {
  4621          numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
  4622          denominator += weight[_n2][_c];
  4623        }
  4624  
  4625        centroids[_c][dim] = numerator / denominator;
  4626      }
  4627    }
  4628  };
  4629  
  4630  var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
  4631    // Save previous step
  4632    for (var i = 0; i < U.length; i++) {
  4633      _U[i] = U[i].slice();
  4634    }
  4635  
  4636    var sum, numerator, denominator;
  4637    var pow = 2 / (opts.m - 1);
  4638  
  4639    for (var c = 0; c < centroids.length; c++) {
  4640      for (var n = 0; n < nodes.length; n++) {
  4641        sum = 0;
  4642  
  4643        for (var k = 0; k < centroids.length; k++) {
  4644          // against all other centroids
  4645          numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
  4646          denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
  4647          sum += Math.pow(numerator / denominator, pow);
  4648        }
  4649  
  4650        U[n][c] = 1 / sum;
  4651      }
  4652    }
  4653  };
  4654  
  4655  var assign$1 = function assign(nodes, U, opts, cy) {
  4656    var clusters = new Array(opts.k);
  4657  
  4658    for (var c = 0; c < clusters.length; c++) {
  4659      clusters[c] = [];
  4660    }
  4661  
  4662    var max;
  4663    var index;
  4664  
  4665    for (var n = 0; n < U.length; n++) {
  4666      // for each node (U is N x C matrix)
  4667      max = -Infinity;
  4668      index = -1; // Determine which cluster the node is most likely to belong in
  4669  
  4670      for (var _c2 = 0; _c2 < U[0].length; _c2++) {
  4671        if (U[n][_c2] > max) {
  4672          max = U[n][_c2];
  4673          index = _c2;
  4674        }
  4675      }
  4676  
  4677      clusters[index].push(nodes[n]);
  4678    } // Turn every array into a collection of nodes
  4679  
  4680  
  4681    for (var _c3 = 0; _c3 < clusters.length; _c3++) {
  4682      clusters[_c3] = cy.collection(clusters[_c3]);
  4683    }
  4684  
  4685    return clusters;
  4686  };
  4687  
  4688  var fuzzyCMeans = function fuzzyCMeans(options) {
  4689    var cy = this.cy();
  4690    var nodes = this.nodes();
  4691    var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
  4692  
  4693    var clusters;
  4694    var centroids;
  4695    var U;
  4696  
  4697    var _U;
  4698  
  4699    var weight; // Step 1: Initialize letiables.
  4700  
  4701    _U = new Array(nodes.length);
  4702  
  4703    for (var i = 0; i < nodes.length; i++) {
  4704      // N x C matrix
  4705      _U[i] = new Array(opts.k);
  4706    }
  4707  
  4708    U = new Array(nodes.length);
  4709  
  4710    for (var _i3 = 0; _i3 < nodes.length; _i3++) {
  4711      // N x C matrix
  4712      U[_i3] = new Array(opts.k);
  4713    }
  4714  
  4715    for (var _i4 = 0; _i4 < nodes.length; _i4++) {
  4716      var total = 0;
  4717  
  4718      for (var j = 0; j < opts.k; j++) {
  4719        U[_i4][j] = Math.random();
  4720        total += U[_i4][j];
  4721      }
  4722  
  4723      for (var _j = 0; _j < opts.k; _j++) {
  4724        U[_i4][_j] = U[_i4][_j] / total;
  4725      }
  4726    }
  4727  
  4728    centroids = new Array(opts.k);
  4729  
  4730    for (var _i5 = 0; _i5 < opts.k; _i5++) {
  4731      centroids[_i5] = new Array(opts.attributes.length);
  4732    }
  4733  
  4734    weight = new Array(nodes.length);
  4735  
  4736    for (var _i6 = 0; _i6 < nodes.length; _i6++) {
  4737      // N x C matrix
  4738      weight[_i6] = new Array(opts.k);
  4739    } // end init FCM
  4740  
  4741  
  4742    var isStillMoving = true;
  4743    var iterations = 0;
  4744  
  4745    while (isStillMoving && iterations < opts.maxIterations) {
  4746      isStillMoving = false; // Step 2: Calculate the centroids for each step.
  4747  
  4748      updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
  4749  
  4750      updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
  4751  
  4752      if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
  4753        isStillMoving = true;
  4754      }
  4755  
  4756      iterations++;
  4757    } // Assign nodes to clusters with highest probability.
  4758  
  4759  
  4760    clusters = assign$1(nodes, U, opts, cy);
  4761    return {
  4762      clusters: clusters,
  4763      degreeOfMembership: U
  4764    };
  4765  };
  4766  
  4767  var kClustering = {
  4768    kMeans: kMeans,
  4769    kMedoids: kMedoids,
  4770    fuzzyCMeans: fuzzyCMeans,
  4771    fcm: fuzzyCMeans
  4772  };
  4773  
  4774  // Implemented by Zoe Xi @zoexi for GSOC 2016
  4775  var defaults$6 = defaults({
  4776    distance: 'euclidean',
  4777    // distance metric to compare nodes
  4778    linkage: 'min',
  4779    // linkage criterion : how to determine the distance between clusters of nodes
  4780    mode: 'threshold',
  4781    // mode:'threshold' => clusters must be threshold distance apart
  4782    threshold: Infinity,
  4783    // the distance threshold
  4784    // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
  4785    addDendrogram: false,
  4786    // whether to add the dendrogram to the graph for viz
  4787    dendrogramDepth: 0,
  4788    // depth at which dendrogram branches are merged into the returned clusters
  4789    attributes: [] // array of attr functions
  4790  
  4791  });
  4792  var linkageAliases = {
  4793    'single': 'min',
  4794    'complete': 'max'
  4795  };
  4796  
  4797  var setOptions$2 = function setOptions(options) {
  4798    var opts = defaults$6(options);
  4799    var preferredAlias = linkageAliases[opts.linkage];
  4800  
  4801    if (preferredAlias != null) {
  4802      opts.linkage = preferredAlias;
  4803    }
  4804  
  4805    return opts;
  4806  };
  4807  
  4808  var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
  4809    // Find two closest clusters from cached mins
  4810    var minKey = 0;
  4811    var min = Infinity;
  4812    var dist;
  4813    var attrs = opts.attributes;
  4814  
  4815    var getDist = function getDist(n1, n2) {
  4816      return clusteringDistance(opts.distance, attrs.length, function (i) {
  4817        return attrs[i](n1);
  4818      }, function (i) {
  4819        return attrs[i](n2);
  4820      }, n1, n2);
  4821    };
  4822  
  4823    for (var i = 0; i < clusters.length; i++) {
  4824      var key = clusters[i].key;
  4825      var _dist = dists[key][mins[key]];
  4826  
  4827      if (_dist < min) {
  4828        minKey = key;
  4829        min = _dist;
  4830      }
  4831    }
  4832  
  4833    if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
  4834      return false;
  4835    }
  4836  
  4837    var c1 = index[minKey];
  4838    var c2 = index[mins[minKey]];
  4839    var merged; // Merge two closest clusters
  4840  
  4841    if (opts.mode === 'dendrogram') {
  4842      merged = {
  4843        left: c1,
  4844        right: c2,
  4845        key: c1.key
  4846      };
  4847    } else {
  4848      merged = {
  4849        value: c1.value.concat(c2.value),
  4850        key: c1.key
  4851      };
  4852    }
  4853  
  4854    clusters[c1.index] = merged;
  4855    clusters.splice(c2.index, 1);
  4856    index[c1.key] = merged; // Update distances with new merged cluster
  4857  
  4858    for (var _i = 0; _i < clusters.length; _i++) {
  4859      var cur = clusters[_i];
  4860  
  4861      if (c1.key === cur.key) {
  4862        dist = Infinity;
  4863      } else if (opts.linkage === 'min') {
  4864        dist = dists[c1.key][cur.key];
  4865  
  4866        if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
  4867          dist = dists[c2.key][cur.key];
  4868        }
  4869      } else if (opts.linkage === 'max') {
  4870        dist = dists[c1.key][cur.key];
  4871  
  4872        if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
  4873          dist = dists[c2.key][cur.key];
  4874        }
  4875      } else if (opts.linkage === 'mean') {
  4876        dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
  4877      } else {
  4878        if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
  4879      }
  4880  
  4881      dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
  4882    } // Update cached mins
  4883  
  4884  
  4885    for (var _i2 = 0; _i2 < clusters.length; _i2++) {
  4886      var key1 = clusters[_i2].key;
  4887  
  4888      if (mins[key1] === c1.key || mins[key1] === c2.key) {
  4889        var _min = key1;
  4890  
  4891        for (var j = 0; j < clusters.length; j++) {
  4892          var key2 = clusters[j].key;
  4893  
  4894          if (dists[key1][key2] < dists[key1][_min]) {
  4895            _min = key2;
  4896          }
  4897        }
  4898  
  4899        mins[key1] = _min;
  4900      }
  4901  
  4902      clusters[_i2].index = _i2;
  4903    } // Clean up meta data used for clustering
  4904  
  4905  
  4906    c1.key = c2.key = c1.index = c2.index = null;
  4907    return true;
  4908  };
  4909  
  4910  var getAllChildren = function getAllChildren(root, arr, cy) {
  4911    if (!root) return;
  4912  
  4913    if (root.value) {
  4914      arr.push(root.value);
  4915    } else {
  4916      if (root.left) getAllChildren(root.left, arr);
  4917      if (root.right) getAllChildren(root.right, arr);
  4918    }
  4919  };
  4920  
  4921  var buildDendrogram = function buildDendrogram(root, cy) {
  4922    if (!root) return '';
  4923  
  4924    if (root.left && root.right) {
  4925      var leftStr = buildDendrogram(root.left, cy);
  4926      var rightStr = buildDendrogram(root.right, cy);
  4927      var node = cy.add({
  4928        group: 'nodes',
  4929        data: {
  4930          id: leftStr + ',' + rightStr
  4931        }
  4932      });
  4933      cy.add({
  4934        group: 'edges',
  4935        data: {
  4936          source: leftStr,
  4937          target: node.id()
  4938        }
  4939      });
  4940      cy.add({
  4941        group: 'edges',
  4942        data: {
  4943          source: rightStr,
  4944          target: node.id()
  4945        }
  4946      });
  4947      return node.id();
  4948    } else if (root.value) {
  4949      return root.value.id();
  4950    }
  4951  };
  4952  
  4953  var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
  4954    if (!root) return [];
  4955    var left = [],
  4956        right = [],
  4957        leaves = [];
  4958  
  4959    if (k === 0) {
  4960      // don't cut tree, simply return all nodes as 1 single cluster
  4961      if (root.left) getAllChildren(root.left, left);
  4962      if (root.right) getAllChildren(root.right, right);
  4963      leaves = left.concat(right);
  4964      return [cy.collection(leaves)];
  4965    } else if (k === 1) {
  4966      // cut at root
  4967      if (root.value) {
  4968        // leaf node
  4969        return [cy.collection(root.value)];
  4970      } else {
  4971        if (root.left) getAllChildren(root.left, left);
  4972        if (root.right) getAllChildren(root.right, right);
  4973        return [cy.collection(left), cy.collection(right)];
  4974      }
  4975    } else {
  4976      if (root.value) {
  4977        return [cy.collection(root.value)];
  4978      } else {
  4979        if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
  4980        if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
  4981        return left.concat(right);
  4982      }
  4983    }
  4984  };
  4985  /* eslint-enable */
  4986  
  4987  
  4988  var hierarchicalClustering = function hierarchicalClustering(options) {
  4989    var cy = this.cy();
  4990    var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
  4991  
  4992    var opts = setOptions$2(options);
  4993    var attrs = opts.attributes;
  4994  
  4995    var getDist = function getDist(n1, n2) {
  4996      return clusteringDistance(opts.distance, attrs.length, function (i) {
  4997        return attrs[i](n1);
  4998      }, function (i) {
  4999        return attrs[i](n2);
  5000      }, n1, n2);
  5001    }; // Begin hierarchical algorithm
  5002  
  5003  
  5004    var clusters = [];
  5005    var dists = []; // distances between each pair of clusters
  5006  
  5007    var mins = []; // closest cluster for each cluster
  5008  
  5009    var index = []; // hash of all clusters by key
  5010    // In agglomerative (bottom-up) clustering, each node starts as its own cluster
  5011  
  5012    for (var n = 0; n < nodes.length; n++) {
  5013      var cluster = {
  5014        value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
  5015        key: n,
  5016        index: n
  5017      };
  5018      clusters[n] = cluster;
  5019      index[n] = cluster;
  5020      dists[n] = [];
  5021      mins[n] = 0;
  5022    } // Calculate the distance between each pair of clusters
  5023  
  5024  
  5025    for (var i = 0; i < clusters.length; i++) {
  5026      for (var j = 0; j <= i; j++) {
  5027        var dist = void 0;
  5028  
  5029        if (opts.mode === 'dendrogram') {
  5030          // modes store cluster values differently
  5031          dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
  5032        } else {
  5033          dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
  5034        }
  5035  
  5036        dists[i][j] = dist;
  5037        dists[j][i] = dist;
  5038  
  5039        if (dist < dists[i][mins[i]]) {
  5040          mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
  5041        }
  5042      }
  5043    } // Find the closest pair of clusters and merge them into a single cluster.
  5044    // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
  5045  
  5046  
  5047    var merged = mergeClosest(clusters, index, dists, mins, opts);
  5048  
  5049    while (merged) {
  5050      merged = mergeClosest(clusters, index, dists, mins, opts);
  5051    }
  5052  
  5053    var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
  5054    // in addition to returning the clusters.
  5055  
  5056    if (opts.mode === 'dendrogram') {
  5057      retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
  5058      if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
  5059    } else {
  5060      // Regular mode simply returns the clusters
  5061      retClusters = new Array(clusters.length);
  5062      clusters.forEach(function (cluster, i) {
  5063        // Clean up meta data used for clustering
  5064        cluster.key = cluster.index = null;
  5065        retClusters[i] = cy.collection(cluster.value);
  5066      });
  5067    }
  5068  
  5069    return retClusters;
  5070  };
  5071  
  5072  var hierarchicalClustering$1 = {
  5073    hierarchicalClustering: hierarchicalClustering,
  5074    hca: hierarchicalClustering
  5075  };
  5076  
  5077  // Implemented by Zoe Xi @zoexi for GSOC 2016
  5078  var defaults$7 = defaults({
  5079    distance: 'euclidean',
  5080    // distance metric to compare attributes between two nodes
  5081    preference: 'median',
  5082    // suitability of a data point to serve as an exemplar
  5083    damping: 0.8,
  5084    // damping factor between [0.5, 1)
  5085    maxIterations: 1000,
  5086    // max number of iterations to run
  5087    minIterations: 100,
  5088    // min number of iterations to run in order for clustering to stop
  5089    attributes: [// functions to quantify the similarity between any two points
  5090      // e.g. node => node.data('weight')
  5091    ]
  5092  });
  5093  
  5094  var setOptions$3 = function setOptions(options) {
  5095    var dmp = options.damping;
  5096    var pref = options.preference;
  5097  
  5098    if (!(0.5 <= dmp && dmp < 1)) {
  5099      error("Damping must range on [0.5, 1).  Got: ".concat(dmp));
  5100    }
  5101  
  5102    var validPrefs = ['median', 'mean', 'min', 'max'];
  5103  
  5104    if (!(validPrefs.some(function (v) {
  5105      return v === pref;
  5106    }) || number(pref))) {
  5107      error("Preference must be one of [".concat(validPrefs.map(function (p) {
  5108        return "'".concat(p, "'");
  5109      }).join(', '), "] or a number.  Got: ").concat(pref));
  5110    }
  5111  
  5112    return defaults$7(options);
  5113  };
  5114  /* eslint-enable */
  5115  
  5116  
  5117  var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
  5118    var attr = function attr(n, i) {
  5119      return attributes[i](n);
  5120    }; // nb negative because similarity should have an inverse relationship to distance
  5121  
  5122  
  5123    return -clusteringDistance(type, attributes.length, function (i) {
  5124      return attr(n1, i);
  5125    }, function (i) {
  5126      return attr(n2, i);
  5127    }, n1, n2);
  5128  };
  5129  
  5130  var getPreference = function getPreference(S, preference) {
  5131    // larger preference = greater # of clusters
  5132    var p = null;
  5133  
  5134    if (preference === 'median') {
  5135      p = median(S);
  5136    } else if (preference === 'mean') {
  5137      p = mean(S);
  5138    } else if (preference === 'min') {
  5139      p = min(S);
  5140    } else if (preference === 'max') {
  5141      p = max(S);
  5142    } else {
  5143      // Custom preference number, as set by user
  5144      p = preference;
  5145    }
  5146  
  5147    return p;
  5148  };
  5149  
  5150  var findExemplars = function findExemplars(n, R, A) {
  5151    var indices = [];
  5152  
  5153    for (var i = 0; i < n; i++) {
  5154      if (R[i * n + i] + A[i * n + i] > 0) {
  5155        indices.push(i);
  5156      }
  5157    }
  5158  
  5159    return indices;
  5160  };
  5161  
  5162  var assignClusters = function assignClusters(n, S, exemplars) {
  5163    var clusters = [];
  5164  
  5165    for (var i = 0; i < n; i++) {
  5166      var index = -1;
  5167      var max = -Infinity;
  5168  
  5169      for (var ei = 0; ei < exemplars.length; ei++) {
  5170        var e = exemplars[ei];
  5171  
  5172        if (S[i * n + e] > max) {
  5173          index = e;
  5174          max = S[i * n + e];
  5175        }
  5176      }
  5177  
  5178      if (index > 0) {
  5179        clusters.push(index);
  5180      }
  5181    }
  5182  
  5183    for (var _ei = 0; _ei < exemplars.length; _ei++) {
  5184      clusters[exemplars[_ei]] = exemplars[_ei];
  5185    }
  5186  
  5187    return clusters;
  5188  };
  5189  
  5190  var assign$2 = function assign(n, S, exemplars) {
  5191    var clusters = assignClusters(n, S, exemplars);
  5192  
  5193    for (var ei = 0; ei < exemplars.length; ei++) {
  5194      var ii = [];
  5195  
  5196      for (var c = 0; c < clusters.length; c++) {
  5197        if (clusters[c] === exemplars[ei]) {
  5198          ii.push(c);
  5199        }
  5200      }
  5201  
  5202      var maxI = -1;
  5203      var maxSum = -Infinity;
  5204  
  5205      for (var i = 0; i < ii.length; i++) {
  5206        var sum = 0;
  5207  
  5208        for (var j = 0; j < ii.length; j++) {
  5209          sum += S[ii[j] * n + ii[i]];
  5210        }
  5211  
  5212        if (sum > maxSum) {
  5213          maxI = i;
  5214          maxSum = sum;
  5215        }
  5216      }
  5217  
  5218      exemplars[ei] = ii[maxI];
  5219    }
  5220  
  5221    clusters = assignClusters(n, S, exemplars);
  5222    return clusters;
  5223  };
  5224  
  5225  var affinityPropagation = function affinityPropagation(options) {
  5226    var cy = this.cy();
  5227    var nodes = this.nodes();
  5228    var opts = setOptions$3(options); // Map each node to its position in node array
  5229  
  5230    var id2position = {};
  5231  
  5232    for (var i = 0; i < nodes.length; i++) {
  5233      id2position[nodes[i].id()] = i;
  5234    } // Begin affinity propagation algorithm
  5235  
  5236  
  5237    var n; // number of data points
  5238  
  5239    var n2; // size of matrices
  5240  
  5241    var S; // similarity matrix (1D array)
  5242  
  5243    var p; // preference/suitability of a data point to serve as an exemplar
  5244  
  5245    var R; // responsibility matrix (1D array)
  5246  
  5247    var A; // availability matrix (1D array)
  5248  
  5249    n = nodes.length;
  5250    n2 = n * n; // Initialize and build S similarity matrix
  5251  
  5252    S = new Array(n2);
  5253  
  5254    for (var _i = 0; _i < n2; _i++) {
  5255      S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
  5256    }
  5257  
  5258    for (var _i2 = 0; _i2 < n; _i2++) {
  5259      for (var j = 0; j < n; j++) {
  5260        if (_i2 !== j) {
  5261          S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
  5262        }
  5263      }
  5264    } // Place preferences on the diagonal of S
  5265  
  5266  
  5267    p = getPreference(S, opts.preference);
  5268  
  5269    for (var _i3 = 0; _i3 < n; _i3++) {
  5270      S[_i3 * n + _i3] = p;
  5271    } // Initialize R responsibility matrix
  5272  
  5273  
  5274    R = new Array(n2);
  5275  
  5276    for (var _i4 = 0; _i4 < n2; _i4++) {
  5277      R[_i4] = 0.0;
  5278    } // Initialize A availability matrix
  5279  
  5280  
  5281    A = new Array(n2);
  5282  
  5283    for (var _i5 = 0; _i5 < n2; _i5++) {
  5284      A[_i5] = 0.0;
  5285    }
  5286  
  5287    var old = new Array(n);
  5288    var Rp = new Array(n);
  5289    var se = new Array(n);
  5290  
  5291    for (var _i6 = 0; _i6 < n; _i6++) {
  5292      old[_i6] = 0.0;
  5293      Rp[_i6] = 0.0;
  5294      se[_i6] = 0;
  5295    }
  5296  
  5297    var e = new Array(n * opts.minIterations);
  5298  
  5299    for (var _i7 = 0; _i7 < e.length; _i7++) {
  5300      e[_i7] = 0;
  5301    }
  5302  
  5303    var iter;
  5304  
  5305    for (iter = 0; iter < opts.maxIterations; iter++) {
  5306      // main algorithmic loop
  5307      // Update R responsibility matrix
  5308      for (var _i8 = 0; _i8 < n; _i8++) {
  5309        var max = -Infinity,
  5310            max2 = -Infinity,
  5311            maxI = -1,
  5312            AS = 0.0;
  5313  
  5314        for (var _j = 0; _j < n; _j++) {
  5315          old[_j] = R[_i8 * n + _j];
  5316          AS = A[_i8 * n + _j] + S[_i8 * n + _j];
  5317  
  5318          if (AS >= max) {
  5319            max2 = max;
  5320            max = AS;
  5321            maxI = _j;
  5322          } else if (AS > max2) {
  5323            max2 = AS;
  5324          }
  5325        }
  5326  
  5327        for (var _j2 = 0; _j2 < n; _j2++) {
  5328          R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
  5329        }
  5330  
  5331        R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
  5332      } // Update A availability matrix
  5333  
  5334  
  5335      for (var _i9 = 0; _i9 < n; _i9++) {
  5336        var sum = 0;
  5337  
  5338        for (var _j3 = 0; _j3 < n; _j3++) {
  5339          old[_j3] = A[_j3 * n + _i9];
  5340          Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
  5341          sum += Rp[_j3];
  5342        }
  5343  
  5344        sum -= Rp[_i9];
  5345        Rp[_i9] = R[_i9 * n + _i9];
  5346        sum += Rp[_i9];
  5347  
  5348        for (var _j4 = 0; _j4 < n; _j4++) {
  5349          A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
  5350        }
  5351  
  5352        A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
  5353      } // Check for convergence
  5354  
  5355  
  5356      var K = 0;
  5357  
  5358      for (var _i10 = 0; _i10 < n; _i10++) {
  5359        var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
  5360        e[iter % opts.minIterations * n + _i10] = E;
  5361        K += E;
  5362      }
  5363  
  5364      if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
  5365        var _sum = 0;
  5366  
  5367        for (var _i11 = 0; _i11 < n; _i11++) {
  5368          se[_i11] = 0;
  5369  
  5370          for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
  5371            se[_i11] += e[_j5 * n + _i11];
  5372          }
  5373  
  5374          if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
  5375            _sum++;
  5376          }
  5377        }
  5378  
  5379        if (_sum === n) {
  5380          // then we have convergence
  5381          break;
  5382        }
  5383      }
  5384    } // Identify exemplars (cluster centers)
  5385  
  5386  
  5387    var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
  5388  
  5389    var clusterIndices = assign$2(n, S, exemplarsIndices);
  5390    var clusters = {};
  5391  
  5392    for (var c = 0; c < exemplarsIndices.length; c++) {
  5393      clusters[exemplarsIndices[c]] = [];
  5394    }
  5395  
  5396    for (var _i12 = 0; _i12 < nodes.length; _i12++) {
  5397      var pos = id2position[nodes[_i12].id()];
  5398  
  5399      var clusterIndex = clusterIndices[pos];
  5400  
  5401      if (clusterIndex != null) {
  5402        // the node may have not been assigned a cluster if no valid attributes were specified
  5403        clusters[clusterIndex].push(nodes[_i12]);
  5404      }
  5405    }
  5406  
  5407    var retClusters = new Array(exemplarsIndices.length);
  5408  
  5409    for (var _c = 0; _c < exemplarsIndices.length; _c++) {
  5410      retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
  5411    }
  5412  
  5413    return retClusters;
  5414  };
  5415  
  5416  var affinityPropagation$1 = {
  5417    affinityPropagation: affinityPropagation,
  5418    ap: affinityPropagation
  5419  };
  5420  
  5421  var hierholzerDefaults = defaults({
  5422    root: undefined,
  5423    directed: false
  5424  });
  5425  var elesfn$b = {
  5426    hierholzer: function hierholzer(options) {
  5427      if (!plainObject(options)) {
  5428        var args = arguments;
  5429        options = {
  5430          root: args[0],
  5431          directed: args[1]
  5432        };
  5433      }
  5434  
  5435      var _hierholzerDefaults = hierholzerDefaults(options),
  5436          root = _hierholzerDefaults.root,
  5437          directed = _hierholzerDefaults.directed;
  5438  
  5439      var eles = this;
  5440      var dflag = false;
  5441      var oddIn;
  5442      var oddOut;
  5443      var startVertex;
  5444      if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
  5445      var nodes = {};
  5446      var edges = {};
  5447  
  5448      if (directed) {
  5449        eles.forEach(function (ele) {
  5450          var id = ele.id();
  5451  
  5452          if (ele.isNode()) {
  5453            var ind = ele.indegree(true);
  5454            var outd = ele.outdegree(true);
  5455            var d1 = ind - outd;
  5456            var d2 = outd - ind;
  5457  
  5458            if (d1 == 1) {
  5459              if (oddIn) dflag = true;else oddIn = id;
  5460            } else if (d2 == 1) {
  5461              if (oddOut) dflag = true;else oddOut = id;
  5462            } else if (d2 > 1 || d1 > 1) {
  5463              dflag = true;
  5464            }
  5465  
  5466            nodes[id] = [];
  5467            ele.outgoers().forEach(function (e) {
  5468              if (e.isEdge()) nodes[id].push(e.id());
  5469            });
  5470          } else {
  5471            edges[id] = [undefined, ele.target().id()];
  5472          }
  5473        });
  5474      } else {
  5475        eles.forEach(function (ele) {
  5476          var id = ele.id();
  5477  
  5478          if (ele.isNode()) {
  5479            var d = ele.degree(true);
  5480  
  5481            if (d % 2) {
  5482              if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
  5483            }
  5484  
  5485            nodes[id] = [];
  5486            ele.connectedEdges().forEach(function (e) {
  5487              return nodes[id].push(e.id());
  5488            });
  5489          } else {
  5490            edges[id] = [ele.source().id(), ele.target().id()];
  5491          }
  5492        });
  5493      }
  5494  
  5495      var result = {
  5496        found: false,
  5497        trail: undefined
  5498      };
  5499      if (dflag) return result;else if (oddOut && oddIn) {
  5500        if (directed) {
  5501          if (startVertex && oddOut != startVertex) {
  5502            return result;
  5503          }
  5504  
  5505          startVertex = oddOut;
  5506        } else {
  5507          if (startVertex && oddOut != startVertex && oddIn != startVertex) {
  5508            return result;
  5509          } else if (!startVertex) {
  5510            startVertex = oddOut;
  5511          }
  5512        }
  5513      } else {
  5514        if (!startVertex) startVertex = eles[0].id();
  5515      }
  5516  
  5517      var walk = function walk(v) {
  5518        var currentNode = v;
  5519        var subtour = [v];
  5520        var adj, adjTail, adjHead;
  5521  
  5522        while (nodes[currentNode].length) {
  5523          adj = nodes[currentNode].shift();
  5524          adjTail = edges[adj][0];
  5525          adjHead = edges[adj][1];
  5526  
  5527          if (currentNode != adjHead) {
  5528            nodes[adjHead] = nodes[adjHead].filter(function (e) {
  5529              return e != adj;
  5530            });
  5531            currentNode = adjHead;
  5532          } else if (!directed && currentNode != adjTail) {
  5533            nodes[adjTail] = nodes[adjTail].filter(function (e) {
  5534              return e != adj;
  5535            });
  5536            currentNode = adjTail;
  5537          }
  5538  
  5539          subtour.unshift(adj);
  5540          subtour.unshift(currentNode);
  5541        }
  5542  
  5543        return subtour;
  5544      };
  5545  
  5546      var trail = [];
  5547      var subtour = [];
  5548      subtour = walk(startVertex);
  5549  
  5550      while (subtour.length != 1) {
  5551        if (nodes[subtour[0]].length == 0) {
  5552          trail.unshift(eles.getElementById(subtour.shift()));
  5553          trail.unshift(eles.getElementById(subtour.shift()));
  5554        } else {
  5555          subtour = walk(subtour.shift()).concat(subtour);
  5556        }
  5557      }
  5558  
  5559      trail.unshift(eles.getElementById(subtour.shift())); // final node
  5560  
  5561      for (var d in nodes) {
  5562        if (nodes[d].length) {
  5563          return result;
  5564        }
  5565      }
  5566  
  5567      result.found = true;
  5568      result.trail = this.spawn(trail);
  5569      return result;
  5570    }
  5571  };
  5572  
  5573  var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
  5574    var eles = this;
  5575    var nodes = {};
  5576    var id = 0;
  5577    var edgeCount = 0;
  5578    var components = [];
  5579    var stack = [];
  5580    var visitedEdges = {};
  5581  
  5582    var buildComponent = function buildComponent(x, y) {
  5583      var i = stack.length - 1;
  5584      var cutset = [];
  5585      var component = eles.spawn();
  5586  
  5587      while (stack[i].x != x || stack[i].y != y) {
  5588        cutset.push(stack.pop().edge);
  5589        i--;
  5590      }
  5591  
  5592      cutset.push(stack.pop().edge);
  5593      cutset.forEach(function (edge) {
  5594        var connectedNodes = edge.connectedNodes().intersection(eles);
  5595        component.merge(edge);
  5596        connectedNodes.forEach(function (node) {
  5597          var nodeId = node.id();
  5598          var connectedEdges = node.connectedEdges().intersection(eles);
  5599          component.merge(node);
  5600  
  5601          if (!nodes[nodeId].cutVertex) {
  5602            component.merge(connectedEdges);
  5603          } else {
  5604            component.merge(connectedEdges.filter(function (edge) {
  5605              return edge.isLoop();
  5606            }));
  5607          }
  5608        });
  5609      });
  5610      components.push(component);
  5611    };
  5612  
  5613    var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
  5614      if (root === parent) edgeCount += 1;
  5615      nodes[currentNode] = {
  5616        id: id,
  5617        low: id++,
  5618        cutVertex: false
  5619      };
  5620      var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
  5621  
  5622      if (edges.size() === 0) {
  5623        components.push(eles.spawn(eles.getElementById(currentNode)));
  5624      } else {
  5625        var sourceId, targetId, otherNodeId, edgeId;
  5626        edges.forEach(function (edge) {
  5627          sourceId = edge.source().id();
  5628          targetId = edge.target().id();
  5629          otherNodeId = sourceId === currentNode ? targetId : sourceId;
  5630  
  5631          if (otherNodeId !== parent) {
  5632            edgeId = edge.id();
  5633  
  5634            if (!visitedEdges[edgeId]) {
  5635              visitedEdges[edgeId] = true;
  5636              stack.push({
  5637                x: currentNode,
  5638                y: otherNodeId,
  5639                edge: edge
  5640              });
  5641            }
  5642  
  5643            if (!(otherNodeId in nodes)) {
  5644              biconnectedSearch(root, otherNodeId, currentNode);
  5645              nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
  5646  
  5647              if (nodes[currentNode].id <= nodes[otherNodeId].low) {
  5648                nodes[currentNode].cutVertex = true;
  5649                buildComponent(currentNode, otherNodeId);
  5650              }
  5651            } else {
  5652              nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
  5653            }
  5654          }
  5655        });
  5656      }
  5657    };
  5658  
  5659    eles.forEach(function (ele) {
  5660      if (ele.isNode()) {
  5661        var nodeId = ele.id();
  5662  
  5663        if (!(nodeId in nodes)) {
  5664          edgeCount = 0;
  5665          biconnectedSearch(nodeId, nodeId);
  5666          nodes[nodeId].cutVertex = edgeCount > 1;
  5667        }
  5668      }
  5669    });
  5670    var cutVertices = Object.keys(nodes).filter(function (id) {
  5671      return nodes[id].cutVertex;
  5672    }).map(function (id) {
  5673      return eles.getElementById(id);
  5674    });
  5675    return {
  5676      cut: eles.spawn(cutVertices),
  5677      components: components
  5678    };
  5679  };
  5680  
  5681  var hopcroftTarjanBiconnected$1 = {
  5682    hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
  5683    htbc: hopcroftTarjanBiconnected,
  5684    htb: hopcroftTarjanBiconnected,
  5685    hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
  5686  };
  5687  
  5688  var elesfn$c = {};
  5689  [elesfn, elesfn$1, elesfn$2, elesfn$3, elesfn$4, elesfn$5, elesfn$6, elesfn$7, elesfn$8, elesfn$9, elesfn$a, markovClustering$1, kClustering, hierarchicalClustering$1, affinityPropagation$1, elesfn$b, hopcroftTarjanBiconnected$1].forEach(function (props) {
  5690    extend(elesfn$c, props);
  5691  });
  5692  
  5693  /*!
  5694  Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
  5695  Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
  5696  Licensed under The MIT License (http://opensource.org/licenses/MIT)
  5697  */
  5698  
  5699  /*  promise states [Promises/A+ 2.1]  */
  5700  var STATE_PENDING = 0;
  5701  /*  [Promises/A+ 2.1.1]  */
  5702  
  5703  var STATE_FULFILLED = 1;
  5704  /*  [Promises/A+ 2.1.2]  */
  5705  
  5706  var STATE_REJECTED = 2;
  5707  /*  [Promises/A+ 2.1.3]  */
  5708  
  5709  /*  promise object constructor  */
  5710  
  5711  var api = function api(executor) {
  5712    /*  optionally support non-constructor/plain-function call  */
  5713    if (!(this instanceof api)) return new api(executor);
  5714    /*  initialize object  */
  5715  
  5716    this.id = 'Thenable/1.0.7';
  5717    this.state = STATE_PENDING;
  5718    /*  initial state  */
  5719  
  5720    this.fulfillValue = undefined;
  5721    /*  initial value  */
  5722  
  5723    /*  [Promises/A+ 1.3, 2.1.2.2]  */
  5724  
  5725    this.rejectReason = undefined;
  5726    /*  initial reason */
  5727  
  5728    /*  [Promises/A+ 1.5, 2.1.3.2]  */
  5729  
  5730    this.onFulfilled = [];
  5731    /*  initial handlers  */
  5732  
  5733    this.onRejected = [];
  5734    /*  initial handlers  */
  5735  
  5736    /*  provide optional information-hiding proxy  */
  5737  
  5738    this.proxy = {
  5739      then: this.then.bind(this)
  5740    };
  5741    /*  support optional executor function  */
  5742  
  5743    if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
  5744  };
  5745  /*  promise API methods  */
  5746  
  5747  
  5748  api.prototype = {
  5749    /*  promise resolving methods  */
  5750    fulfill: function fulfill(value) {
  5751      return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
  5752    },
  5753    reject: function reject(value) {
  5754      return deliver(this, STATE_REJECTED, 'rejectReason', value);
  5755    },
  5756  
  5757    /*  "The then Method" [Promises/A+ 1.1, 1.2, 2.2]  */
  5758    then: function then(onFulfilled, onRejected) {
  5759      var curr = this;
  5760      var next = new api();
  5761      /*  [Promises/A+ 2.2.7]  */
  5762  
  5763      curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
  5764      /*  [Promises/A+ 2.2.2/2.2.6]  */
  5765  
  5766      curr.onRejected.push(resolver(onRejected, next, 'reject'));
  5767      /*  [Promises/A+ 2.2.3/2.2.6]  */
  5768  
  5769      execute(curr);
  5770      return next.proxy;
  5771      /*  [Promises/A+ 2.2.7, 3.3]  */
  5772    }
  5773  };
  5774  /*  deliver an action  */
  5775  
  5776  var deliver = function deliver(curr, state, name, value) {
  5777    if (curr.state === STATE_PENDING) {
  5778      curr.state = state;
  5779      /*  [Promises/A+ 2.1.2.1, 2.1.3.1]  */
  5780  
  5781      curr[name] = value;
  5782      /*  [Promises/A+ 2.1.2.2, 2.1.3.2]  */
  5783  
  5784      execute(curr);
  5785    }
  5786  
  5787    return curr;
  5788  };
  5789  /*  execute all handlers  */
  5790  
  5791  
  5792  var execute = function execute(curr) {
  5793    if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
  5794  };
  5795  /*  execute particular set of handlers  */
  5796  
  5797  
  5798  var execute_handlers = function execute_handlers(curr, name, value) {
  5799    /* global setImmediate: true */
  5800  
  5801    /* global setTimeout: true */
  5802  
  5803    /*  short-circuit processing  */
  5804    if (curr[name].length === 0) return;
  5805    /*  iterate over all handlers, exactly once  */
  5806  
  5807    var handlers = curr[name];
  5808    curr[name] = [];
  5809    /*  [Promises/A+ 2.2.2.3, 2.2.3.3]  */
  5810  
  5811    var func = function func() {
  5812      for (var i = 0; i < handlers.length; i++) {
  5813        handlers[i](value);
  5814      }
  5815      /*  [Promises/A+ 2.2.5]  */
  5816  
  5817    };
  5818    /*  execute procedure asynchronously  */
  5819  
  5820    /*  [Promises/A+ 2.2.4, 3.1]  */
  5821  
  5822  
  5823    if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
  5824  };
  5825  /*  generate a resolver function  */
  5826  
  5827  
  5828  var resolver = function resolver(cb, next, method) {
  5829    return function (value) {
  5830      if (typeof cb !== 'function')
  5831        /*  [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4]  */
  5832        next[method].call(next, value);
  5833        /*  [Promises/A+ 2.2.7.3, 2.2.7.4]  */
  5834      else {
  5835          var result;
  5836  
  5837          try {
  5838            result = cb(value);
  5839          }
  5840          /*  [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2]  */
  5841          catch (e) {
  5842            next.reject(e);
  5843            /*  [Promises/A+ 2.2.7.2]  */
  5844  
  5845            return;
  5846          }
  5847  
  5848          resolve(next, result);
  5849          /*  [Promises/A+ 2.2.7.1]  */
  5850        }
  5851    };
  5852  };
  5853  /*  "Promise Resolution Procedure"  */
  5854  
  5855  /*  [Promises/A+ 2.3]  */
  5856  
  5857  
  5858  var resolve = function resolve(promise, x) {
  5859    /*  sanity check arguments  */
  5860  
  5861    /*  [Promises/A+ 2.3.1]  */
  5862    if (promise === x || promise.proxy === x) {
  5863      promise.reject(new TypeError('cannot resolve promise with itself'));
  5864      return;
  5865    }
  5866    /*  surgically check for a "then" method
  5867      (mainly to just call the "getter" of "then" only once)  */
  5868  
  5869  
  5870    var then;
  5871  
  5872    if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
  5873      try {
  5874        then = x.then;
  5875      }
  5876      /*  [Promises/A+ 2.3.3.1, 3.5]  */
  5877      catch (e) {
  5878        promise.reject(e);
  5879        /*  [Promises/A+ 2.3.3.2]  */
  5880  
  5881        return;
  5882      }
  5883    }
  5884    /*  handle own Thenables    [Promises/A+ 2.3.2]
  5885      and similar "thenables" [Promises/A+ 2.3.3]  */
  5886  
  5887  
  5888    if (typeof then === 'function') {
  5889      var resolved = false;
  5890  
  5891      try {
  5892        /*  call retrieved "then" method */
  5893  
  5894        /*  [Promises/A+ 2.3.3.3]  */
  5895        then.call(x,
  5896        /*  resolvePromise  */
  5897  
  5898        /*  [Promises/A+ 2.3.3.3.1]  */
  5899        function (y) {
  5900          if (resolved) return;
  5901          resolved = true;
  5902          /*  [Promises/A+ 2.3.3.3.3]  */
  5903  
  5904          if (y === x)
  5905            /*  [Promises/A+ 3.6]  */
  5906            promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
  5907        },
  5908        /*  rejectPromise  */
  5909  
  5910        /*  [Promises/A+ 2.3.3.3.2]  */
  5911        function (r) {
  5912          if (resolved) return;
  5913          resolved = true;
  5914          /*  [Promises/A+ 2.3.3.3.3]  */
  5915  
  5916          promise.reject(r);
  5917        });
  5918      } catch (e) {
  5919        if (!resolved)
  5920          /*  [Promises/A+ 2.3.3.3.3]  */
  5921          promise.reject(e);
  5922        /*  [Promises/A+ 2.3.3.3.4]  */
  5923      }
  5924  
  5925      return;
  5926    }
  5927    /*  handle other values  */
  5928  
  5929  
  5930    promise.fulfill(x);
  5931    /*  [Promises/A+ 2.3.4, 2.3.3.4]  */
  5932  }; // so we always have Promise.all()
  5933  
  5934  
  5935  api.all = function (ps) {
  5936    return new api(function (resolveAll, rejectAll) {
  5937      var vals = new Array(ps.length);
  5938      var doneCount = 0;
  5939  
  5940      var fulfill = function fulfill(i, val) {
  5941        vals[i] = val;
  5942        doneCount++;
  5943  
  5944        if (doneCount === ps.length) {
  5945          resolveAll(vals);
  5946        }
  5947      };
  5948  
  5949      for (var i = 0; i < ps.length; i++) {
  5950        (function (i) {
  5951          var p = ps[i];
  5952          var isPromise = p != null && p.then != null;
  5953  
  5954          if (isPromise) {
  5955            p.then(function (val) {
  5956              fulfill(i, val);
  5957            }, function (err) {
  5958              rejectAll(err);
  5959            });
  5960          } else {
  5961            var val = p;
  5962            fulfill(i, val);
  5963          }
  5964        })(i);
  5965      }
  5966    });
  5967  };
  5968  
  5969  api.resolve = function (val) {
  5970    return new api(function (resolve, reject) {
  5971      resolve(val);
  5972    });
  5973  };
  5974  
  5975  api.reject = function (val) {
  5976    return new api(function (resolve, reject) {
  5977      reject(val);
  5978    });
  5979  };
  5980  
  5981  var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
  5982  
  5983  var Animation = function Animation(target, opts, opts2) {
  5984    var isCore = core(target);
  5985    var isEle = !isCore;
  5986  
  5987    var _p = this._private = extend({
  5988      duration: 1000
  5989    }, opts, opts2);
  5990  
  5991    _p.target = target;
  5992    _p.style = _p.style || _p.css;
  5993    _p.started = false;
  5994    _p.playing = false;
  5995    _p.hooked = false;
  5996    _p.applying = false;
  5997    _p.progress = 0;
  5998    _p.completes = [];
  5999    _p.frames = [];
  6000  
  6001    if (_p.complete && fn(_p.complete)) {
  6002      _p.completes.push(_p.complete);
  6003    }
  6004  
  6005    if (isEle) {
  6006      var pos = target.position();
  6007      _p.startPosition = _p.startPosition || {
  6008        x: pos.x,
  6009        y: pos.y
  6010      };
  6011      _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
  6012    }
  6013  
  6014    if (isCore) {
  6015      var pan = target.pan();
  6016      _p.startPan = {
  6017        x: pan.x,
  6018        y: pan.y
  6019      };
  6020      _p.startZoom = target.zoom();
  6021    } // for future timeline/animations impl
  6022  
  6023  
  6024    this.length = 1;
  6025    this[0] = this;
  6026  };
  6027  
  6028  var anifn = Animation.prototype;
  6029  extend(anifn, {
  6030    instanceString: function instanceString() {
  6031      return 'animation';
  6032    },
  6033    hook: function hook() {
  6034      var _p = this._private;
  6035  
  6036      if (!_p.hooked) {
  6037        // add to target's animation queue
  6038        var q;
  6039        var tAni = _p.target._private.animation;
  6040  
  6041        if (_p.queue) {
  6042          q = tAni.queue;
  6043        } else {
  6044          q = tAni.current;
  6045        }
  6046  
  6047        q.push(this); // add to the animation loop pool
  6048  
  6049        if (elementOrCollection(_p.target)) {
  6050          _p.target.cy().addToAnimationPool(_p.target);
  6051        }
  6052  
  6053        _p.hooked = true;
  6054      }
  6055  
  6056      return this;
  6057    },
  6058    play: function play() {
  6059      var _p = this._private; // autorewind
  6060  
  6061      if (_p.progress === 1) {
  6062        _p.progress = 0;
  6063      }
  6064  
  6065      _p.playing = true;
  6066      _p.started = false; // needs to be started by animation loop
  6067  
  6068      _p.stopped = false;
  6069      this.hook(); // the animation loop will start the animation...
  6070  
  6071      return this;
  6072    },
  6073    playing: function playing() {
  6074      return this._private.playing;
  6075    },
  6076    apply: function apply() {
  6077      var _p = this._private;
  6078      _p.applying = true;
  6079      _p.started = false; // needs to be started by animation loop
  6080  
  6081      _p.stopped = false;
  6082      this.hook(); // the animation loop will apply the animation at this progress
  6083  
  6084      return this;
  6085    },
  6086    applying: function applying() {
  6087      return this._private.applying;
  6088    },
  6089    pause: function pause() {
  6090      var _p = this._private;
  6091      _p.playing = false;
  6092      _p.started = false;
  6093      return this;
  6094    },
  6095    stop: function stop() {
  6096      var _p = this._private;
  6097      _p.playing = false;
  6098      _p.started = false;
  6099      _p.stopped = true; // to be removed from animation queues
  6100  
  6101      return this;
  6102    },
  6103    rewind: function rewind() {
  6104      return this.progress(0);
  6105    },
  6106    fastforward: function fastforward() {
  6107      return this.progress(1);
  6108    },
  6109    time: function time(t) {
  6110      var _p = this._private;
  6111  
  6112      if (t === undefined) {
  6113        return _p.progress * _p.duration;
  6114      } else {
  6115        return this.progress(t / _p.duration);
  6116      }
  6117    },
  6118    progress: function progress(p) {
  6119      var _p = this._private;
  6120      var wasPlaying = _p.playing;
  6121  
  6122      if (p === undefined) {
  6123        return _p.progress;
  6124      } else {
  6125        if (wasPlaying) {
  6126          this.pause();
  6127        }
  6128  
  6129        _p.progress = p;
  6130        _p.started = false;
  6131  
  6132        if (wasPlaying) {
  6133          this.play();
  6134        }
  6135      }
  6136  
  6137      return this;
  6138    },
  6139    completed: function completed() {
  6140      return this._private.progress === 1;
  6141    },
  6142    reverse: function reverse() {
  6143      var _p = this._private;
  6144      var wasPlaying = _p.playing;
  6145  
  6146      if (wasPlaying) {
  6147        this.pause();
  6148      }
  6149  
  6150      _p.progress = 1 - _p.progress;
  6151      _p.started = false;
  6152  
  6153      var swap = function swap(a, b) {
  6154        var _pa = _p[a];
  6155  
  6156        if (_pa == null) {
  6157          return;
  6158        }
  6159  
  6160        _p[a] = _p[b];
  6161        _p[b] = _pa;
  6162      };
  6163  
  6164      swap('zoom', 'startZoom');
  6165      swap('pan', 'startPan');
  6166      swap('position', 'startPosition'); // swap styles
  6167  
  6168      if (_p.style) {
  6169        for (var i = 0; i < _p.style.length; i++) {
  6170          var prop = _p.style[i];
  6171          var name = prop.name;
  6172          var startStyleProp = _p.startStyle[name];
  6173          _p.startStyle[name] = prop;
  6174          _p.style[i] = startStyleProp;
  6175        }
  6176      }
  6177  
  6178      if (wasPlaying) {
  6179        this.play();
  6180      }
  6181  
  6182      return this;
  6183    },
  6184    promise: function promise(type) {
  6185      var _p = this._private;
  6186      var arr;
  6187  
  6188      switch (type) {
  6189        case 'frame':
  6190          arr = _p.frames;
  6191          break;
  6192  
  6193        default:
  6194        case 'complete':
  6195        case 'completed':
  6196          arr = _p.completes;
  6197      }
  6198  
  6199      return new Promise$1(function (resolve, reject) {
  6200        arr.push(function () {
  6201          resolve();
  6202        });
  6203      });
  6204    }
  6205  });
  6206  anifn.complete = anifn.completed;
  6207  anifn.run = anifn.play;
  6208  anifn.running = anifn.playing;
  6209  
  6210  var define = {
  6211    animated: function animated() {
  6212      return function animatedImpl() {
  6213        var self = this;
  6214        var selfIsArrayLike = self.length !== undefined;
  6215        var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6216  
  6217        var cy = this._private.cy || this;
  6218  
  6219        if (!cy.styleEnabled()) {
  6220          return false;
  6221        }
  6222  
  6223        var ele = all[0];
  6224  
  6225        if (ele) {
  6226          return ele._private.animation.current.length > 0;
  6227        }
  6228      };
  6229    },
  6230    // animated
  6231    clearQueue: function clearQueue() {
  6232      return function clearQueueImpl() {
  6233        var self = this;
  6234        var selfIsArrayLike = self.length !== undefined;
  6235        var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6236  
  6237        var cy = this._private.cy || this;
  6238  
  6239        if (!cy.styleEnabled()) {
  6240          return this;
  6241        }
  6242  
  6243        for (var i = 0; i < all.length; i++) {
  6244          var ele = all[i];
  6245          ele._private.animation.queue = [];
  6246        }
  6247  
  6248        return this;
  6249      };
  6250    },
  6251    // clearQueue
  6252    delay: function delay() {
  6253      return function delayImpl(time, complete) {
  6254        var cy = this._private.cy || this;
  6255  
  6256        if (!cy.styleEnabled()) {
  6257          return this;
  6258        }
  6259  
  6260        return this.animate({
  6261          delay: time,
  6262          duration: time,
  6263          complete: complete
  6264        });
  6265      };
  6266    },
  6267    // delay
  6268    delayAnimation: function delayAnimation() {
  6269      return function delayAnimationImpl(time, complete) {
  6270        var cy = this._private.cy || this;
  6271  
  6272        if (!cy.styleEnabled()) {
  6273          return this;
  6274        }
  6275  
  6276        return this.animation({
  6277          delay: time,
  6278          duration: time,
  6279          complete: complete
  6280        });
  6281      };
  6282    },
  6283    // delay
  6284    animation: function animation() {
  6285      return function animationImpl(properties, params) {
  6286        var self = this;
  6287        var selfIsArrayLike = self.length !== undefined;
  6288        var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6289  
  6290        var cy = this._private.cy || this;
  6291        var isCore = !selfIsArrayLike;
  6292        var isEles = !isCore;
  6293  
  6294        if (!cy.styleEnabled()) {
  6295          return this;
  6296        }
  6297  
  6298        var style = cy.style();
  6299        properties = extend({}, properties, params);
  6300        var propertiesEmpty = Object.keys(properties).length === 0;
  6301  
  6302        if (propertiesEmpty) {
  6303          return new Animation(all[0], properties); // nothing to animate
  6304        }
  6305  
  6306        if (properties.duration === undefined) {
  6307          properties.duration = 400;
  6308        }
  6309  
  6310        switch (properties.duration) {
  6311          case 'slow':
  6312            properties.duration = 600;
  6313            break;
  6314  
  6315          case 'fast':
  6316            properties.duration = 200;
  6317            break;
  6318        }
  6319  
  6320        if (isEles) {
  6321          properties.style = style.getPropsList(properties.style || properties.css);
  6322          properties.css = undefined;
  6323        }
  6324  
  6325        if (isEles && properties.renderedPosition != null) {
  6326          var rpos = properties.renderedPosition;
  6327          var pan = cy.pan();
  6328          var zoom = cy.zoom();
  6329          properties.position = renderedToModelPosition(rpos, zoom, pan);
  6330        } // override pan w/ panBy if set
  6331  
  6332  
  6333        if (isCore && properties.panBy != null) {
  6334          var panBy = properties.panBy;
  6335          var cyPan = cy.pan();
  6336          properties.pan = {
  6337            x: cyPan.x + panBy.x,
  6338            y: cyPan.y + panBy.y
  6339          };
  6340        } // override pan w/ center if set
  6341  
  6342  
  6343        var center = properties.center || properties.centre;
  6344  
  6345        if (isCore && center != null) {
  6346          var centerPan = cy.getCenterPan(center.eles, properties.zoom);
  6347  
  6348          if (centerPan != null) {
  6349            properties.pan = centerPan;
  6350          }
  6351        } // override pan & zoom w/ fit if set
  6352  
  6353  
  6354        if (isCore && properties.fit != null) {
  6355          var fit = properties.fit;
  6356          var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
  6357  
  6358          if (fitVp != null) {
  6359            properties.pan = fitVp.pan;
  6360            properties.zoom = fitVp.zoom;
  6361          }
  6362        } // override zoom (& potentially pan) w/ zoom obj if set
  6363  
  6364  
  6365        if (isCore && plainObject(properties.zoom)) {
  6366          var vp = cy.getZoomedViewport(properties.zoom);
  6367  
  6368          if (vp != null) {
  6369            if (vp.zoomed) {
  6370              properties.zoom = vp.zoom;
  6371            }
  6372  
  6373            if (vp.panned) {
  6374              properties.pan = vp.pan;
  6375            }
  6376          } else {
  6377            properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
  6378          }
  6379        }
  6380  
  6381        return new Animation(all[0], properties);
  6382      };
  6383    },
  6384    // animate
  6385    animate: function animate() {
  6386      return function animateImpl(properties, params) {
  6387        var self = this;
  6388        var selfIsArrayLike = self.length !== undefined;
  6389        var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6390  
  6391        var cy = this._private.cy || this;
  6392  
  6393        if (!cy.styleEnabled()) {
  6394          return this;
  6395        }
  6396  
  6397        if (params) {
  6398          properties = extend({}, properties, params);
  6399        } // manually hook and run the animation
  6400  
  6401  
  6402        for (var i = 0; i < all.length; i++) {
  6403          var ele = all[i];
  6404          var queue = ele.animated() && (properties.queue === undefined || properties.queue);
  6405          var ani = ele.animation(properties, queue ? {
  6406            queue: true
  6407          } : undefined);
  6408          ani.play();
  6409        }
  6410  
  6411        return this; // chaining
  6412      };
  6413    },
  6414    // animate
  6415    stop: function stop() {
  6416      return function stopImpl(clearQueue, jumpToEnd) {
  6417        var self = this;
  6418        var selfIsArrayLike = self.length !== undefined;
  6419        var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6420  
  6421        var cy = this._private.cy || this;
  6422  
  6423        if (!cy.styleEnabled()) {
  6424          return this;
  6425        }
  6426  
  6427        for (var i = 0; i < all.length; i++) {
  6428          var ele = all[i];
  6429          var _p = ele._private;
  6430          var anis = _p.animation.current;
  6431  
  6432          for (var j = 0; j < anis.length; j++) {
  6433            var ani = anis[j];
  6434            var ani_p = ani._private;
  6435  
  6436            if (jumpToEnd) {
  6437              // next iteration of the animation loop, the animation
  6438              // will go straight to the end and be removed
  6439              ani_p.duration = 0;
  6440            }
  6441          } // clear the queue of future animations
  6442  
  6443  
  6444          if (clearQueue) {
  6445            _p.animation.queue = [];
  6446          }
  6447  
  6448          if (!jumpToEnd) {
  6449            _p.animation.current = [];
  6450          }
  6451        } // we have to notify (the animation loop doesn't do it for us on `stop`)
  6452  
  6453  
  6454        cy.notify('draw');
  6455        return this;
  6456      };
  6457    } // stop
  6458  
  6459  }; // define
  6460  
  6461  var define$1 = {
  6462    // access data field
  6463    data: function data(params) {
  6464      var defaults = {
  6465        field: 'data',
  6466        bindingEvent: 'data',
  6467        allowBinding: false,
  6468        allowSetting: false,
  6469        allowGetting: false,
  6470        settingEvent: 'data',
  6471        settingTriggersEvent: false,
  6472        triggerFnName: 'trigger',
  6473        immutableKeys: {},
  6474        // key => true if immutable
  6475        updateStyle: false,
  6476        beforeGet: function beforeGet(self) {},
  6477        beforeSet: function beforeSet(self, obj) {},
  6478        onSet: function onSet(self) {},
  6479        canSet: function canSet(self) {
  6480          return true;
  6481        }
  6482      };
  6483      params = extend({}, defaults, params);
  6484      return function dataImpl(name, value) {
  6485        var p = params;
  6486        var self = this;
  6487        var selfIsArrayLike = self.length !== undefined;
  6488        var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6489  
  6490        var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
  6491  
  6492        if (string(name)) {
  6493          // set or get property
  6494          // .data('foo')
  6495          if (p.allowGetting && value === undefined) {
  6496            // get
  6497            var ret;
  6498  
  6499            if (single) {
  6500              p.beforeGet(single);
  6501              ret = single._private[p.field][name];
  6502            }
  6503  
  6504            return ret; // .data('foo', 'bar')
  6505          } else if (p.allowSetting && value !== undefined) {
  6506            // set
  6507            var valid = !p.immutableKeys[name];
  6508  
  6509            if (valid) {
  6510              var change = _defineProperty({}, name, value);
  6511  
  6512              p.beforeSet(self, change);
  6513  
  6514              for (var i = 0, l = all.length; i < l; i++) {
  6515                var ele = all[i];
  6516  
  6517                if (p.canSet(ele)) {
  6518                  ele._private[p.field][name] = value;
  6519                }
  6520              } // update mappers if asked
  6521  
  6522  
  6523              if (p.updateStyle) {
  6524                self.updateStyle();
  6525              } // call onSet callback
  6526  
  6527  
  6528              p.onSet(self);
  6529  
  6530              if (p.settingTriggersEvent) {
  6531                self[p.triggerFnName](p.settingEvent);
  6532              }
  6533            }
  6534          } // .data({ 'foo': 'bar' })
  6535  
  6536        } else if (p.allowSetting && plainObject(name)) {
  6537          // extend
  6538          var obj = name;
  6539          var k, v;
  6540          var keys = Object.keys(obj);
  6541          p.beforeSet(self, obj);
  6542  
  6543          for (var _i = 0; _i < keys.length; _i++) {
  6544            k = keys[_i];
  6545            v = obj[k];
  6546  
  6547            var _valid = !p.immutableKeys[k];
  6548  
  6549            if (_valid) {
  6550              for (var j = 0; j < all.length; j++) {
  6551                var _ele = all[j];
  6552  
  6553                if (p.canSet(_ele)) {
  6554                  _ele._private[p.field][k] = v;
  6555                }
  6556              }
  6557            }
  6558          } // update mappers if asked
  6559  
  6560  
  6561          if (p.updateStyle) {
  6562            self.updateStyle();
  6563          } // call onSet callback
  6564  
  6565  
  6566          p.onSet(self);
  6567  
  6568          if (p.settingTriggersEvent) {
  6569            self[p.triggerFnName](p.settingEvent);
  6570          } // .data(function(){ ... })
  6571  
  6572        } else if (p.allowBinding && fn(name)) {
  6573          // bind to event
  6574          var fn$1 = name;
  6575          self.on(p.bindingEvent, fn$1); // .data()
  6576        } else if (p.allowGetting && name === undefined) {
  6577          // get whole object
  6578          var _ret;
  6579  
  6580          if (single) {
  6581            p.beforeGet(single);
  6582            _ret = single._private[p.field];
  6583          }
  6584  
  6585          return _ret;
  6586        }
  6587  
  6588        return self; // maintain chainability
  6589      }; // function
  6590    },
  6591    // data
  6592    // remove data field
  6593    removeData: function removeData(params) {
  6594      var defaults = {
  6595        field: 'data',
  6596        event: 'data',
  6597        triggerFnName: 'trigger',
  6598        triggerEvent: false,
  6599        immutableKeys: {} // key => true if immutable
  6600  
  6601      };
  6602      params = extend({}, defaults, params);
  6603      return function removeDataImpl(names) {
  6604        var p = params;
  6605        var self = this;
  6606        var selfIsArrayLike = self.length !== undefined;
  6607        var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6608        // .removeData('foo bar')
  6609  
  6610        if (string(names)) {
  6611          // then get the list of keys, and delete them
  6612          var keys = names.split(/\s+/);
  6613          var l = keys.length;
  6614  
  6615          for (var i = 0; i < l; i++) {
  6616            // delete each non-empty key
  6617            var key = keys[i];
  6618  
  6619            if (emptyString(key)) {
  6620              continue;
  6621            }
  6622  
  6623            var valid = !p.immutableKeys[key]; // not valid if immutable
  6624  
  6625            if (valid) {
  6626              for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
  6627                all[i_a]._private[p.field][key] = undefined;
  6628              }
  6629            }
  6630          }
  6631  
  6632          if (p.triggerEvent) {
  6633            self[p.triggerFnName](p.event);
  6634          } // .removeData()
  6635  
  6636        } else if (names === undefined) {
  6637          // then delete all keys
  6638          for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
  6639            var _privateFields = all[_i_a]._private[p.field];
  6640  
  6641            var _keys = Object.keys(_privateFields);
  6642  
  6643            for (var _i2 = 0; _i2 < _keys.length; _i2++) {
  6644              var _key = _keys[_i2];
  6645              var validKeyToDelete = !p.immutableKeys[_key];
  6646  
  6647              if (validKeyToDelete) {
  6648                _privateFields[_key] = undefined;
  6649              }
  6650            }
  6651          }
  6652  
  6653          if (p.triggerEvent) {
  6654            self[p.triggerFnName](p.event);
  6655          }
  6656        }
  6657  
  6658        return self; // maintain chaining
  6659      }; // function
  6660    } // removeData
  6661  
  6662  }; // define
  6663  
  6664  var define$2 = {
  6665    eventAliasesOn: function eventAliasesOn(proto) {
  6666      var p = proto;
  6667      p.addListener = p.listen = p.bind = p.on;
  6668      p.unlisten = p.unbind = p.off = p.removeListener;
  6669      p.trigger = p.emit; // this is just a wrapper alias of .on()
  6670  
  6671      p.pon = p.promiseOn = function (events, selector) {
  6672        var self = this;
  6673        var args = Array.prototype.slice.call(arguments, 0);
  6674        return new Promise$1(function (resolve, reject) {
  6675          var callback = function callback(e) {
  6676            self.off.apply(self, offArgs);
  6677            resolve(e);
  6678          };
  6679  
  6680          var onArgs = args.concat([callback]);
  6681          var offArgs = onArgs.concat([]);
  6682          self.on.apply(self, onArgs);
  6683        });
  6684      };
  6685    }
  6686  }; // define
  6687  
  6688  // use this module to cherry pick functions into your prototype
  6689  var define$3 = {};
  6690  [define, define$1, define$2].forEach(function (m) {
  6691    extend(define$3, m);
  6692  });
  6693  
  6694  var elesfn$d = {
  6695    animate: define$3.animate(),
  6696    animation: define$3.animation(),
  6697    animated: define$3.animated(),
  6698    clearQueue: define$3.clearQueue(),
  6699    delay: define$3.delay(),
  6700    delayAnimation: define$3.delayAnimation(),
  6701    stop: define$3.stop()
  6702  };
  6703  
  6704  var elesfn$e = {
  6705    classes: function classes(_classes) {
  6706      var self = this;
  6707  
  6708      if (_classes === undefined) {
  6709        var ret = [];
  6710  
  6711        self[0]._private.classes.forEach(function (cls) {
  6712          return ret.push(cls);
  6713        });
  6714  
  6715        return ret;
  6716      } else if (!array(_classes)) {
  6717        // extract classes from string
  6718        _classes = (_classes || '').match(/\S+/g) || [];
  6719      }
  6720  
  6721      var changed = [];
  6722      var classesSet = new Set$1(_classes); // check and update each ele
  6723  
  6724      for (var j = 0; j < self.length; j++) {
  6725        var ele = self[j];
  6726        var _p = ele._private;
  6727        var eleClasses = _p.classes;
  6728        var changedEle = false; // check if ele has all of the passed classes
  6729  
  6730        for (var i = 0; i < _classes.length; i++) {
  6731          var cls = _classes[i];
  6732          var eleHasClass = eleClasses.has(cls);
  6733  
  6734          if (!eleHasClass) {
  6735            changedEle = true;
  6736            break;
  6737          }
  6738        } // check if ele has classes outside of those passed
  6739  
  6740  
  6741        if (!changedEle) {
  6742          changedEle = eleClasses.size !== _classes.length;
  6743        }
  6744  
  6745        if (changedEle) {
  6746          _p.classes = classesSet;
  6747          changed.push(ele);
  6748        }
  6749      } // trigger update style on those eles that had class changes
  6750  
  6751  
  6752      if (changed.length > 0) {
  6753        this.spawn(changed).updateStyle().emit('class');
  6754      }
  6755  
  6756      return self;
  6757    },
  6758    addClass: function addClass(classes) {
  6759      return this.toggleClass(classes, true);
  6760    },
  6761    hasClass: function hasClass(className) {
  6762      var ele = this[0];
  6763      return ele != null && ele._private.classes.has(className);
  6764    },
  6765    toggleClass: function toggleClass(classes, toggle) {
  6766      if (!array(classes)) {
  6767        // extract classes from string
  6768        classes = classes.match(/\S+/g) || [];
  6769      }
  6770  
  6771      var self = this;
  6772      var toggleUndefd = toggle === undefined;
  6773      var changed = []; // eles who had classes changed
  6774  
  6775      for (var i = 0, il = self.length; i < il; i++) {
  6776        var ele = self[i];
  6777        var eleClasses = ele._private.classes;
  6778        var changedEle = false;
  6779  
  6780        for (var j = 0; j < classes.length; j++) {
  6781          var cls = classes[j];
  6782          var hasClass = eleClasses.has(cls);
  6783          var changedNow = false;
  6784  
  6785          if (toggle || toggleUndefd && !hasClass) {
  6786            eleClasses.add(cls);
  6787            changedNow = true;
  6788          } else if (!toggle || toggleUndefd && hasClass) {
  6789            eleClasses["delete"](cls);
  6790            changedNow = true;
  6791          }
  6792  
  6793          if (!changedEle && changedNow) {
  6794            changed.push(ele);
  6795            changedEle = true;
  6796          }
  6797        } // for j classes
  6798  
  6799      } // for i eles
  6800      // trigger update style on those eles that had class changes
  6801  
  6802  
  6803      if (changed.length > 0) {
  6804        this.spawn(changed).updateStyle().emit('class');
  6805      }
  6806  
  6807      return self;
  6808    },
  6809    removeClass: function removeClass(classes) {
  6810      return this.toggleClass(classes, false);
  6811    },
  6812    flashClass: function flashClass(classes, duration) {
  6813      var self = this;
  6814  
  6815      if (duration == null) {
  6816        duration = 250;
  6817      } else if (duration === 0) {
  6818        return self; // nothing to do really
  6819      }
  6820  
  6821      self.addClass(classes);
  6822      setTimeout(function () {
  6823        self.removeClass(classes);
  6824      }, duration);
  6825      return self;
  6826    }
  6827  };
  6828  elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
  6829  
  6830  var tokens = {
  6831    metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
  6832    // chars we need to escape in let names, etc
  6833    comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
  6834    // binary comparison op (used in data selectors)
  6835    boolOp: '\\?|\\!|\\^',
  6836    // boolean (unary) operators (used in data selectors)
  6837    string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
  6838    // string literals (used in data selectors) -- doublequotes | singlequotes
  6839    number: number$1,
  6840    // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
  6841    meta: 'degree|indegree|outdegree',
  6842    // allowed metadata fields (i.e. allowed functions to use from Collection)
  6843    separator: '\\s*,\\s*',
  6844    // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
  6845    descendant: '\\s+',
  6846    child: '\\s+>\\s+',
  6847    subject: '\\$',
  6848    group: 'node|edge|\\*',
  6849    directedEdge: '\\s+->\\s+',
  6850    undirectedEdge: '\\s+<->\\s+'
  6851  };
  6852  tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name
  6853  
  6854  tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
  6855  
  6856  tokens.className = tokens.variable; // a class name (follows variable conventions)
  6857  
  6858  tokens.id = tokens.variable; // an element id (follows variable conventions)
  6859  
  6860  (function () {
  6861    var ops, op, i; // add @ variants to comparatorOp
  6862  
  6863    ops = tokens.comparatorOp.split('|');
  6864  
  6865    for (i = 0; i < ops.length; i++) {
  6866      op = ops[i];
  6867      tokens.comparatorOp += '|@' + op;
  6868    } // add ! variants to comparatorOp
  6869  
  6870  
  6871    ops = tokens.comparatorOp.split('|');
  6872  
  6873    for (i = 0; i < ops.length; i++) {
  6874      op = ops[i];
  6875  
  6876      if (op.indexOf('!') >= 0) {
  6877        continue;
  6878      } // skip ops that explicitly contain !
  6879  
  6880  
  6881      if (op === '=') {
  6882        continue;
  6883      } // skip = b/c != is explicitly defined
  6884  
  6885  
  6886      tokens.comparatorOp += '|\\!' + op;
  6887    }
  6888  })();
  6889  
  6890  /**
  6891   * Make a new query object
  6892   *
  6893   * @prop type {Type} The type enum (int) of the query
  6894   * @prop checks List of checks to make against an ele to test for a match
  6895   */
  6896  var newQuery = function newQuery() {
  6897    return {
  6898      checks: []
  6899    };
  6900  };
  6901  
  6902  /**
  6903   * A check type enum-like object.  Uses integer values for fast match() lookup.
  6904   * The ordering does not matter as long as the ints are unique.
  6905   */
  6906  var Type = {
  6907    /** E.g. node */
  6908    GROUP: 0,
  6909  
  6910    /** A collection of elements */
  6911    COLLECTION: 1,
  6912  
  6913    /** A filter(ele) function */
  6914    FILTER: 2,
  6915  
  6916    /** E.g. [foo > 1] */
  6917    DATA_COMPARE: 3,
  6918  
  6919    /** E.g. [foo] */
  6920    DATA_EXIST: 4,
  6921  
  6922    /** E.g. [?foo] */
  6923    DATA_BOOL: 5,
  6924  
  6925    /** E.g. [[degree > 2]] */
  6926    META_COMPARE: 6,
  6927  
  6928    /** E.g. :selected */
  6929    STATE: 7,
  6930  
  6931    /** E.g. #foo */
  6932    ID: 8,
  6933  
  6934    /** E.g. .foo */
  6935    CLASS: 9,
  6936  
  6937    /** E.g. #foo <-> #bar */
  6938    UNDIRECTED_EDGE: 10,
  6939  
  6940    /** E.g. #foo -> #bar */
  6941    DIRECTED_EDGE: 11,
  6942  
  6943    /** E.g. $#foo -> #bar */
  6944    NODE_SOURCE: 12,
  6945  
  6946    /** E.g. #foo -> $#bar */
  6947    NODE_TARGET: 13,
  6948  
  6949    /** E.g. $#foo <-> #bar */
  6950    NODE_NEIGHBOR: 14,
  6951  
  6952    /** E.g. #foo > #bar */
  6953    CHILD: 15,
  6954  
  6955    /** E.g. #foo #bar */
  6956    DESCENDANT: 16,
  6957  
  6958    /** E.g. $#foo > #bar */
  6959    PARENT: 17,
  6960  
  6961    /** E.g. $#foo #bar */
  6962    ANCESTOR: 18,
  6963  
  6964    /** E.g. #foo > $bar > #baz */
  6965    COMPOUND_SPLIT: 19,
  6966  
  6967    /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
  6968    TRUE: 20
  6969  };
  6970  
  6971  var stateSelectors = [{
  6972    selector: ':selected',
  6973    matches: function matches(ele) {
  6974      return ele.selected();
  6975    }
  6976  }, {
  6977    selector: ':unselected',
  6978    matches: function matches(ele) {
  6979      return !ele.selected();
  6980    }
  6981  }, {
  6982    selector: ':selectable',
  6983    matches: function matches(ele) {
  6984      return ele.selectable();
  6985    }
  6986  }, {
  6987    selector: ':unselectable',
  6988    matches: function matches(ele) {
  6989      return !ele.selectable();
  6990    }
  6991  }, {
  6992    selector: ':locked',
  6993    matches: function matches(ele) {
  6994      return ele.locked();
  6995    }
  6996  }, {
  6997    selector: ':unlocked',
  6998    matches: function matches(ele) {
  6999      return !ele.locked();
  7000    }
  7001  }, {
  7002    selector: ':visible',
  7003    matches: function matches(ele) {
  7004      return ele.visible();
  7005    }
  7006  }, {
  7007    selector: ':hidden',
  7008    matches: function matches(ele) {
  7009      return !ele.visible();
  7010    }
  7011  }, {
  7012    selector: ':transparent',
  7013    matches: function matches(ele) {
  7014      return ele.transparent();
  7015    }
  7016  }, {
  7017    selector: ':grabbed',
  7018    matches: function matches(ele) {
  7019      return ele.grabbed();
  7020    }
  7021  }, {
  7022    selector: ':free',
  7023    matches: function matches(ele) {
  7024      return !ele.grabbed();
  7025    }
  7026  }, {
  7027    selector: ':removed',
  7028    matches: function matches(ele) {
  7029      return ele.removed();
  7030    }
  7031  }, {
  7032    selector: ':inside',
  7033    matches: function matches(ele) {
  7034      return !ele.removed();
  7035    }
  7036  }, {
  7037    selector: ':grabbable',
  7038    matches: function matches(ele) {
  7039      return ele.grabbable();
  7040    }
  7041  }, {
  7042    selector: ':ungrabbable',
  7043    matches: function matches(ele) {
  7044      return !ele.grabbable();
  7045    }
  7046  }, {
  7047    selector: ':animated',
  7048    matches: function matches(ele) {
  7049      return ele.animated();
  7050    }
  7051  }, {
  7052    selector: ':unanimated',
  7053    matches: function matches(ele) {
  7054      return !ele.animated();
  7055    }
  7056  }, {
  7057    selector: ':parent',
  7058    matches: function matches(ele) {
  7059      return ele.isParent();
  7060    }
  7061  }, {
  7062    selector: ':childless',
  7063    matches: function matches(ele) {
  7064      return ele.isChildless();
  7065    }
  7066  }, {
  7067    selector: ':child',
  7068    matches: function matches(ele) {
  7069      return ele.isChild();
  7070    }
  7071  }, {
  7072    selector: ':orphan',
  7073    matches: function matches(ele) {
  7074      return ele.isOrphan();
  7075    }
  7076  }, {
  7077    selector: ':nonorphan',
  7078    matches: function matches(ele) {
  7079      return ele.isChild();
  7080    }
  7081  }, {
  7082    selector: ':compound',
  7083    matches: function matches(ele) {
  7084      if (ele.isNode()) {
  7085        return ele.isParent();
  7086      } else {
  7087        return ele.source().isParent() || ele.target().isParent();
  7088      }
  7089    }
  7090  }, {
  7091    selector: ':loop',
  7092    matches: function matches(ele) {
  7093      return ele.isLoop();
  7094    }
  7095  }, {
  7096    selector: ':simple',
  7097    matches: function matches(ele) {
  7098      return ele.isSimple();
  7099    }
  7100  }, {
  7101    selector: ':active',
  7102    matches: function matches(ele) {
  7103      return ele.active();
  7104    }
  7105  }, {
  7106    selector: ':inactive',
  7107    matches: function matches(ele) {
  7108      return !ele.active();
  7109    }
  7110  }, {
  7111    selector: ':backgrounding',
  7112    matches: function matches(ele) {
  7113      return ele.backgrounding();
  7114    }
  7115  }, {
  7116    selector: ':nonbackgrounding',
  7117    matches: function matches(ele) {
  7118      return !ele.backgrounding();
  7119    }
  7120  }].sort(function (a, b) {
  7121    // n.b. selectors that are starting substrings of others must have the longer ones first
  7122    return descending(a.selector, b.selector);
  7123  });
  7124  
  7125  var lookup = function () {
  7126    var selToFn = {};
  7127    var s;
  7128  
  7129    for (var i = 0; i < stateSelectors.length; i++) {
  7130      s = stateSelectors[i];
  7131      selToFn[s.selector] = s.matches;
  7132    }
  7133  
  7134    return selToFn;
  7135  }();
  7136  
  7137  var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
  7138    return lookup[sel](ele);
  7139  };
  7140  var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
  7141    return s.selector;
  7142  }).join('|') + ')';
  7143  
  7144  // so that values get compared properly in Selector.filter()
  7145  
  7146  var cleanMetaChars = function cleanMetaChars(str) {
  7147    return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
  7148      return $1;
  7149    });
  7150  };
  7151  
  7152  var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
  7153    selector[selector.length - 1] = replacementQuery;
  7154  }; // NOTE: add new expression syntax here to have it recognised by the parser;
  7155  // - a query contains all adjacent (i.e. no separator in between) expressions;
  7156  // - the current query is stored in selector[i]
  7157  // - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
  7158  
  7159  
  7160  var exprs = [{
  7161    name: 'group',
  7162    // just used for identifying when debugging
  7163    query: true,
  7164    regex: '(' + tokens.group + ')',
  7165    populate: function populate(selector, query, _ref) {
  7166      var _ref2 = _slicedToArray(_ref, 1),
  7167          group = _ref2[0];
  7168  
  7169      query.checks.push({
  7170        type: Type.GROUP,
  7171        value: group === '*' ? group : group + 's'
  7172      });
  7173    }
  7174  }, {
  7175    name: 'state',
  7176    query: true,
  7177    regex: stateSelectorRegex,
  7178    populate: function populate(selector, query, _ref3) {
  7179      var _ref4 = _slicedToArray(_ref3, 1),
  7180          state = _ref4[0];
  7181  
  7182      query.checks.push({
  7183        type: Type.STATE,
  7184        value: state
  7185      });
  7186    }
  7187  }, {
  7188    name: 'id',
  7189    query: true,
  7190    regex: '\\#(' + tokens.id + ')',
  7191    populate: function populate(selector, query, _ref5) {
  7192      var _ref6 = _slicedToArray(_ref5, 1),
  7193          id = _ref6[0];
  7194  
  7195      query.checks.push({
  7196        type: Type.ID,
  7197        value: cleanMetaChars(id)
  7198      });
  7199    }
  7200  }, {
  7201    name: 'className',
  7202    query: true,
  7203    regex: '\\.(' + tokens.className + ')',
  7204    populate: function populate(selector, query, _ref7) {
  7205      var _ref8 = _slicedToArray(_ref7, 1),
  7206          className = _ref8[0];
  7207  
  7208      query.checks.push({
  7209        type: Type.CLASS,
  7210        value: cleanMetaChars(className)
  7211      });
  7212    }
  7213  }, {
  7214    name: 'dataExists',
  7215    query: true,
  7216    regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
  7217    populate: function populate(selector, query, _ref9) {
  7218      var _ref10 = _slicedToArray(_ref9, 1),
  7219          variable = _ref10[0];
  7220  
  7221      query.checks.push({
  7222        type: Type.DATA_EXIST,
  7223        field: cleanMetaChars(variable)
  7224      });
  7225    }
  7226  }, {
  7227    name: 'dataCompare',
  7228    query: true,
  7229    regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
  7230    populate: function populate(selector, query, _ref11) {
  7231      var _ref12 = _slicedToArray(_ref11, 3),
  7232          variable = _ref12[0],
  7233          comparatorOp = _ref12[1],
  7234          value = _ref12[2];
  7235  
  7236      var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
  7237  
  7238      if (valueIsString) {
  7239        value = value.substring(1, value.length - 1);
  7240      } else {
  7241        value = parseFloat(value);
  7242      }
  7243  
  7244      query.checks.push({
  7245        type: Type.DATA_COMPARE,
  7246        field: cleanMetaChars(variable),
  7247        operator: comparatorOp,
  7248        value: value
  7249      });
  7250    }
  7251  }, {
  7252    name: 'dataBool',
  7253    query: true,
  7254    regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
  7255    populate: function populate(selector, query, _ref13) {
  7256      var _ref14 = _slicedToArray(_ref13, 2),
  7257          boolOp = _ref14[0],
  7258          variable = _ref14[1];
  7259  
  7260      query.checks.push({
  7261        type: Type.DATA_BOOL,
  7262        field: cleanMetaChars(variable),
  7263        operator: boolOp
  7264      });
  7265    }
  7266  }, {
  7267    name: 'metaCompare',
  7268    query: true,
  7269    regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
  7270    populate: function populate(selector, query, _ref15) {
  7271      var _ref16 = _slicedToArray(_ref15, 3),
  7272          meta = _ref16[0],
  7273          comparatorOp = _ref16[1],
  7274          number = _ref16[2];
  7275  
  7276      query.checks.push({
  7277        type: Type.META_COMPARE,
  7278        field: cleanMetaChars(meta),
  7279        operator: comparatorOp,
  7280        value: parseFloat(number)
  7281      });
  7282    }
  7283  }, {
  7284    name: 'nextQuery',
  7285    separator: true,
  7286    regex: tokens.separator,
  7287    populate: function populate(selector, query) {
  7288      var currentSubject = selector.currentSubject;
  7289      var edgeCount = selector.edgeCount;
  7290      var compoundCount = selector.compoundCount;
  7291      var lastQ = selector[selector.length - 1];
  7292  
  7293      if (currentSubject != null) {
  7294        lastQ.subject = currentSubject;
  7295        selector.currentSubject = null;
  7296      }
  7297  
  7298      lastQ.edgeCount = edgeCount;
  7299      lastQ.compoundCount = compoundCount;
  7300      selector.edgeCount = 0;
  7301      selector.compoundCount = 0; // go on to next query
  7302  
  7303      var nextQuery = selector[selector.length++] = newQuery();
  7304      return nextQuery; // this is the new query to be filled by the following exprs
  7305    }
  7306  }, {
  7307    name: 'directedEdge',
  7308    separator: true,
  7309    regex: tokens.directedEdge,
  7310    populate: function populate(selector, query) {
  7311      if (selector.currentSubject == null) {
  7312        // undirected edge
  7313        var edgeQuery = newQuery();
  7314        var source = query;
  7315        var target = newQuery();
  7316        edgeQuery.checks.push({
  7317          type: Type.DIRECTED_EDGE,
  7318          source: source,
  7319          target: target
  7320        }); // the query in the selector should be the edge rather than the source
  7321  
  7322        replaceLastQuery(selector, query, edgeQuery);
  7323        selector.edgeCount++; // we're now populating the target query with expressions that follow
  7324  
  7325        return target;
  7326      } else {
  7327        // source/target
  7328        var srcTgtQ = newQuery();
  7329        var _source = query;
  7330  
  7331        var _target = newQuery();
  7332  
  7333        srcTgtQ.checks.push({
  7334          type: Type.NODE_SOURCE,
  7335          source: _source,
  7336          target: _target
  7337        }); // the query in the selector should be the neighbourhood rather than the node
  7338  
  7339        replaceLastQuery(selector, query, srcTgtQ);
  7340        selector.edgeCount++;
  7341        return _target; // now populating the target with the following expressions
  7342      }
  7343    }
  7344  }, {
  7345    name: 'undirectedEdge',
  7346    separator: true,
  7347    regex: tokens.undirectedEdge,
  7348    populate: function populate(selector, query) {
  7349      if (selector.currentSubject == null) {
  7350        // undirected edge
  7351        var edgeQuery = newQuery();
  7352        var source = query;
  7353        var target = newQuery();
  7354        edgeQuery.checks.push({
  7355          type: Type.UNDIRECTED_EDGE,
  7356          nodes: [source, target]
  7357        }); // the query in the selector should be the edge rather than the source
  7358  
  7359        replaceLastQuery(selector, query, edgeQuery);
  7360        selector.edgeCount++; // we're now populating the target query with expressions that follow
  7361  
  7362        return target;
  7363      } else {
  7364        // neighbourhood
  7365        var nhoodQ = newQuery();
  7366        var node = query;
  7367        var neighbor = newQuery();
  7368        nhoodQ.checks.push({
  7369          type: Type.NODE_NEIGHBOR,
  7370          node: node,
  7371          neighbor: neighbor
  7372        }); // the query in the selector should be the neighbourhood rather than the node
  7373  
  7374        replaceLastQuery(selector, query, nhoodQ);
  7375        return neighbor; // now populating the neighbor with following expressions
  7376      }
  7377    }
  7378  }, {
  7379    name: 'child',
  7380    separator: true,
  7381    regex: tokens.child,
  7382    populate: function populate(selector, query) {
  7383      if (selector.currentSubject == null) {
  7384        // default: child query
  7385        var parentChildQuery = newQuery();
  7386        var child = newQuery();
  7387        var parent = selector[selector.length - 1];
  7388        parentChildQuery.checks.push({
  7389          type: Type.CHILD,
  7390          parent: parent,
  7391          child: child
  7392        }); // the query in the selector should be the '>' itself
  7393  
  7394        replaceLastQuery(selector, query, parentChildQuery);
  7395        selector.compoundCount++; // we're now populating the child query with expressions that follow
  7396  
  7397        return child;
  7398      } else if (selector.currentSubject === query) {
  7399        // compound split query
  7400        var compound = newQuery();
  7401        var left = selector[selector.length - 1];
  7402        var right = newQuery();
  7403        var subject = newQuery();
  7404  
  7405        var _child = newQuery();
  7406  
  7407        var _parent = newQuery(); // set up the root compound q
  7408  
  7409  
  7410        compound.checks.push({
  7411          type: Type.COMPOUND_SPLIT,
  7412          left: left,
  7413          right: right,
  7414          subject: subject
  7415        }); // populate the subject and replace the q at the old spot (within left) with TRUE
  7416  
  7417        subject.checks = query.checks; // take the checks from the left
  7418  
  7419        query.checks = [{
  7420          type: Type.TRUE
  7421        }]; // checks under left refs the subject implicitly
  7422        // set up the right q
  7423  
  7424        _parent.checks.push({
  7425          type: Type.TRUE
  7426        }); // parent implicitly refs the subject
  7427  
  7428  
  7429        right.checks.push({
  7430          type: Type.PARENT,
  7431          // type is swapped on right side queries
  7432          parent: _parent,
  7433          child: _child // empty for now
  7434  
  7435        });
  7436        replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
  7437  
  7438        selector.currentSubject = subject;
  7439        selector.compoundCount++;
  7440        return _child; // now populating the right side's child
  7441      } else {
  7442        // parent query
  7443        // info for parent query
  7444        var _parent2 = newQuery();
  7445  
  7446        var _child2 = newQuery();
  7447  
  7448        var pcQChecks = [{
  7449          type: Type.PARENT,
  7450          parent: _parent2,
  7451          child: _child2
  7452        }]; // the parent-child query takes the place of the query previously being populated
  7453  
  7454        _parent2.checks = query.checks; // the previous query contains the checks for the parent
  7455  
  7456        query.checks = pcQChecks; // pc query takes over
  7457  
  7458        selector.compoundCount++;
  7459        return _child2; // we're now populating the child
  7460      }
  7461    }
  7462  }, {
  7463    name: 'descendant',
  7464    separator: true,
  7465    regex: tokens.descendant,
  7466    populate: function populate(selector, query) {
  7467      if (selector.currentSubject == null) {
  7468        // default: descendant query
  7469        var ancChQuery = newQuery();
  7470        var descendant = newQuery();
  7471        var ancestor = selector[selector.length - 1];
  7472        ancChQuery.checks.push({
  7473          type: Type.DESCENDANT,
  7474          ancestor: ancestor,
  7475          descendant: descendant
  7476        }); // the query in the selector should be the '>' itself
  7477  
  7478        replaceLastQuery(selector, query, ancChQuery);
  7479        selector.compoundCount++; // we're now populating the descendant query with expressions that follow
  7480  
  7481        return descendant;
  7482      } else if (selector.currentSubject === query) {
  7483        // compound split query
  7484        var compound = newQuery();
  7485        var left = selector[selector.length - 1];
  7486        var right = newQuery();
  7487        var subject = newQuery();
  7488  
  7489        var _descendant = newQuery();
  7490  
  7491        var _ancestor = newQuery(); // set up the root compound q
  7492  
  7493  
  7494        compound.checks.push({
  7495          type: Type.COMPOUND_SPLIT,
  7496          left: left,
  7497          right: right,
  7498          subject: subject
  7499        }); // populate the subject and replace the q at the old spot (within left) with TRUE
  7500  
  7501        subject.checks = query.checks; // take the checks from the left
  7502  
  7503        query.checks = [{
  7504          type: Type.TRUE
  7505        }]; // checks under left refs the subject implicitly
  7506        // set up the right q
  7507  
  7508        _ancestor.checks.push({
  7509          type: Type.TRUE
  7510        }); // ancestor implicitly refs the subject
  7511  
  7512  
  7513        right.checks.push({
  7514          type: Type.ANCESTOR,
  7515          // type is swapped on right side queries
  7516          ancestor: _ancestor,
  7517          descendant: _descendant // empty for now
  7518  
  7519        });
  7520        replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
  7521  
  7522        selector.currentSubject = subject;
  7523        selector.compoundCount++;
  7524        return _descendant; // now populating the right side's descendant
  7525      } else {
  7526        // ancestor query
  7527        // info for parent query
  7528        var _ancestor2 = newQuery();
  7529  
  7530        var _descendant2 = newQuery();
  7531  
  7532        var adQChecks = [{
  7533          type: Type.ANCESTOR,
  7534          ancestor: _ancestor2,
  7535          descendant: _descendant2
  7536        }]; // the parent-child query takes the place of the query previously being populated
  7537  
  7538        _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
  7539  
  7540        query.checks = adQChecks; // pc query takes over
  7541  
  7542        selector.compoundCount++;
  7543        return _descendant2; // we're now populating the child
  7544      }
  7545    }
  7546  }, {
  7547    name: 'subject',
  7548    modifier: true,
  7549    regex: tokens.subject,
  7550    populate: function populate(selector, query) {
  7551      if (selector.currentSubject != null && selector.currentSubject !== query) {
  7552        warn('Redefinition of subject in selector `' + selector.toString() + '`');
  7553        return false;
  7554      }
  7555  
  7556      selector.currentSubject = query;
  7557      var topQ = selector[selector.length - 1];
  7558      var topChk = topQ.checks[0];
  7559      var topType = topChk == null ? null : topChk.type;
  7560  
  7561      if (topType === Type.DIRECTED_EDGE) {
  7562        // directed edge with subject on the target
  7563        // change to target node check
  7564        topChk.type = Type.NODE_TARGET;
  7565      } else if (topType === Type.UNDIRECTED_EDGE) {
  7566        // undirected edge with subject on the second node
  7567        // change to neighbor check
  7568        topChk.type = Type.NODE_NEIGHBOR;
  7569        topChk.node = topChk.nodes[1]; // second node is subject
  7570  
  7571        topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
  7572  
  7573        topChk.nodes = null;
  7574      }
  7575    }
  7576  }];
  7577  exprs.forEach(function (e) {
  7578    return e.regexObj = new RegExp('^' + e.regex);
  7579  });
  7580  
  7581  /**
  7582   * Of all the expressions, find the first match in the remaining text.
  7583   * @param {string} remaining The remaining text to parse
  7584   * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
  7585   */
  7586  
  7587  var consumeExpr = function consumeExpr(remaining) {
  7588    var expr;
  7589    var match;
  7590    var name;
  7591  
  7592    for (var j = 0; j < exprs.length; j++) {
  7593      var e = exprs[j];
  7594      var n = e.name;
  7595      var m = remaining.match(e.regexObj);
  7596  
  7597      if (m != null) {
  7598        match = m;
  7599        expr = e;
  7600        name = n;
  7601        var consumed = m[0];
  7602        remaining = remaining.substring(consumed.length);
  7603        break; // we've consumed one expr, so we can return now
  7604      }
  7605    }
  7606  
  7607    return {
  7608      expr: expr,
  7609      match: match,
  7610      name: name,
  7611      remaining: remaining
  7612    };
  7613  };
  7614  /**
  7615   * Consume all the leading whitespace
  7616   * @param {string} remaining The text to consume
  7617   * @returns The text with the leading whitespace removed
  7618   */
  7619  
  7620  
  7621  var consumeWhitespace = function consumeWhitespace(remaining) {
  7622    var match = remaining.match(/^\s+/);
  7623  
  7624    if (match) {
  7625      var consumed = match[0];
  7626      remaining = remaining.substring(consumed.length);
  7627    }
  7628  
  7629    return remaining;
  7630  };
  7631  /**
  7632   * Parse the string and store the parsed representation in the Selector.
  7633   * @param {string} selector The selector string
  7634   * @returns `true` if the selector was successfully parsed, `false` otherwise
  7635   */
  7636  
  7637  
  7638  var parse = function parse(selector) {
  7639    var self = this;
  7640    var remaining = self.inputText = selector;
  7641    var currentQuery = self[0] = newQuery();
  7642    self.length = 1;
  7643    remaining = consumeWhitespace(remaining); // get rid of leading whitespace
  7644  
  7645    for (;;) {
  7646      var exprInfo = consumeExpr(remaining);
  7647  
  7648      if (exprInfo.expr == null) {
  7649        warn('The selector `' + selector + '`is invalid');
  7650        return false;
  7651      } else {
  7652        var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
  7653  
  7654        var ret = exprInfo.expr.populate(self, currentQuery, args);
  7655  
  7656        if (ret === false) {
  7657          return false; // exit if population failed
  7658        } else if (ret != null) {
  7659          currentQuery = ret; // change the current query to be filled if the expr specifies
  7660        }
  7661      }
  7662  
  7663      remaining = exprInfo.remaining; // we're done when there's nothing left to parse
  7664  
  7665      if (remaining.match(/^\s*$/)) {
  7666        break;
  7667      }
  7668    }
  7669  
  7670    var lastQ = self[self.length - 1];
  7671  
  7672    if (self.currentSubject != null) {
  7673      lastQ.subject = self.currentSubject;
  7674    }
  7675  
  7676    lastQ.edgeCount = self.edgeCount;
  7677    lastQ.compoundCount = self.compoundCount;
  7678  
  7679    for (var i = 0; i < self.length; i++) {
  7680      var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
  7681  
  7682      if (q.compoundCount > 0 && q.edgeCount > 0) {
  7683        warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
  7684        return false;
  7685      }
  7686  
  7687      if (q.edgeCount > 1) {
  7688        warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
  7689        return false;
  7690      } else if (q.edgeCount === 1) {
  7691        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.');
  7692      }
  7693    }
  7694  
  7695    return true; // success
  7696  };
  7697  /**
  7698   * Get the selector represented as a string.  This value uses default formatting,
  7699   * so things like spacing may differ from the input text passed to the constructor.
  7700   * @returns {string} The selector string
  7701   */
  7702  
  7703  
  7704  var toString = function toString() {
  7705    if (this.toStringCache != null) {
  7706      return this.toStringCache;
  7707    }
  7708  
  7709    var clean = function clean(obj) {
  7710      if (obj == null) {
  7711        return '';
  7712      } else {
  7713        return obj;
  7714      }
  7715    };
  7716  
  7717    var cleanVal = function cleanVal(val) {
  7718      if (string(val)) {
  7719        return '"' + val + '"';
  7720      } else {
  7721        return clean(val);
  7722      }
  7723    };
  7724  
  7725    var space = function space(val) {
  7726      return ' ' + val + ' ';
  7727    };
  7728  
  7729    var checkToString = function checkToString(check, subject) {
  7730      var type = check.type,
  7731          value = check.value;
  7732  
  7733      switch (type) {
  7734        case Type.GROUP:
  7735          {
  7736            var group = clean(value);
  7737            return group.substring(0, group.length - 1);
  7738          }
  7739  
  7740        case Type.DATA_COMPARE:
  7741          {
  7742            var field = check.field,
  7743                operator = check.operator;
  7744            return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
  7745          }
  7746  
  7747        case Type.DATA_BOOL:
  7748          {
  7749            var _operator = check.operator,
  7750                _field = check.field;
  7751            return '[' + clean(_operator) + _field + ']';
  7752          }
  7753  
  7754        case Type.DATA_EXIST:
  7755          {
  7756            var _field2 = check.field;
  7757            return '[' + _field2 + ']';
  7758          }
  7759  
  7760        case Type.META_COMPARE:
  7761          {
  7762            var _operator2 = check.operator,
  7763                _field3 = check.field;
  7764            return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
  7765          }
  7766  
  7767        case Type.STATE:
  7768          {
  7769            return value;
  7770          }
  7771  
  7772        case Type.ID:
  7773          {
  7774            return '#' + value;
  7775          }
  7776  
  7777        case Type.CLASS:
  7778          {
  7779            return '.' + value;
  7780          }
  7781  
  7782        case Type.PARENT:
  7783        case Type.CHILD:
  7784          {
  7785            return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
  7786          }
  7787  
  7788        case Type.ANCESTOR:
  7789        case Type.DESCENDANT:
  7790          {
  7791            return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
  7792          }
  7793  
  7794        case Type.COMPOUND_SPLIT:
  7795          {
  7796            var lhs = queryToString(check.left, subject);
  7797            var sub = queryToString(check.subject, subject);
  7798            var rhs = queryToString(check.right, subject);
  7799            return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
  7800          }
  7801  
  7802        case Type.TRUE:
  7803          {
  7804            return '';
  7805          }
  7806      }
  7807    };
  7808  
  7809    var queryToString = function queryToString(query, subject) {
  7810      return query.checks.reduce(function (str, chk, i) {
  7811        return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
  7812      }, '');
  7813    };
  7814  
  7815    var str = '';
  7816  
  7817    for (var i = 0; i < this.length; i++) {
  7818      var query = this[i];
  7819      str += queryToString(query, query.subject);
  7820  
  7821      if (this.length > 1 && i < this.length - 1) {
  7822        str += ', ';
  7823      }
  7824    }
  7825  
  7826    this.toStringCache = str;
  7827    return str;
  7828  };
  7829  var parse$1 = {
  7830    parse: parse,
  7831    toString: toString
  7832  };
  7833  
  7834  var valCmp = function valCmp(fieldVal, operator, value) {
  7835    var matches;
  7836    var isFieldStr = string(fieldVal);
  7837    var isFieldNum = number(fieldVal);
  7838    var isValStr = string(value);
  7839    var fieldStr, valStr;
  7840    var caseInsensitive = false;
  7841    var notExpr = false;
  7842    var isIneqCmp = false;
  7843  
  7844    if (operator.indexOf('!') >= 0) {
  7845      operator = operator.replace('!', '');
  7846      notExpr = true;
  7847    }
  7848  
  7849    if (operator.indexOf('@') >= 0) {
  7850      operator = operator.replace('@', '');
  7851      caseInsensitive = true;
  7852    }
  7853  
  7854    if (isFieldStr || isValStr || caseInsensitive) {
  7855      fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
  7856      valStr = '' + value;
  7857    } // if we're doing a case insensitive comparison, then we're using a STRING comparison
  7858    // even if we're comparing numbers
  7859  
  7860  
  7861    if (caseInsensitive) {
  7862      fieldVal = fieldStr = fieldStr.toLowerCase();
  7863      value = valStr = valStr.toLowerCase();
  7864    }
  7865  
  7866    switch (operator) {
  7867      case '*=':
  7868        matches = fieldStr.indexOf(valStr) >= 0;
  7869        break;
  7870  
  7871      case '$=':
  7872        matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
  7873        break;
  7874  
  7875      case '^=':
  7876        matches = fieldStr.indexOf(valStr) === 0;
  7877        break;
  7878  
  7879      case '=':
  7880        matches = fieldVal === value;
  7881        break;
  7882  
  7883      case '>':
  7884        isIneqCmp = true;
  7885        matches = fieldVal > value;
  7886        break;
  7887  
  7888      case '>=':
  7889        isIneqCmp = true;
  7890        matches = fieldVal >= value;
  7891        break;
  7892  
  7893      case '<':
  7894        isIneqCmp = true;
  7895        matches = fieldVal < value;
  7896        break;
  7897  
  7898      case '<=':
  7899        isIneqCmp = true;
  7900        matches = fieldVal <= value;
  7901        break;
  7902  
  7903      default:
  7904        matches = false;
  7905        break;
  7906    } // apply the not op, but null vals for inequalities should always stay non-matching
  7907  
  7908  
  7909    if (notExpr && (fieldVal != null || !isIneqCmp)) {
  7910      matches = !matches;
  7911    }
  7912  
  7913    return matches;
  7914  };
  7915  var boolCmp = function boolCmp(fieldVal, operator) {
  7916    switch (operator) {
  7917      case '?':
  7918        return fieldVal ? true : false;
  7919  
  7920      case '!':
  7921        return fieldVal ? false : true;
  7922  
  7923      case '^':
  7924        return fieldVal === undefined;
  7925    }
  7926  };
  7927  var existCmp = function existCmp(fieldVal) {
  7928    return fieldVal !== undefined;
  7929  };
  7930  var data = function data(ele, field) {
  7931    return ele.data(field);
  7932  };
  7933  var meta = function meta(ele, field) {
  7934    return ele[field]();
  7935  };
  7936  
  7937  /** A lookup of `match(check, ele)` functions by `Type` int */
  7938  
  7939  var match = [];
  7940  /**
  7941   * Returns whether the query matches for the element
  7942   * @param query The `{ type, value, ... }` query object
  7943   * @param ele The element to compare against
  7944  */
  7945  
  7946  var matches = function matches(query, ele) {
  7947    return query.checks.every(function (chk) {
  7948      return match[chk.type](chk, ele);
  7949    });
  7950  };
  7951  
  7952  match[Type.GROUP] = function (check, ele) {
  7953    var group = check.value;
  7954    return group === '*' || group === ele.group();
  7955  };
  7956  
  7957  match[Type.STATE] = function (check, ele) {
  7958    var stateSelector = check.value;
  7959    return stateSelectorMatches(stateSelector, ele);
  7960  };
  7961  
  7962  match[Type.ID] = function (check, ele) {
  7963    var id = check.value;
  7964    return ele.id() === id;
  7965  };
  7966  
  7967  match[Type.CLASS] = function (check, ele) {
  7968    var cls = check.value;
  7969    return ele.hasClass(cls);
  7970  };
  7971  
  7972  match[Type.META_COMPARE] = function (check, ele) {
  7973    var field = check.field,
  7974        operator = check.operator,
  7975        value = check.value;
  7976    return valCmp(meta(ele, field), operator, value);
  7977  };
  7978  
  7979  match[Type.DATA_COMPARE] = function (check, ele) {
  7980    var field = check.field,
  7981        operator = check.operator,
  7982        value = check.value;
  7983    return valCmp(data(ele, field), operator, value);
  7984  };
  7985  
  7986  match[Type.DATA_BOOL] = function (check, ele) {
  7987    var field = check.field,
  7988        operator = check.operator;
  7989    return boolCmp(data(ele, field), operator);
  7990  };
  7991  
  7992  match[Type.DATA_EXIST] = function (check, ele) {
  7993    var field = check.field,
  7994        operator = check.operator;
  7995    return existCmp(data(ele, field));
  7996  };
  7997  
  7998  match[Type.UNDIRECTED_EDGE] = function (check, ele) {
  7999    var qA = check.nodes[0];
  8000    var qB = check.nodes[1];
  8001    var src = ele.source();
  8002    var tgt = ele.target();
  8003    return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
  8004  };
  8005  
  8006  match[Type.NODE_NEIGHBOR] = function (check, ele) {
  8007    return matches(check.node, ele) && ele.neighborhood().some(function (n) {
  8008      return n.isNode() && matches(check.neighbor, n);
  8009    });
  8010  };
  8011  
  8012  match[Type.DIRECTED_EDGE] = function (check, ele) {
  8013    return matches(check.source, ele.source()) && matches(check.target, ele.target());
  8014  };
  8015  
  8016  match[Type.NODE_SOURCE] = function (check, ele) {
  8017    return matches(check.source, ele) && ele.outgoers().some(function (n) {
  8018      return n.isNode() && matches(check.target, n);
  8019    });
  8020  };
  8021  
  8022  match[Type.NODE_TARGET] = function (check, ele) {
  8023    return matches(check.target, ele) && ele.incomers().some(function (n) {
  8024      return n.isNode() && matches(check.source, n);
  8025    });
  8026  };
  8027  
  8028  match[Type.CHILD] = function (check, ele) {
  8029    return matches(check.child, ele) && matches(check.parent, ele.parent());
  8030  };
  8031  
  8032  match[Type.PARENT] = function (check, ele) {
  8033    return matches(check.parent, ele) && ele.children().some(function (c) {
  8034      return matches(check.child, c);
  8035    });
  8036  };
  8037  
  8038  match[Type.DESCENDANT] = function (check, ele) {
  8039    return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
  8040      return matches(check.ancestor, a);
  8041    });
  8042  };
  8043  
  8044  match[Type.ANCESTOR] = function (check, ele) {
  8045    return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
  8046      return matches(check.descendant, d);
  8047    });
  8048  };
  8049  
  8050  match[Type.COMPOUND_SPLIT] = function (check, ele) {
  8051    return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
  8052  };
  8053  
  8054  match[Type.TRUE] = function () {
  8055    return true;
  8056  };
  8057  
  8058  match[Type.COLLECTION] = function (check, ele) {
  8059    var collection = check.value;
  8060    return collection.has(ele);
  8061  };
  8062  
  8063  match[Type.FILTER] = function (check, ele) {
  8064    var filter = check.value;
  8065    return filter(ele);
  8066  };
  8067  
  8068  var filter = function filter(collection) {
  8069    var self = this; // for 1 id #foo queries, just get the element
  8070  
  8071    if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
  8072      return collection.getElementById(self[0].checks[0].value).collection();
  8073    }
  8074  
  8075    var selectorFunction = function selectorFunction(element) {
  8076      for (var j = 0; j < self.length; j++) {
  8077        var query = self[j];
  8078  
  8079        if (matches(query, element)) {
  8080          return true;
  8081        }
  8082      }
  8083  
  8084      return false;
  8085    };
  8086  
  8087    if (self.text() == null) {
  8088      selectorFunction = function selectorFunction() {
  8089        return true;
  8090      };
  8091    }
  8092  
  8093    return collection.filter(selectorFunction);
  8094  }; // filter
  8095  // does selector match a single element?
  8096  
  8097  
  8098  var matches$1 = function matches$1(ele) {
  8099    var self = this;
  8100  
  8101    for (var j = 0; j < self.length; j++) {
  8102      var query = self[j];
  8103  
  8104      if (matches(query, ele)) {
  8105        return true;
  8106      }
  8107    }
  8108  
  8109    return false;
  8110  }; // matches
  8111  
  8112  
  8113  var matching = {
  8114    matches: matches$1,
  8115    filter: filter
  8116  };
  8117  
  8118  var Selector = function Selector(selector) {
  8119    this.inputText = selector;
  8120    this.currentSubject = null;
  8121    this.compoundCount = 0;
  8122    this.edgeCount = 0;
  8123    this.length = 0;
  8124  
  8125    if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
  8126      this.addQuery({
  8127        checks: [{
  8128          type: Type.COLLECTION,
  8129          value: selector.collection()
  8130        }]
  8131      });
  8132    } else if (fn(selector)) {
  8133      this.addQuery({
  8134        checks: [{
  8135          type: Type.FILTER,
  8136          value: selector
  8137        }]
  8138      });
  8139    } else if (string(selector)) {
  8140      if (!this.parse(selector)) {
  8141        this.invalid = true;
  8142      }
  8143    } else {
  8144      error('A selector must be created from a string; found ');
  8145    }
  8146  };
  8147  
  8148  var selfn = Selector.prototype;
  8149  [parse$1, matching].forEach(function (p) {
  8150    return extend(selfn, p);
  8151  });
  8152  
  8153  selfn.text = function () {
  8154    return this.inputText;
  8155  };
  8156  
  8157  selfn.size = function () {
  8158    return this.length;
  8159  };
  8160  
  8161  selfn.eq = function (i) {
  8162    return this[i];
  8163  };
  8164  
  8165  selfn.sameText = function (otherSel) {
  8166    return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
  8167  };
  8168  
  8169  selfn.addQuery = function (q) {
  8170    this[this.length++] = q;
  8171  };
  8172  
  8173  selfn.selector = selfn.toString;
  8174  
  8175  var elesfn$f = {
  8176    allAre: function allAre(selector) {
  8177      var selObj = new Selector(selector);
  8178      return this.every(function (ele) {
  8179        return selObj.matches(ele);
  8180      });
  8181    },
  8182    is: function is(selector) {
  8183      var selObj = new Selector(selector);
  8184      return this.some(function (ele) {
  8185        return selObj.matches(ele);
  8186      });
  8187    },
  8188    some: function some(fn, thisArg) {
  8189      for (var i = 0; i < this.length; i++) {
  8190        var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
  8191  
  8192        if (ret) {
  8193          return true;
  8194        }
  8195      }
  8196  
  8197      return false;
  8198    },
  8199    every: function every(fn, thisArg) {
  8200      for (var i = 0; i < this.length; i++) {
  8201        var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
  8202  
  8203        if (!ret) {
  8204          return false;
  8205        }
  8206      }
  8207  
  8208      return true;
  8209    },
  8210    same: function same(collection) {
  8211      // cheap collection ref check
  8212      if (this === collection) {
  8213        return true;
  8214      }
  8215  
  8216      collection = this.cy().collection(collection);
  8217      var thisLength = this.length;
  8218      var collectionLength = collection.length; // cheap length check
  8219  
  8220      if (thisLength !== collectionLength) {
  8221        return false;
  8222      } // cheap element ref check
  8223  
  8224  
  8225      if (thisLength === 1) {
  8226        return this[0] === collection[0];
  8227      }
  8228  
  8229      return this.every(function (ele) {
  8230        return collection.hasElementWithId(ele.id());
  8231      });
  8232    },
  8233    anySame: function anySame(collection) {
  8234      collection = this.cy().collection(collection);
  8235      return this.some(function (ele) {
  8236        return collection.hasElementWithId(ele.id());
  8237      });
  8238    },
  8239    allAreNeighbors: function allAreNeighbors(collection) {
  8240      collection = this.cy().collection(collection);
  8241      var nhood = this.neighborhood();
  8242      return collection.every(function (ele) {
  8243        return nhood.hasElementWithId(ele.id());
  8244      });
  8245    },
  8246    contains: function contains(collection) {
  8247      collection = this.cy().collection(collection);
  8248      var self = this;
  8249      return collection.every(function (ele) {
  8250        return self.hasElementWithId(ele.id());
  8251      });
  8252    }
  8253  };
  8254  elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
  8255  elesfn$f.has = elesfn$f.contains;
  8256  elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
  8257  
  8258  var cache = function cache(fn, name) {
  8259    return function traversalCache(arg1, arg2, arg3, arg4) {
  8260      var selectorOrEles = arg1;
  8261      var eles = this;
  8262      var key;
  8263  
  8264      if (selectorOrEles == null) {
  8265        key = '';
  8266      } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
  8267        key = selectorOrEles.id();
  8268      }
  8269  
  8270      if (eles.length === 1 && key) {
  8271        var _p = eles[0]._private;
  8272        var tch = _p.traversalCache = _p.traversalCache || {};
  8273        var ch = tch[name] = tch[name] || [];
  8274        var hash = hashString(key);
  8275        var cacheHit = ch[hash];
  8276  
  8277        if (cacheHit) {
  8278          return cacheHit;
  8279        } else {
  8280          return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
  8281        }
  8282      } else {
  8283        return fn.call(eles, arg1, arg2, arg3, arg4);
  8284      }
  8285    };
  8286  };
  8287  
  8288  var elesfn$g = {
  8289    parent: function parent(selector) {
  8290      var parents = []; // optimisation for single ele call
  8291  
  8292      if (this.length === 1) {
  8293        var parent = this[0]._private.parent;
  8294  
  8295        if (parent) {
  8296          return parent;
  8297        }
  8298      }
  8299  
  8300      for (var i = 0; i < this.length; i++) {
  8301        var ele = this[i];
  8302        var _parent = ele._private.parent;
  8303  
  8304        if (_parent) {
  8305          parents.push(_parent);
  8306        }
  8307      }
  8308  
  8309      return this.spawn(parents, {
  8310        unique: true
  8311      }).filter(selector);
  8312    },
  8313    parents: function parents(selector) {
  8314      var parents = [];
  8315      var eles = this.parent();
  8316  
  8317      while (eles.nonempty()) {
  8318        for (var i = 0; i < eles.length; i++) {
  8319          var ele = eles[i];
  8320          parents.push(ele);
  8321        }
  8322  
  8323        eles = eles.parent();
  8324      }
  8325  
  8326      return this.spawn(parents, {
  8327        unique: true
  8328      }).filter(selector);
  8329    },
  8330    commonAncestors: function commonAncestors(selector) {
  8331      var ancestors;
  8332  
  8333      for (var i = 0; i < this.length; i++) {
  8334        var ele = this[i];
  8335        var parents = ele.parents();
  8336        ancestors = ancestors || parents;
  8337        ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
  8338      }
  8339  
  8340      return ancestors.filter(selector);
  8341    },
  8342    orphans: function orphans(selector) {
  8343      return this.stdFilter(function (ele) {
  8344        return ele.isOrphan();
  8345      }).filter(selector);
  8346    },
  8347    nonorphans: function nonorphans(selector) {
  8348      return this.stdFilter(function (ele) {
  8349        return ele.isChild();
  8350      }).filter(selector);
  8351    },
  8352    children: cache(function (selector) {
  8353      var children = [];
  8354  
  8355      for (var i = 0; i < this.length; i++) {
  8356        var ele = this[i];
  8357        var eleChildren = ele._private.children;
  8358  
  8359        for (var j = 0; j < eleChildren.length; j++) {
  8360          children.push(eleChildren[j]);
  8361        }
  8362      }
  8363  
  8364      return this.spawn(children, {
  8365        unique: true
  8366      }).filter(selector);
  8367    }, 'children'),
  8368    siblings: function siblings(selector) {
  8369      return this.parent().children().not(this).filter(selector);
  8370    },
  8371    isParent: function isParent() {
  8372      var ele = this[0];
  8373  
  8374      if (ele) {
  8375        return ele.isNode() && ele._private.children.length !== 0;
  8376      }
  8377    },
  8378    isChildless: function isChildless() {
  8379      var ele = this[0];
  8380  
  8381      if (ele) {
  8382        return ele.isNode() && ele._private.children.length === 0;
  8383      }
  8384    },
  8385    isChild: function isChild() {
  8386      var ele = this[0];
  8387  
  8388      if (ele) {
  8389        return ele.isNode() && ele._private.parent != null;
  8390      }
  8391    },
  8392    isOrphan: function isOrphan() {
  8393      var ele = this[0];
  8394  
  8395      if (ele) {
  8396        return ele.isNode() && ele._private.parent == null;
  8397      }
  8398    },
  8399    descendants: function descendants(selector) {
  8400      var elements = [];
  8401  
  8402      function add(eles) {
  8403        for (var i = 0; i < eles.length; i++) {
  8404          var ele = eles[i];
  8405          elements.push(ele);
  8406  
  8407          if (ele.children().nonempty()) {
  8408            add(ele.children());
  8409          }
  8410        }
  8411      }
  8412  
  8413      add(this.children());
  8414      return this.spawn(elements, {
  8415        unique: true
  8416      }).filter(selector);
  8417    }
  8418  };
  8419  
  8420  function forEachCompound(eles, fn, includeSelf, recursiveStep) {
  8421    var q = [];
  8422    var did = new Set$1();
  8423    var cy = eles.cy();
  8424    var hasCompounds = cy.hasCompoundNodes();
  8425  
  8426    for (var i = 0; i < eles.length; i++) {
  8427      var ele = eles[i];
  8428  
  8429      if (includeSelf) {
  8430        q.push(ele);
  8431      } else if (hasCompounds) {
  8432        recursiveStep(q, did, ele);
  8433      }
  8434    }
  8435  
  8436    while (q.length > 0) {
  8437      var _ele = q.shift();
  8438  
  8439      fn(_ele);
  8440      did.add(_ele.id());
  8441  
  8442      if (hasCompounds) {
  8443        recursiveStep(q, did, _ele);
  8444      }
  8445    }
  8446  
  8447    return eles;
  8448  }
  8449  
  8450  function addChildren(q, did, ele) {
  8451    if (ele.isParent()) {
  8452      var children = ele._private.children;
  8453  
  8454      for (var i = 0; i < children.length; i++) {
  8455        var child = children[i];
  8456  
  8457        if (!did.has(child.id())) {
  8458          q.push(child);
  8459        }
  8460      }
  8461    }
  8462  } // very efficient version of eles.add( eles.descendants() ).forEach()
  8463  // for internal use
  8464  
  8465  
  8466  elesfn$g.forEachDown = function (fn) {
  8467    var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  8468    return forEachCompound(this, fn, includeSelf, addChildren);
  8469  };
  8470  
  8471  function addParent(q, did, ele) {
  8472    if (ele.isChild()) {
  8473      var parent = ele._private.parent;
  8474  
  8475      if (!did.has(parent.id())) {
  8476        q.push(parent);
  8477      }
  8478    }
  8479  }
  8480  
  8481  elesfn$g.forEachUp = function (fn) {
  8482    var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  8483    return forEachCompound(this, fn, includeSelf, addParent);
  8484  };
  8485  
  8486  function addParentAndChildren(q, did, ele) {
  8487    addParent(q, did, ele);
  8488    addChildren(q, did, ele);
  8489  }
  8490  
  8491  elesfn$g.forEachUpAndDown = function (fn) {
  8492    var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  8493    return forEachCompound(this, fn, includeSelf, addParentAndChildren);
  8494  }; // aliases
  8495  
  8496  
  8497  elesfn$g.ancestors = elesfn$g.parents;
  8498  
  8499  var fn$1, elesfn$h;
  8500  fn$1 = elesfn$h = {
  8501    data: define$3.data({
  8502      field: 'data',
  8503      bindingEvent: 'data',
  8504      allowBinding: true,
  8505      allowSetting: true,
  8506      settingEvent: 'data',
  8507      settingTriggersEvent: true,
  8508      triggerFnName: 'trigger',
  8509      allowGetting: true,
  8510      immutableKeys: {
  8511        'id': true,
  8512        'source': true,
  8513        'target': true,
  8514        'parent': true
  8515      },
  8516      updateStyle: true
  8517    }),
  8518    removeData: define$3.removeData({
  8519      field: 'data',
  8520      event: 'data',
  8521      triggerFnName: 'trigger',
  8522      triggerEvent: true,
  8523      immutableKeys: {
  8524        'id': true,
  8525        'source': true,
  8526        'target': true,
  8527        'parent': true
  8528      },
  8529      updateStyle: true
  8530    }),
  8531    scratch: define$3.data({
  8532      field: 'scratch',
  8533      bindingEvent: 'scratch',
  8534      allowBinding: true,
  8535      allowSetting: true,
  8536      settingEvent: 'scratch',
  8537      settingTriggersEvent: true,
  8538      triggerFnName: 'trigger',
  8539      allowGetting: true,
  8540      updateStyle: true
  8541    }),
  8542    removeScratch: define$3.removeData({
  8543      field: 'scratch',
  8544      event: 'scratch',
  8545      triggerFnName: 'trigger',
  8546      triggerEvent: true,
  8547      updateStyle: true
  8548    }),
  8549    rscratch: define$3.data({
  8550      field: 'rscratch',
  8551      allowBinding: false,
  8552      allowSetting: true,
  8553      settingTriggersEvent: false,
  8554      allowGetting: true
  8555    }),
  8556    removeRscratch: define$3.removeData({
  8557      field: 'rscratch',
  8558      triggerEvent: false
  8559    }),
  8560    id: function id() {
  8561      var ele = this[0];
  8562  
  8563      if (ele) {
  8564        return ele._private.data.id;
  8565      }
  8566    }
  8567  }; // aliases
  8568  
  8569  fn$1.attr = fn$1.data;
  8570  fn$1.removeAttr = fn$1.removeData;
  8571  var data$1 = elesfn$h;
  8572  
  8573  var elesfn$i = {};
  8574  
  8575  function defineDegreeFunction(callback) {
  8576    return function (includeLoops) {
  8577      var self = this;
  8578  
  8579      if (includeLoops === undefined) {
  8580        includeLoops = true;
  8581      }
  8582  
  8583      if (self.length === 0) {
  8584        return;
  8585      }
  8586  
  8587      if (self.isNode() && !self.removed()) {
  8588        var degree = 0;
  8589        var node = self[0];
  8590        var connectedEdges = node._private.edges;
  8591  
  8592        for (var i = 0; i < connectedEdges.length; i++) {
  8593          var edge = connectedEdges[i];
  8594  
  8595          if (!includeLoops && edge.isLoop()) {
  8596            continue;
  8597          }
  8598  
  8599          degree += callback(node, edge);
  8600        }
  8601  
  8602        return degree;
  8603      } else {
  8604        return;
  8605      }
  8606    };
  8607  }
  8608  
  8609  extend(elesfn$i, {
  8610    degree: defineDegreeFunction(function (node, edge) {
  8611      if (edge.source().same(edge.target())) {
  8612        return 2;
  8613      } else {
  8614        return 1;
  8615      }
  8616    }),
  8617    indegree: defineDegreeFunction(function (node, edge) {
  8618      if (edge.target().same(node)) {
  8619        return 1;
  8620      } else {
  8621        return 0;
  8622      }
  8623    }),
  8624    outdegree: defineDegreeFunction(function (node, edge) {
  8625      if (edge.source().same(node)) {
  8626        return 1;
  8627      } else {
  8628        return 0;
  8629      }
  8630    })
  8631  });
  8632  
  8633  function defineDegreeBoundsFunction(degreeFn, callback) {
  8634    return function (includeLoops) {
  8635      var ret;
  8636      var nodes = this.nodes();
  8637  
  8638      for (var i = 0; i < nodes.length; i++) {
  8639        var ele = nodes[i];
  8640        var degree = ele[degreeFn](includeLoops);
  8641  
  8642        if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
  8643          ret = degree;
  8644        }
  8645      }
  8646  
  8647      return ret;
  8648    };
  8649  }
  8650  
  8651  extend(elesfn$i, {
  8652    minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
  8653      return degree < min;
  8654    }),
  8655    maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
  8656      return degree > max;
  8657    }),
  8658    minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
  8659      return degree < min;
  8660    }),
  8661    maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
  8662      return degree > max;
  8663    }),
  8664    minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
  8665      return degree < min;
  8666    }),
  8667    maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
  8668      return degree > max;
  8669    })
  8670  });
  8671  extend(elesfn$i, {
  8672    totalDegree: function totalDegree(includeLoops) {
  8673      var total = 0;
  8674      var nodes = this.nodes();
  8675  
  8676      for (var i = 0; i < nodes.length; i++) {
  8677        total += nodes[i].degree(includeLoops);
  8678      }
  8679  
  8680      return total;
  8681    }
  8682  });
  8683  
  8684  var fn$2, elesfn$j;
  8685  
  8686  var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
  8687    for (var i = 0; i < eles.length; i++) {
  8688      var ele = eles[i];
  8689  
  8690      if (!ele.locked()) {
  8691        var oldPos = ele._private.position;
  8692        var delta = {
  8693          x: newPos.x != null ? newPos.x - oldPos.x : 0,
  8694          y: newPos.y != null ? newPos.y - oldPos.y : 0
  8695        };
  8696  
  8697        if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
  8698          ele.children().shift(delta, silent);
  8699        }
  8700  
  8701        ele.shiftCachedBoundingBox(delta);
  8702      }
  8703    }
  8704  };
  8705  
  8706  var positionDef = {
  8707    field: 'position',
  8708    bindingEvent: 'position',
  8709    allowBinding: true,
  8710    allowSetting: true,
  8711    settingEvent: 'position',
  8712    settingTriggersEvent: true,
  8713    triggerFnName: 'emitAndNotify',
  8714    allowGetting: true,
  8715    validKeys: ['x', 'y'],
  8716    beforeGet: function beforeGet(ele) {
  8717      ele.updateCompoundBounds();
  8718    },
  8719    beforeSet: function beforeSet(eles, newPos) {
  8720      beforePositionSet(eles, newPos, false);
  8721    },
  8722    onSet: function onSet(eles) {
  8723      eles.dirtyCompoundBoundsCache();
  8724    },
  8725    canSet: function canSet(ele) {
  8726      return !ele.locked();
  8727    }
  8728  };
  8729  fn$2 = elesfn$j = {
  8730    position: define$3.data(positionDef),
  8731    // position but no notification to renderer
  8732    silentPosition: define$3.data(extend({}, positionDef, {
  8733      allowBinding: false,
  8734      allowSetting: true,
  8735      settingTriggersEvent: false,
  8736      allowGetting: false,
  8737      beforeSet: function beforeSet(eles, newPos) {
  8738        beforePositionSet(eles, newPos, true);
  8739      }
  8740    })),
  8741    positions: function positions(pos, silent) {
  8742      if (plainObject(pos)) {
  8743        if (silent) {
  8744          this.silentPosition(pos);
  8745        } else {
  8746          this.position(pos);
  8747        }
  8748      } else if (fn(pos)) {
  8749        var _fn = pos;
  8750        var cy = this.cy();
  8751        cy.startBatch();
  8752  
  8753        for (var i = 0; i < this.length; i++) {
  8754          var ele = this[i];
  8755  
  8756          var _pos = void 0;
  8757  
  8758          if (_pos = _fn(ele, i)) {
  8759            if (silent) {
  8760              ele.silentPosition(_pos);
  8761            } else {
  8762              ele.position(_pos);
  8763            }
  8764          }
  8765        }
  8766  
  8767        cy.endBatch();
  8768      }
  8769  
  8770      return this; // chaining
  8771    },
  8772    silentPositions: function silentPositions(pos) {
  8773      return this.positions(pos, true);
  8774    },
  8775    shift: function shift(dim, val, silent) {
  8776      var delta;
  8777  
  8778      if (plainObject(dim)) {
  8779        delta = {
  8780          x: number(dim.x) ? dim.x : 0,
  8781          y: number(dim.y) ? dim.y : 0
  8782        };
  8783        silent = val;
  8784      } else if (string(dim) && number(val)) {
  8785        delta = {
  8786          x: 0,
  8787          y: 0
  8788        };
  8789        delta[dim] = val;
  8790      }
  8791  
  8792      if (delta != null) {
  8793        var cy = this.cy();
  8794        cy.startBatch();
  8795  
  8796        for (var i = 0; i < this.length; i++) {
  8797          var ele = this[i];
  8798          var pos = ele.position();
  8799          var newPos = {
  8800            x: pos.x + delta.x,
  8801            y: pos.y + delta.y
  8802          };
  8803  
  8804          if (silent) {
  8805            ele.silentPosition(newPos);
  8806          } else {
  8807            ele.position(newPos);
  8808          }
  8809        }
  8810  
  8811        cy.endBatch();
  8812      }
  8813  
  8814      return this;
  8815    },
  8816    silentShift: function silentShift(dim, val) {
  8817      if (plainObject(dim)) {
  8818        this.shift(dim, true);
  8819      } else if (string(dim) && number(val)) {
  8820        this.shift(dim, val, true);
  8821      }
  8822  
  8823      return this;
  8824    },
  8825    // get/set the rendered (i.e. on screen) positon of the element
  8826    renderedPosition: function renderedPosition(dim, val) {
  8827      var ele = this[0];
  8828      var cy = this.cy();
  8829      var zoom = cy.zoom();
  8830      var pan = cy.pan();
  8831      var rpos = plainObject(dim) ? dim : undefined;
  8832      var setting = rpos !== undefined || val !== undefined && string(dim);
  8833  
  8834      if (ele && ele.isNode()) {
  8835        // must have an element and must be a node to return position
  8836        if (setting) {
  8837          for (var i = 0; i < this.length; i++) {
  8838            var _ele = this[i];
  8839  
  8840            if (val !== undefined) {
  8841              // set one dimension
  8842              _ele.position(dim, (val - pan[dim]) / zoom);
  8843            } else if (rpos !== undefined) {
  8844              // set whole position
  8845              _ele.position(renderedToModelPosition(rpos, zoom, pan));
  8846            }
  8847          }
  8848        } else {
  8849          // getting
  8850          var pos = ele.position();
  8851          rpos = modelToRenderedPosition(pos, zoom, pan);
  8852  
  8853          if (dim === undefined) {
  8854            // then return the whole rendered position
  8855            return rpos;
  8856          } else {
  8857            // then return the specified dimension
  8858            return rpos[dim];
  8859          }
  8860        }
  8861      } else if (!setting) {
  8862        return undefined; // for empty collection case
  8863      }
  8864  
  8865      return this; // chaining
  8866    },
  8867    // get/set the position relative to the parent
  8868    relativePosition: function relativePosition(dim, val) {
  8869      var ele = this[0];
  8870      var cy = this.cy();
  8871      var ppos = plainObject(dim) ? dim : undefined;
  8872      var setting = ppos !== undefined || val !== undefined && string(dim);
  8873      var hasCompoundNodes = cy.hasCompoundNodes();
  8874  
  8875      if (ele && ele.isNode()) {
  8876        // must have an element and must be a node to return position
  8877        if (setting) {
  8878          for (var i = 0; i < this.length; i++) {
  8879            var _ele2 = this[i];
  8880            var parent = hasCompoundNodes ? _ele2.parent() : null;
  8881            var hasParent = parent && parent.length > 0;
  8882            var relativeToParent = hasParent;
  8883  
  8884            if (hasParent) {
  8885              parent = parent[0];
  8886            }
  8887  
  8888            var origin = relativeToParent ? parent.position() : {
  8889              x: 0,
  8890              y: 0
  8891            };
  8892  
  8893            if (val !== undefined) {
  8894              // set one dimension
  8895              _ele2.position(dim, val + origin[dim]);
  8896            } else if (ppos !== undefined) {
  8897              // set whole position
  8898              _ele2.position({
  8899                x: ppos.x + origin.x,
  8900                y: ppos.y + origin.y
  8901              });
  8902            }
  8903          }
  8904        } else {
  8905          // getting
  8906          var pos = ele.position();
  8907  
  8908          var _parent = hasCompoundNodes ? ele.parent() : null;
  8909  
  8910          var _hasParent = _parent && _parent.length > 0;
  8911  
  8912          var _relativeToParent = _hasParent;
  8913  
  8914          if (_hasParent) {
  8915            _parent = _parent[0];
  8916          }
  8917  
  8918          var _origin = _relativeToParent ? _parent.position() : {
  8919            x: 0,
  8920            y: 0
  8921          };
  8922  
  8923          ppos = {
  8924            x: pos.x - _origin.x,
  8925            y: pos.y - _origin.y
  8926          };
  8927  
  8928          if (dim === undefined) {
  8929            // then return the whole rendered position
  8930            return ppos;
  8931          } else {
  8932            // then return the specified dimension
  8933            return ppos[dim];
  8934          }
  8935        }
  8936      } else if (!setting) {
  8937        return undefined; // for empty collection case
  8938      }
  8939  
  8940      return this; // chaining
  8941    }
  8942  }; // aliases
  8943  
  8944  fn$2.modelPosition = fn$2.point = fn$2.position;
  8945  fn$2.modelPositions = fn$2.points = fn$2.positions;
  8946  fn$2.renderedPoint = fn$2.renderedPosition;
  8947  fn$2.relativePoint = fn$2.relativePosition;
  8948  var position = elesfn$j;
  8949  
  8950  var fn$3, elesfn$k;
  8951  fn$3 = elesfn$k = {};
  8952  
  8953  elesfn$k.renderedBoundingBox = function (options) {
  8954    var bb = this.boundingBox(options);
  8955    var cy = this.cy();
  8956    var zoom = cy.zoom();
  8957    var pan = cy.pan();
  8958    var x1 = bb.x1 * zoom + pan.x;
  8959    var x2 = bb.x2 * zoom + pan.x;
  8960    var y1 = bb.y1 * zoom + pan.y;
  8961    var y2 = bb.y2 * zoom + pan.y;
  8962    return {
  8963      x1: x1,
  8964      x2: x2,
  8965      y1: y1,
  8966      y2: y2,
  8967      w: x2 - x1,
  8968      h: y2 - y1
  8969    };
  8970  };
  8971  
  8972  elesfn$k.dirtyCompoundBoundsCache = function () {
  8973    var cy = this.cy();
  8974  
  8975    if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
  8976      return this;
  8977    }
  8978  
  8979    this.forEachUp(function (ele) {
  8980      if (ele.isParent()) {
  8981        var _p = ele._private;
  8982        _p.compoundBoundsClean = false;
  8983        _p.bbCache = null;
  8984        ele.emitAndNotify('bounds');
  8985      }
  8986    });
  8987    return this;
  8988  };
  8989  
  8990  elesfn$k.updateCompoundBounds = function () {
  8991    var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  8992    var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
  8993  
  8994    if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
  8995      return this;
  8996    } // save cycles when batching -- but bounds will be stale (or not exist yet)
  8997  
  8998  
  8999    if (!force && cy.batching()) {
  9000      return this;
  9001    }
  9002  
  9003    function update(parent) {
  9004      if (!parent.isParent()) {
  9005        return;
  9006      }
  9007  
  9008      var _p = parent._private;
  9009      var children = parent.children();
  9010      var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
  9011      var min = {
  9012        width: {
  9013          val: parent.pstyle('min-width').pfValue,
  9014          left: parent.pstyle('min-width-bias-left'),
  9015          right: parent.pstyle('min-width-bias-right')
  9016        },
  9017        height: {
  9018          val: parent.pstyle('min-height').pfValue,
  9019          top: parent.pstyle('min-height-bias-top'),
  9020          bottom: parent.pstyle('min-height-bias-bottom')
  9021        }
  9022      };
  9023      var bb = children.boundingBox({
  9024        includeLabels: includeLabels,
  9025        includeOverlays: false,
  9026        // updating the compound bounds happens outside of the regular
  9027        // cache cycle (i.e. before fired events)
  9028        useCache: false
  9029      });
  9030      var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
  9031  
  9032      if (bb.w === 0 || bb.h === 0) {
  9033        bb = {
  9034          w: parent.pstyle('width').pfValue,
  9035          h: parent.pstyle('height').pfValue
  9036        };
  9037        bb.x1 = pos.x - bb.w / 2;
  9038        bb.x2 = pos.x + bb.w / 2;
  9039        bb.y1 = pos.y - bb.h / 2;
  9040        bb.y2 = pos.y + bb.h / 2;
  9041      }
  9042  
  9043      function computeBiasValues(propDiff, propBias, propBiasComplement) {
  9044        var biasDiff = 0;
  9045        var biasComplementDiff = 0;
  9046        var biasTotal = propBias + propBiasComplement;
  9047  
  9048        if (propDiff > 0 && biasTotal > 0) {
  9049          biasDiff = propBias / biasTotal * propDiff;
  9050          biasComplementDiff = propBiasComplement / biasTotal * propDiff;
  9051        }
  9052  
  9053        return {
  9054          biasDiff: biasDiff,
  9055          biasComplementDiff: biasComplementDiff
  9056        };
  9057      }
  9058  
  9059      function computePaddingValues(width, height, paddingObject, relativeTo) {
  9060        // Assuming percentage is number from 0 to 1
  9061        if (paddingObject.units === '%') {
  9062          switch (relativeTo) {
  9063            case 'width':
  9064              return width > 0 ? paddingObject.pfValue * width : 0;
  9065  
  9066            case 'height':
  9067              return height > 0 ? paddingObject.pfValue * height : 0;
  9068  
  9069            case 'average':
  9070              return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
  9071  
  9072            case 'min':
  9073              return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
  9074  
  9075            case 'max':
  9076              return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
  9077  
  9078            default:
  9079              return 0;
  9080          }
  9081        } else if (paddingObject.units === 'px') {
  9082          return paddingObject.pfValue;
  9083        } else {
  9084          return 0;
  9085        }
  9086      }
  9087  
  9088      var leftVal = min.width.left.value;
  9089  
  9090      if (min.width.left.units === 'px' && min.width.val > 0) {
  9091        leftVal = leftVal * 100 / min.width.val;
  9092      }
  9093  
  9094      var rightVal = min.width.right.value;
  9095  
  9096      if (min.width.right.units === 'px' && min.width.val > 0) {
  9097        rightVal = rightVal * 100 / min.width.val;
  9098      }
  9099  
  9100      var topVal = min.height.top.value;
  9101  
  9102      if (min.height.top.units === 'px' && min.height.val > 0) {
  9103        topVal = topVal * 100 / min.height.val;
  9104      }
  9105  
  9106      var bottomVal = min.height.bottom.value;
  9107  
  9108      if (min.height.bottom.units === 'px' && min.height.val > 0) {
  9109        bottomVal = bottomVal * 100 / min.height.val;
  9110      }
  9111  
  9112      var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
  9113      var diffLeft = widthBiasDiffs.biasDiff;
  9114      var diffRight = widthBiasDiffs.biasComplementDiff;
  9115      var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
  9116      var diffTop = heightBiasDiffs.biasDiff;
  9117      var diffBottom = heightBiasDiffs.biasComplementDiff;
  9118      _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
  9119      _p.autoWidth = Math.max(bb.w, min.width.val);
  9120      pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
  9121      _p.autoHeight = Math.max(bb.h, min.height.val);
  9122      pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
  9123    }
  9124  
  9125    for (var i = 0; i < this.length; i++) {
  9126      var ele = this[i];
  9127      var _p = ele._private;
  9128  
  9129      if (!_p.compoundBoundsClean) {
  9130        update(ele);
  9131  
  9132        if (!cy.batching()) {
  9133          _p.compoundBoundsClean = true;
  9134        }
  9135      }
  9136    }
  9137  
  9138    return this;
  9139  };
  9140  
  9141  var noninf = function noninf(x) {
  9142    if (x === Infinity || x === -Infinity) {
  9143      return 0;
  9144    }
  9145  
  9146    return x;
  9147  };
  9148  
  9149  var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
  9150    // don't update with zero area boxes
  9151    if (x2 - x1 === 0 || y2 - y1 === 0) {
  9152      return;
  9153    } // don't update with null dim
  9154  
  9155  
  9156    if (x1 == null || y1 == null || x2 == null || y2 == null) {
  9157      return;
  9158    }
  9159  
  9160    b.x1 = x1 < b.x1 ? x1 : b.x1;
  9161    b.x2 = x2 > b.x2 ? x2 : b.x2;
  9162    b.y1 = y1 < b.y1 ? y1 : b.y1;
  9163    b.y2 = y2 > b.y2 ? y2 : b.y2;
  9164    b.w = b.x2 - b.x1;
  9165    b.h = b.y2 - b.y1;
  9166  };
  9167  
  9168  var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
  9169    if (b2 == null) {
  9170      return b;
  9171    }
  9172  
  9173    return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
  9174  };
  9175  
  9176  var prefixedProperty = function prefixedProperty(obj, field, prefix) {
  9177    return getPrefixedProperty(obj, field, prefix);
  9178  };
  9179  
  9180  var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
  9181    if (ele.cy().headless()) {
  9182      return;
  9183    }
  9184  
  9185    var _p = ele._private;
  9186    var rstyle = _p.rstyle;
  9187    var halfArW = rstyle.arrowWidth / 2;
  9188    var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
  9189    var x;
  9190    var y;
  9191  
  9192    if (arrowType !== 'none') {
  9193      if (prefix === 'source') {
  9194        x = rstyle.srcX;
  9195        y = rstyle.srcY;
  9196      } else if (prefix === 'target') {
  9197        x = rstyle.tgtX;
  9198        y = rstyle.tgtY;
  9199      } else {
  9200        x = rstyle.midX;
  9201        y = rstyle.midY;
  9202      } // always store the individual arrow bounds
  9203  
  9204  
  9205      var bbs = _p.arrowBounds = _p.arrowBounds || {};
  9206      var bb = bbs[prefix] = bbs[prefix] || {};
  9207      bb.x1 = x - halfArW;
  9208      bb.y1 = y - halfArW;
  9209      bb.x2 = x + halfArW;
  9210      bb.y2 = y + halfArW;
  9211      bb.w = bb.x2 - bb.x1;
  9212      bb.h = bb.y2 - bb.y1;
  9213      expandBoundingBox(bb, 1);
  9214      updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
  9215    }
  9216  };
  9217  
  9218  var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
  9219    if (ele.cy().headless()) {
  9220      return;
  9221    }
  9222  
  9223    var prefixDash;
  9224  
  9225    if (prefix) {
  9226      prefixDash = prefix + '-';
  9227    } else {
  9228      prefixDash = '';
  9229    }
  9230  
  9231    var _p = ele._private;
  9232    var rstyle = _p.rstyle;
  9233    var label = ele.pstyle(prefixDash + 'label').strValue;
  9234  
  9235    if (label) {
  9236      var halign = ele.pstyle('text-halign');
  9237      var valign = ele.pstyle('text-valign');
  9238      var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
  9239      var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
  9240      var labelX = prefixedProperty(rstyle, 'labelX', prefix);
  9241      var labelY = prefixedProperty(rstyle, 'labelY', prefix);
  9242      var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
  9243      var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
  9244      var isEdge = ele.isEdge();
  9245      var rotation = ele.pstyle(prefixDash + 'text-rotation');
  9246      var outlineWidth = ele.pstyle('text-outline-width').pfValue;
  9247      var borderWidth = ele.pstyle('text-border-width').pfValue;
  9248      var halfBorderWidth = borderWidth / 2;
  9249      var padding = ele.pstyle('text-background-padding').pfValue;
  9250      var lh = labelHeight;
  9251      var lw = labelWidth;
  9252      var lw_2 = lw / 2;
  9253      var lh_2 = lh / 2;
  9254      var lx1, lx2, ly1, ly2;
  9255  
  9256      if (isEdge) {
  9257        lx1 = labelX - lw_2;
  9258        lx2 = labelX + lw_2;
  9259        ly1 = labelY - lh_2;
  9260        ly2 = labelY + lh_2;
  9261      } else {
  9262        switch (halign.value) {
  9263          case 'left':
  9264            lx1 = labelX - lw;
  9265            lx2 = labelX;
  9266            break;
  9267  
  9268          case 'center':
  9269            lx1 = labelX - lw_2;
  9270            lx2 = labelX + lw_2;
  9271            break;
  9272  
  9273          case 'right':
  9274            lx1 = labelX;
  9275            lx2 = labelX + lw;
  9276            break;
  9277        }
  9278  
  9279        switch (valign.value) {
  9280          case 'top':
  9281            ly1 = labelY - lh;
  9282            ly2 = labelY;
  9283            break;
  9284  
  9285          case 'center':
  9286            ly1 = labelY - lh_2;
  9287            ly2 = labelY + lh_2;
  9288            break;
  9289  
  9290          case 'bottom':
  9291            ly1 = labelY;
  9292            ly2 = labelY + lh;
  9293            break;
  9294        }
  9295      } // shift by margin and expand by outline and border
  9296  
  9297  
  9298      lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding;
  9299      lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding;
  9300      ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding;
  9301      ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding; // always store the unrotated label bounds separately
  9302  
  9303      var bbPrefix = prefix || 'main';
  9304      var bbs = _p.labelBounds;
  9305      var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
  9306      bb.x1 = lx1;
  9307      bb.y1 = ly1;
  9308      bb.x2 = lx2;
  9309      bb.y2 = ly2;
  9310      bb.w = lx2 - lx1;
  9311      bb.h = ly2 - ly1;
  9312      expandBoundingBox(bb, 1); // expand to work around browser dimension inaccuracies
  9313  
  9314      var isAutorotate = isEdge && rotation.strValue === 'autorotate';
  9315      var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
  9316  
  9317      if (isAutorotate || isPfValue) {
  9318        var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
  9319        var cos = Math.cos(theta);
  9320        var sin = Math.sin(theta); // rotation point (default value for center-center)
  9321  
  9322        var xo = (lx1 + lx2) / 2;
  9323        var yo = (ly1 + ly2) / 2;
  9324  
  9325        if (!isEdge) {
  9326          switch (halign.value) {
  9327            case 'left':
  9328              xo = lx2;
  9329              break;
  9330  
  9331            case 'right':
  9332              xo = lx1;
  9333              break;
  9334          }
  9335  
  9336          switch (valign.value) {
  9337            case 'top':
  9338              yo = ly2;
  9339              break;
  9340  
  9341            case 'bottom':
  9342              yo = ly1;
  9343              break;
  9344          }
  9345        }
  9346  
  9347        var rotate = function rotate(x, y) {
  9348          x = x - xo;
  9349          y = y - yo;
  9350          return {
  9351            x: x * cos - y * sin + xo,
  9352            y: x * sin + y * cos + yo
  9353          };
  9354        };
  9355  
  9356        var px1y1 = rotate(lx1, ly1);
  9357        var px1y2 = rotate(lx1, ly2);
  9358        var px2y1 = rotate(lx2, ly1);
  9359        var px2y2 = rotate(lx2, ly2);
  9360        lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
  9361        lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
  9362        ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
  9363        ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
  9364      }
  9365  
  9366      var bbPrefixRot = bbPrefix + 'Rot';
  9367      var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
  9368      bbRot.x1 = lx1;
  9369      bbRot.y1 = ly1;
  9370      bbRot.x2 = lx2;
  9371      bbRot.y2 = ly2;
  9372      bbRot.w = lx2 - lx1;
  9373      bbRot.h = ly2 - ly1;
  9374      updateBounds(bounds, lx1, ly1, lx2, ly2);
  9375      updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
  9376    }
  9377  
  9378    return bounds;
  9379  }; // get the bounding box of the elements (in raw model position)
  9380  
  9381  
  9382  var boundingBoxImpl = function boundingBoxImpl(ele, options) {
  9383    var cy = ele._private.cy;
  9384    var styleEnabled = cy.styleEnabled();
  9385    var headless = cy.headless();
  9386    var bounds = makeBoundingBox();
  9387    var _p = ele._private;
  9388    var isNode = ele.isNode();
  9389    var isEdge = ele.isEdge();
  9390    var ex1, ex2, ey1, ey2; // extrema of body / lines
  9391  
  9392    var x, y; // node pos
  9393  
  9394    var rstyle = _p.rstyle;
  9395    var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
  9396    // (other factors like width values will be considered later in this function anyway)
  9397  
  9398    var isDisplayed = function isDisplayed(ele) {
  9399      return ele.pstyle('display').value !== 'none';
  9400    };
  9401  
  9402    var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
  9403    && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
  9404  
  9405    if (displayed) {
  9406      // displayed suffices, since we will find zero area eles anyway
  9407      var overlayOpacity = 0;
  9408      var overlayPadding = 0;
  9409  
  9410      if (styleEnabled && options.includeOverlays) {
  9411        overlayOpacity = ele.pstyle('overlay-opacity').value;
  9412  
  9413        if (overlayOpacity !== 0) {
  9414          overlayPadding = ele.pstyle('overlay-padding').value;
  9415        }
  9416      }
  9417  
  9418      var w = 0;
  9419      var wHalf = 0;
  9420  
  9421      if (styleEnabled) {
  9422        w = ele.pstyle('width').pfValue;
  9423        wHalf = w / 2;
  9424      }
  9425  
  9426      if (isNode && options.includeNodes) {
  9427        var pos = ele.position();
  9428        x = pos.x;
  9429        y = pos.y;
  9430  
  9431        var _w = ele.outerWidth();
  9432  
  9433        var halfW = _w / 2;
  9434        var h = ele.outerHeight();
  9435        var halfH = h / 2; // handle node dimensions
  9436        /////////////////////////
  9437  
  9438        ex1 = x - halfW;
  9439        ex2 = x + halfW;
  9440        ey1 = y - halfH;
  9441        ey2 = y + halfH;
  9442        updateBounds(bounds, ex1, ey1, ex2, ey2);
  9443      } else if (isEdge && options.includeEdges) {
  9444        if (styleEnabled && !headless) {
  9445          var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
  9446          //////////////////////////////////////////////
  9447  
  9448          ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
  9449          ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
  9450          ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
  9451          ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
  9452  
  9453          ex1 -= wHalf;
  9454          ex2 += wHalf;
  9455          ey1 -= wHalf;
  9456          ey2 += wHalf;
  9457          updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
  9458          ////////////////
  9459  
  9460          if (curveStyle === 'haystack') {
  9461            var hpts = rstyle.haystackPts;
  9462  
  9463            if (hpts && hpts.length === 2) {
  9464              ex1 = hpts[0].x;
  9465              ey1 = hpts[0].y;
  9466              ex2 = hpts[1].x;
  9467              ey2 = hpts[1].y;
  9468  
  9469              if (ex1 > ex2) {
  9470                var temp = ex1;
  9471                ex1 = ex2;
  9472                ex2 = temp;
  9473              }
  9474  
  9475              if (ey1 > ey2) {
  9476                var _temp = ey1;
  9477                ey1 = ey2;
  9478                ey2 = _temp;
  9479              }
  9480  
  9481              updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
  9482            }
  9483          } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
  9484            var pts;
  9485  
  9486            switch (curveStyle) {
  9487              case 'bezier':
  9488              case 'unbundled-bezier':
  9489                pts = rstyle.bezierPts;
  9490                break;
  9491  
  9492              case 'segments':
  9493              case 'taxi':
  9494                pts = rstyle.linePts;
  9495                break;
  9496            }
  9497  
  9498            if (pts != null) {
  9499              for (var j = 0; j < pts.length; j++) {
  9500                var pt = pts[j];
  9501                ex1 = pt.x - wHalf;
  9502                ex2 = pt.x + wHalf;
  9503                ey1 = pt.y - wHalf;
  9504                ey2 = pt.y + wHalf;
  9505                updateBounds(bounds, ex1, ey1, ex2, ey2);
  9506              }
  9507            }
  9508          } // bezier-like or segment-like edge
  9509  
  9510        } else {
  9511          // headless or style disabled
  9512          // fallback on source and target positions
  9513          //////////////////////////////////////////
  9514          var n1 = ele.source();
  9515          var n1pos = n1.position();
  9516          var n2 = ele.target();
  9517          var n2pos = n2.position();
  9518          ex1 = n1pos.x;
  9519          ex2 = n2pos.x;
  9520          ey1 = n1pos.y;
  9521          ey2 = n2pos.y;
  9522  
  9523          if (ex1 > ex2) {
  9524            var _temp2 = ex1;
  9525            ex1 = ex2;
  9526            ex2 = _temp2;
  9527          }
  9528  
  9529          if (ey1 > ey2) {
  9530            var _temp3 = ey1;
  9531            ey1 = ey2;
  9532            ey2 = _temp3;
  9533          } // take into account edge width
  9534  
  9535  
  9536          ex1 -= wHalf;
  9537          ex2 += wHalf;
  9538          ey1 -= wHalf;
  9539          ey2 += wHalf;
  9540          updateBounds(bounds, ex1, ey1, ex2, ey2);
  9541        } // headless or style disabled
  9542  
  9543      } // edges
  9544      // handle edge arrow size
  9545      /////////////////////////
  9546  
  9547  
  9548      if (styleEnabled && options.includeEdges && isEdge) {
  9549        updateBoundsFromArrow(bounds, ele, 'mid-source');
  9550        updateBoundsFromArrow(bounds, ele, 'mid-target');
  9551        updateBoundsFromArrow(bounds, ele, 'source');
  9552        updateBoundsFromArrow(bounds, ele, 'target');
  9553      } // ghost
  9554      ////////
  9555  
  9556  
  9557      if (styleEnabled) {
  9558        var ghost = ele.pstyle('ghost').value === 'yes';
  9559  
  9560        if (ghost) {
  9561          var gx = ele.pstyle('ghost-offset-x').pfValue;
  9562          var gy = ele.pstyle('ghost-offset-y').pfValue;
  9563          updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
  9564        }
  9565      } // always store the body bounds separately from the labels
  9566  
  9567  
  9568      var bbBody = _p.bodyBounds = _p.bodyBounds || {};
  9569      assignBoundingBox(bbBody, bounds);
  9570      expandBoundingBoxSides(bbBody, manualExpansion);
  9571      expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
  9572      // overlay
  9573      //////////
  9574  
  9575      if (styleEnabled) {
  9576        ex1 = bounds.x1;
  9577        ex2 = bounds.x2;
  9578        ey1 = bounds.y1;
  9579        ey2 = bounds.y2;
  9580        updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding);
  9581      } // always store the body bounds separately from the labels
  9582  
  9583  
  9584      var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
  9585      assignBoundingBox(bbOverlay, bounds);
  9586      expandBoundingBoxSides(bbOverlay, manualExpansion);
  9587      expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
  9588      // handle label dimensions
  9589      //////////////////////////
  9590  
  9591      var bbLabels = _p.labelBounds = _p.labelBounds || {};
  9592  
  9593      if (bbLabels.all != null) {
  9594        clearBoundingBox(bbLabels.all);
  9595      } else {
  9596        bbLabels.all = makeBoundingBox();
  9597      }
  9598  
  9599      if (styleEnabled && options.includeLabels) {
  9600        if (options.includeMainLabels) {
  9601          updateBoundsFromLabel(bounds, ele, null);
  9602        }
  9603  
  9604        if (isEdge) {
  9605          if (options.includeSourceLabels) {
  9606            updateBoundsFromLabel(bounds, ele, 'source');
  9607          }
  9608  
  9609          if (options.includeTargetLabels) {
  9610            updateBoundsFromLabel(bounds, ele, 'target');
  9611          }
  9612        }
  9613      } // style enabled for labels
  9614  
  9615    } // if displayed
  9616  
  9617  
  9618    bounds.x1 = noninf(bounds.x1);
  9619    bounds.y1 = noninf(bounds.y1);
  9620    bounds.x2 = noninf(bounds.x2);
  9621    bounds.y2 = noninf(bounds.y2);
  9622    bounds.w = noninf(bounds.x2 - bounds.x1);
  9623    bounds.h = noninf(bounds.y2 - bounds.y1);
  9624  
  9625    if (bounds.w > 0 && bounds.h > 0 && displayed) {
  9626      expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
  9627  
  9628      expandBoundingBox(bounds, 1);
  9629    }
  9630  
  9631    return bounds;
  9632  };
  9633  
  9634  var getKey = function getKey(opts) {
  9635    var i = 0;
  9636  
  9637    var tf = function tf(val) {
  9638      return (val ? 1 : 0) << i++;
  9639    };
  9640  
  9641    var key = 0;
  9642    key += tf(opts.incudeNodes);
  9643    key += tf(opts.includeEdges);
  9644    key += tf(opts.includeLabels);
  9645    key += tf(opts.includeMainLabels);
  9646    key += tf(opts.includeSourceLabels);
  9647    key += tf(opts.includeTargetLabels);
  9648    key += tf(opts.includeOverlays);
  9649    return key;
  9650  };
  9651  
  9652  var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
  9653    if (ele.isEdge()) {
  9654      var p1 = ele.source().position();
  9655      var p2 = ele.target().position();
  9656  
  9657      var r = function r(x) {
  9658        return Math.round(x);
  9659      };
  9660  
  9661      return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
  9662    } else {
  9663      return 0;
  9664    }
  9665  };
  9666  
  9667  var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
  9668    var _p = ele._private;
  9669    var bb;
  9670    var isEdge = ele.isEdge();
  9671    var key = opts == null ? defBbOptsKey : getKey(opts);
  9672    var usingDefOpts = key === defBbOptsKey;
  9673    var currPosKey = getBoundingBoxPosKey(ele);
  9674    var isPosKeySame = _p.bbCachePosKey === currPosKey;
  9675    var useCache = opts.useCache && isPosKeySame;
  9676  
  9677    var isDirty = function isDirty(ele) {
  9678      return ele._private.bbCache == null;
  9679    };
  9680  
  9681    var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
  9682  
  9683    if (needRecalc) {
  9684      if (!isPosKeySame) {
  9685        ele.recalculateRenderedStyle();
  9686      }
  9687  
  9688      bb = boundingBoxImpl(ele, defBbOpts);
  9689      _p.bbCache = bb;
  9690      _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
  9691      _p.bbCachePosKey = currPosKey;
  9692    } else {
  9693      bb = _p.bbCache;
  9694    }
  9695  
  9696    if (!needRecalc && (_p.bbCacheShift.x !== 0 || _p.bbCacheShift.y !== 0)) {
  9697      var shift = assignShiftToBoundingBox;
  9698      var delta = _p.bbCacheShift;
  9699  
  9700      var safeShift = function safeShift(bb, delta) {
  9701        if (bb != null) {
  9702          shift(bb, delta);
  9703        }
  9704      };
  9705  
  9706      shift(bb, delta);
  9707      var bodyBounds = _p.bodyBounds,
  9708          overlayBounds = _p.overlayBounds,
  9709          labelBounds = _p.labelBounds,
  9710          arrowBounds = _p.arrowBounds;
  9711      safeShift(bodyBounds, delta);
  9712      safeShift(overlayBounds, delta);
  9713  
  9714      if (arrowBounds != null) {
  9715        safeShift(arrowBounds.source, delta);
  9716        safeShift(arrowBounds.target, delta);
  9717        safeShift(arrowBounds['mid-source'], delta);
  9718        safeShift(arrowBounds['mid-target'], delta);
  9719      }
  9720  
  9721      if (labelBounds != null) {
  9722        safeShift(labelBounds.main, delta);
  9723        safeShift(labelBounds.all, delta);
  9724        safeShift(labelBounds.source, delta);
  9725        safeShift(labelBounds.target, delta);
  9726      }
  9727    } // always reset the shift, because we either applied the shift or cleared it by doing a fresh recalc
  9728  
  9729  
  9730    _p.bbCacheShift.x = _p.bbCacheShift.y = 0; // not using def opts => need to build up bb from combination of sub bbs
  9731  
  9732    if (!usingDefOpts) {
  9733      var isNode = ele.isNode();
  9734      bb = makeBoundingBox();
  9735  
  9736      if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
  9737        if (opts.includeOverlays) {
  9738          updateBoundsFromBox(bb, _p.overlayBounds);
  9739        } else {
  9740          updateBoundsFromBox(bb, _p.bodyBounds);
  9741        }
  9742      }
  9743  
  9744      if (opts.includeLabels) {
  9745        if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
  9746          updateBoundsFromBox(bb, _p.labelBounds.all);
  9747        } else {
  9748          if (opts.includeMainLabels) {
  9749            updateBoundsFromBox(bb, _p.labelBounds.mainRot);
  9750          }
  9751  
  9752          if (opts.includeSourceLabels) {
  9753            updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
  9754          }
  9755  
  9756          if (opts.includeTargetLabels) {
  9757            updateBoundsFromBox(bb, _p.labelBounds.targetRot);
  9758          }
  9759        }
  9760      }
  9761  
  9762      bb.w = bb.x2 - bb.x1;
  9763      bb.h = bb.y2 - bb.y1;
  9764    }
  9765  
  9766    return bb;
  9767  };
  9768  
  9769  var defBbOpts = {
  9770    includeNodes: true,
  9771    includeEdges: true,
  9772    includeLabels: true,
  9773    includeMainLabels: true,
  9774    includeSourceLabels: true,
  9775    includeTargetLabels: true,
  9776    includeOverlays: true,
  9777    useCache: true
  9778  };
  9779  var defBbOptsKey = getKey(defBbOpts);
  9780  var filledBbOpts = defaults(defBbOpts);
  9781  
  9782  elesfn$k.boundingBox = function (options) {
  9783    var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
  9784    // specified s.t. the cache is used, so check for this case to make it faster by
  9785    // avoiding the overhead of the rest of the function
  9786  
  9787    if (this.length === 1 && this[0]._private.bbCache != null && (options === undefined || options.useCache === undefined || options.useCache === true)) {
  9788      if (options === undefined) {
  9789        options = defBbOpts;
  9790      } else {
  9791        options = filledBbOpts(options);
  9792      }
  9793  
  9794      bounds = cachedBoundingBoxImpl(this[0], options);
  9795    } else {
  9796      bounds = makeBoundingBox();
  9797      options = options || defBbOpts;
  9798      var opts = filledBbOpts(options);
  9799      var eles = this;
  9800      var cy = eles.cy();
  9801      var styleEnabled = cy.styleEnabled();
  9802  
  9803      if (styleEnabled) {
  9804        for (var i = 0; i < eles.length; i++) {
  9805          var ele = eles[i];
  9806          var _p = ele._private;
  9807          var currPosKey = getBoundingBoxPosKey(ele);
  9808          var isPosKeySame = _p.bbCachePosKey === currPosKey;
  9809          var useCache = opts.useCache && isPosKeySame;
  9810          ele.recalculateRenderedStyle(useCache);
  9811        }
  9812      }
  9813  
  9814      this.updateCompoundBounds();
  9815  
  9816      for (var _i = 0; _i < eles.length; _i++) {
  9817        var _ele = eles[_i];
  9818        updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
  9819      }
  9820    }
  9821  
  9822    bounds.x1 = noninf(bounds.x1);
  9823    bounds.y1 = noninf(bounds.y1);
  9824    bounds.x2 = noninf(bounds.x2);
  9825    bounds.y2 = noninf(bounds.y2);
  9826    bounds.w = noninf(bounds.x2 - bounds.x1);
  9827    bounds.h = noninf(bounds.y2 - bounds.y1);
  9828    return bounds;
  9829  };
  9830  
  9831  elesfn$k.dirtyBoundingBoxCache = function () {
  9832    for (var i = 0; i < this.length; i++) {
  9833      var _p = this[i]._private;
  9834      _p.bbCache = null;
  9835      _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
  9836      _p.bbCachePosKey = null;
  9837      _p.bodyBounds = null;
  9838      _p.overlayBounds = null;
  9839      _p.labelBounds.all = null;
  9840      _p.labelBounds.source = null;
  9841      _p.labelBounds.target = null;
  9842      _p.labelBounds.main = null;
  9843      _p.labelBounds.sourceRot = null;
  9844      _p.labelBounds.targetRot = null;
  9845      _p.labelBounds.mainRot = null;
  9846      _p.arrowBounds.source = null;
  9847      _p.arrowBounds.target = null;
  9848      _p.arrowBounds['mid-source'] = null;
  9849      _p.arrowBounds['mid-target'] = null;
  9850    }
  9851  
  9852    this.emitAndNotify('bounds');
  9853    return this;
  9854  };
  9855  
  9856  elesfn$k.shiftCachedBoundingBox = function (delta) {
  9857    for (var i = 0; i < this.length; i++) {
  9858      var ele = this[i];
  9859      var _p = ele._private;
  9860      var bb = _p.bbCache;
  9861  
  9862      if (bb != null) {
  9863        _p.bbCacheShift.x += delta.x;
  9864        _p.bbCacheShift.y += delta.y;
  9865      }
  9866    }
  9867  
  9868    this.emitAndNotify('bounds');
  9869    return this;
  9870  }; // private helper to get bounding box for custom node positions
  9871  // - good for perf in certain cases but currently requires dirtying the rendered style
  9872  // - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
  9873  // - try to use for only things like discrete layouts where the node position would change anyway
  9874  
  9875  
  9876  elesfn$k.boundingBoxAt = function (fn) {
  9877    var nodes = this.nodes();
  9878    var cy = this.cy();
  9879    var hasCompoundNodes = cy.hasCompoundNodes();
  9880  
  9881    if (hasCompoundNodes) {
  9882      nodes = nodes.filter(function (node) {
  9883        return !node.isParent();
  9884      });
  9885    }
  9886  
  9887    if (plainObject(fn)) {
  9888      var obj = fn;
  9889  
  9890      fn = function fn() {
  9891        return obj;
  9892      };
  9893    }
  9894  
  9895    var storeOldPos = function storeOldPos(node, i) {
  9896      return node._private.bbAtOldPos = fn(node, i);
  9897    };
  9898  
  9899    var getOldPos = function getOldPos(node) {
  9900      return node._private.bbAtOldPos;
  9901    };
  9902  
  9903    cy.startBatch();
  9904    nodes.forEach(storeOldPos).silentPositions(fn);
  9905  
  9906    if (hasCompoundNodes) {
  9907      this.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
  9908    }
  9909  
  9910    var bb = copyBoundingBox(this.boundingBox({
  9911      useCache: false
  9912    }));
  9913    nodes.silentPositions(getOldPos);
  9914    cy.endBatch();
  9915    return bb;
  9916  };
  9917  
  9918  fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
  9919  fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
  9920  var bounds = elesfn$k;
  9921  
  9922  var fn$4, elesfn$l;
  9923  fn$4 = elesfn$l = {};
  9924  
  9925  var defineDimFns = function defineDimFns(opts) {
  9926    opts.uppercaseName = capitalize(opts.name);
  9927    opts.autoName = 'auto' + opts.uppercaseName;
  9928    opts.labelName = 'label' + opts.uppercaseName;
  9929    opts.outerName = 'outer' + opts.uppercaseName;
  9930    opts.uppercaseOuterName = capitalize(opts.outerName);
  9931  
  9932    fn$4[opts.name] = function dimImpl() {
  9933      var ele = this[0];
  9934      var _p = ele._private;
  9935      var cy = _p.cy;
  9936      var styleEnabled = cy._private.styleEnabled;
  9937  
  9938      if (ele) {
  9939        if (styleEnabled) {
  9940          if (ele.isParent()) {
  9941            ele.updateCompoundBounds();
  9942            return _p[opts.autoName] || 0;
  9943          }
  9944  
  9945          var d = ele.pstyle(opts.name);
  9946  
  9947          switch (d.strValue) {
  9948            case 'label':
  9949              ele.recalculateRenderedStyle();
  9950              return _p.rstyle[opts.labelName] || 0;
  9951  
  9952            default:
  9953              return d.pfValue;
  9954          }
  9955        } else {
  9956          return 1;
  9957        }
  9958      }
  9959    };
  9960  
  9961    fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
  9962      var ele = this[0];
  9963      var _p = ele._private;
  9964      var cy = _p.cy;
  9965      var styleEnabled = cy._private.styleEnabled;
  9966  
  9967      if (ele) {
  9968        if (styleEnabled) {
  9969          var dim = ele[opts.name]();
  9970          var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
  9971  
  9972          var padding = 2 * ele.padding();
  9973          return dim + border + padding;
  9974        } else {
  9975          return 1;
  9976        }
  9977      }
  9978    };
  9979  
  9980    fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
  9981      var ele = this[0];
  9982  
  9983      if (ele) {
  9984        var d = ele[opts.name]();
  9985        return d * this.cy().zoom();
  9986      }
  9987    };
  9988  
  9989    fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
  9990      var ele = this[0];
  9991  
  9992      if (ele) {
  9993        var od = ele[opts.outerName]();
  9994        return od * this.cy().zoom();
  9995      }
  9996    };
  9997  };
  9998  
  9999  defineDimFns({
 10000    name: 'width'
 10001  });
 10002  defineDimFns({
 10003    name: 'height'
 10004  });
 10005  
 10006  elesfn$l.padding = function () {
 10007    var ele = this[0];
 10008    var _p = ele._private;
 10009  
 10010    if (ele.isParent()) {
 10011      ele.updateCompoundBounds();
 10012  
 10013      if (_p.autoPadding !== undefined) {
 10014        return _p.autoPadding;
 10015      } else {
 10016        return ele.pstyle('padding').pfValue;
 10017      }
 10018    } else {
 10019      return ele.pstyle('padding').pfValue;
 10020    }
 10021  };
 10022  
 10023  elesfn$l.paddedHeight = function () {
 10024    var ele = this[0];
 10025    return ele.height() + 2 * ele.padding();
 10026  };
 10027  
 10028  elesfn$l.paddedWidth = function () {
 10029    var ele = this[0];
 10030    return ele.width() + 2 * ele.padding();
 10031  };
 10032  
 10033  var widthHeight = elesfn$l;
 10034  
 10035  var ifEdge = function ifEdge(ele, getValue) {
 10036    if (ele.isEdge()) {
 10037      return getValue(ele);
 10038    }
 10039  };
 10040  
 10041  var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
 10042    if (ele.isEdge()) {
 10043      var cy = ele.cy();
 10044      return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
 10045    }
 10046  };
 10047  
 10048  var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
 10049    if (ele.isEdge()) {
 10050      var cy = ele.cy();
 10051      var pan = cy.pan();
 10052      var zoom = cy.zoom();
 10053      return getPoints(ele).map(function (p) {
 10054        return modelToRenderedPosition(p, zoom, pan);
 10055      });
 10056    }
 10057  };
 10058  
 10059  var controlPoints = function controlPoints(ele) {
 10060    return ele.renderer().getControlPoints(ele);
 10061  };
 10062  
 10063  var segmentPoints = function segmentPoints(ele) {
 10064    return ele.renderer().getSegmentPoints(ele);
 10065  };
 10066  
 10067  var sourceEndpoint = function sourceEndpoint(ele) {
 10068    return ele.renderer().getSourceEndpoint(ele);
 10069  };
 10070  
 10071  var targetEndpoint = function targetEndpoint(ele) {
 10072    return ele.renderer().getTargetEndpoint(ele);
 10073  };
 10074  
 10075  var midpoint = function midpoint(ele) {
 10076    return ele.renderer().getEdgeMidpoint(ele);
 10077  };
 10078  
 10079  var pts = {
 10080    controlPoints: {
 10081      get: controlPoints,
 10082      mult: true
 10083    },
 10084    segmentPoints: {
 10085      get: segmentPoints,
 10086      mult: true
 10087    },
 10088    sourceEndpoint: {
 10089      get: sourceEndpoint
 10090    },
 10091    targetEndpoint: {
 10092      get: targetEndpoint
 10093    },
 10094    midpoint: {
 10095      get: midpoint
 10096    }
 10097  };
 10098  
 10099  var renderedName = function renderedName(name) {
 10100    return 'rendered' + name[0].toUpperCase() + name.substr(1);
 10101  };
 10102  
 10103  var edgePoints = Object.keys(pts).reduce(function (obj, name) {
 10104    var spec = pts[name];
 10105    var rName = renderedName(name);
 10106  
 10107    obj[name] = function () {
 10108      return ifEdge(this, spec.get);
 10109    };
 10110  
 10111    if (spec.mult) {
 10112      obj[rName] = function () {
 10113        return ifEdgeRenderedPositions(this, spec.get);
 10114      };
 10115    } else {
 10116      obj[rName] = function () {
 10117        return ifEdgeRenderedPosition(this, spec.get);
 10118      };
 10119    }
 10120  
 10121    return obj;
 10122  }, {});
 10123  
 10124  var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
 10125  
 10126  /*!
 10127  Event object based on jQuery events, MIT license
 10128  
 10129  https://jquery.org/license/
 10130  https://tldrlegal.com/license/mit-license
 10131  https://github.com/jquery/jquery/blob/master/src/event.js
 10132  */
 10133  var Event = function Event(src, props) {
 10134    this.recycle(src, props);
 10135  };
 10136  
 10137  function returnFalse() {
 10138    return false;
 10139  }
 10140  
 10141  function returnTrue() {
 10142    return true;
 10143  } // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
 10144  
 10145  
 10146  Event.prototype = {
 10147    instanceString: function instanceString() {
 10148      return 'event';
 10149    },
 10150    recycle: function recycle(src, props) {
 10151      this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
 10152  
 10153      if (src != null && src.preventDefault) {
 10154        // Browser Event object
 10155        this.type = src.type; // Events bubbling up the document may have been marked as prevented
 10156        // by a handler lower down the tree; reflect the correct value.
 10157  
 10158        this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
 10159      } else if (src != null && src.type) {
 10160        // Plain object containing all event details
 10161        props = src;
 10162      } else {
 10163        // Event string
 10164        this.type = src;
 10165      } // Put explicitly provided properties onto the event object
 10166  
 10167  
 10168      if (props != null) {
 10169        // more efficient to manually copy fields we use
 10170        this.originalEvent = props.originalEvent;
 10171        this.type = props.type != null ? props.type : this.type;
 10172        this.cy = props.cy;
 10173        this.target = props.target;
 10174        this.position = props.position;
 10175        this.renderedPosition = props.renderedPosition;
 10176        this.namespace = props.namespace;
 10177        this.layout = props.layout;
 10178      }
 10179  
 10180      if (this.cy != null && this.position != null && this.renderedPosition == null) {
 10181        // create a rendered position based on the passed position
 10182        var pos = this.position;
 10183        var zoom = this.cy.zoom();
 10184        var pan = this.cy.pan();
 10185        this.renderedPosition = {
 10186          x: pos.x * zoom + pan.x,
 10187          y: pos.y * zoom + pan.y
 10188        };
 10189      } // Create a timestamp if incoming event doesn't have one
 10190  
 10191  
 10192      this.timeStamp = src && src.timeStamp || Date.now();
 10193    },
 10194    preventDefault: function preventDefault() {
 10195      this.isDefaultPrevented = returnTrue;
 10196      var e = this.originalEvent;
 10197  
 10198      if (!e) {
 10199        return;
 10200      } // if preventDefault exists run it on the original event
 10201  
 10202  
 10203      if (e.preventDefault) {
 10204        e.preventDefault();
 10205      }
 10206    },
 10207    stopPropagation: function stopPropagation() {
 10208      this.isPropagationStopped = returnTrue;
 10209      var e = this.originalEvent;
 10210  
 10211      if (!e) {
 10212        return;
 10213      } // if stopPropagation exists run it on the original event
 10214  
 10215  
 10216      if (e.stopPropagation) {
 10217        e.stopPropagation();
 10218      }
 10219    },
 10220    stopImmediatePropagation: function stopImmediatePropagation() {
 10221      this.isImmediatePropagationStopped = returnTrue;
 10222      this.stopPropagation();
 10223    },
 10224    isDefaultPrevented: returnFalse,
 10225    isPropagationStopped: returnFalse,
 10226    isImmediatePropagationStopped: returnFalse
 10227  };
 10228  
 10229  var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
 10230  
 10231  var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
 10232  
 10233  var defaults$8 = {
 10234    qualifierCompare: function qualifierCompare(q1, q2) {
 10235      return q1 === q2;
 10236    },
 10237    eventMatches: function eventMatches()
 10238    /*context, listener, eventObj*/
 10239    {
 10240      return true;
 10241    },
 10242    addEventFields: function addEventFields()
 10243    /*context, evt*/
 10244    {},
 10245    callbackContext: function callbackContext(context
 10246    /*, listener, eventObj*/
 10247    ) {
 10248      return context;
 10249    },
 10250    beforeEmit: function beforeEmit()
 10251    /* context, listener, eventObj */
 10252    {},
 10253    afterEmit: function afterEmit()
 10254    /* context, listener, eventObj */
 10255    {},
 10256    bubble: function bubble()
 10257    /*context*/
 10258    {
 10259      return false;
 10260    },
 10261    parent: function parent()
 10262    /*context*/
 10263    {
 10264      return null;
 10265    },
 10266    context: null
 10267  };
 10268  var defaultsKeys = Object.keys(defaults$8);
 10269  var emptyOpts = {};
 10270  
 10271  function Emitter() {
 10272    var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
 10273    var context = arguments.length > 1 ? arguments[1] : undefined;
 10274  
 10275    // micro-optimisation vs Object.assign() -- reduces Element instantiation time
 10276    for (var i = 0; i < defaultsKeys.length; i++) {
 10277      var key = defaultsKeys[i];
 10278      this[key] = opts[key] || defaults$8[key];
 10279    }
 10280  
 10281    this.context = context || this.context;
 10282    this.listeners = [];
 10283    this.emitting = 0;
 10284  }
 10285  
 10286  var p = Emitter.prototype;
 10287  
 10288  var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
 10289    if (fn(qualifier)) {
 10290      callback = qualifier;
 10291      qualifier = null;
 10292    }
 10293  
 10294    if (confOverrides) {
 10295      if (conf == null) {
 10296        conf = confOverrides;
 10297      } else {
 10298        conf = extend({}, conf, confOverrides);
 10299      }
 10300    }
 10301  
 10302    var eventList = array(events) ? events : events.split(/\s+/);
 10303  
 10304    for (var i = 0; i < eventList.length; i++) {
 10305      var evt = eventList[i];
 10306  
 10307      if (emptyString(evt)) {
 10308        continue;
 10309      }
 10310  
 10311      var match = evt.match(eventRegex); // type[.namespace]
 10312  
 10313      if (match) {
 10314        var type = match[1];
 10315        var namespace = match[2] ? match[2] : null;
 10316        var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
 10317  
 10318        if (ret === false) {
 10319          break;
 10320        } // allow exiting early
 10321  
 10322      }
 10323    }
 10324  };
 10325  
 10326  var makeEventObj = function makeEventObj(self, obj) {
 10327    self.addEventFields(self.context, obj);
 10328    return new Event(obj.type, obj);
 10329  };
 10330  
 10331  var forEachEventObj = function forEachEventObj(self, handler, events) {
 10332    if (event(events)) {
 10333      handler(self, events);
 10334      return;
 10335    } else if (plainObject(events)) {
 10336      handler(self, makeEventObj(self, events));
 10337      return;
 10338    }
 10339  
 10340    var eventList = array(events) ? events : events.split(/\s+/);
 10341  
 10342    for (var i = 0; i < eventList.length; i++) {
 10343      var evt = eventList[i];
 10344  
 10345      if (emptyString(evt)) {
 10346        continue;
 10347      }
 10348  
 10349      var match = evt.match(eventRegex); // type[.namespace]
 10350  
 10351      if (match) {
 10352        var type = match[1];
 10353        var namespace = match[2] ? match[2] : null;
 10354        var eventObj = makeEventObj(self, {
 10355          type: type,
 10356          namespace: namespace,
 10357          target: self.context
 10358        });
 10359        handler(self, eventObj);
 10360      }
 10361    }
 10362  };
 10363  
 10364  p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
 10365    forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
 10366      if (fn(callback)) {
 10367        self.listeners.push({
 10368          event: event,
 10369          // full event string
 10370          callback: callback,
 10371          // callback to run
 10372          type: type,
 10373          // the event type (e.g. 'click')
 10374          namespace: namespace,
 10375          // the event namespace (e.g. ".foo")
 10376          qualifier: qualifier,
 10377          // a restriction on whether to match this emitter
 10378          conf: conf // additional configuration
 10379  
 10380        });
 10381      }
 10382    }, events, qualifier, callback, conf, confOverrides);
 10383    return this;
 10384  };
 10385  
 10386  p.one = function (events, qualifier, callback, conf) {
 10387    return this.on(events, qualifier, callback, conf, {
 10388      one: true
 10389    });
 10390  };
 10391  
 10392  p.removeListener = p.off = function (events, qualifier, callback, conf) {
 10393    var _this = this;
 10394  
 10395    if (this.emitting !== 0) {
 10396      this.listeners = copyArray(this.listeners);
 10397    }
 10398  
 10399    var listeners = this.listeners;
 10400  
 10401    var _loop = function _loop(i) {
 10402      var listener = listeners[i];
 10403      forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
 10404      /*, conf*/
 10405      ) {
 10406        if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
 10407          listeners.splice(i, 1);
 10408          return false;
 10409        }
 10410      }, events, qualifier, callback, conf);
 10411    };
 10412  
 10413    for (var i = listeners.length - 1; i >= 0; i--) {
 10414      _loop(i);
 10415    }
 10416  
 10417    return this;
 10418  };
 10419  
 10420  p.removeAllListeners = function () {
 10421    return this.removeListener('*');
 10422  };
 10423  
 10424  p.emit = p.trigger = function (events, extraParams, manualCallback) {
 10425    var listeners = this.listeners;
 10426    var numListenersBeforeEmit = listeners.length;
 10427    this.emitting++;
 10428  
 10429    if (!array(extraParams)) {
 10430      extraParams = [extraParams];
 10431    }
 10432  
 10433    forEachEventObj(this, function (self, eventObj) {
 10434      if (manualCallback != null) {
 10435        listeners = [{
 10436          event: eventObj.event,
 10437          type: eventObj.type,
 10438          namespace: eventObj.namespace,
 10439          callback: manualCallback
 10440        }];
 10441        numListenersBeforeEmit = listeners.length;
 10442      }
 10443  
 10444      var _loop2 = function _loop2(i) {
 10445        var listener = listeners[i];
 10446  
 10447        if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
 10448          var args = [eventObj];
 10449  
 10450          if (extraParams != null) {
 10451            push(args, extraParams);
 10452          }
 10453  
 10454          self.beforeEmit(self.context, listener, eventObj);
 10455  
 10456          if (listener.conf && listener.conf.one) {
 10457            self.listeners = self.listeners.filter(function (l) {
 10458              return l !== listener;
 10459            });
 10460          }
 10461  
 10462          var context = self.callbackContext(self.context, listener, eventObj);
 10463          var ret = listener.callback.apply(context, args);
 10464          self.afterEmit(self.context, listener, eventObj);
 10465  
 10466          if (ret === false) {
 10467            eventObj.stopPropagation();
 10468            eventObj.preventDefault();
 10469          }
 10470        } // if listener matches
 10471  
 10472      };
 10473  
 10474      for (var i = 0; i < numListenersBeforeEmit; i++) {
 10475        _loop2(i);
 10476      } // for listener
 10477  
 10478  
 10479      if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
 10480        self.parent(self.context).emit(eventObj, extraParams);
 10481      }
 10482    }, events);
 10483    this.emitting--;
 10484    return this;
 10485  };
 10486  
 10487  var emitterOptions = {
 10488    qualifierCompare: function qualifierCompare(selector1, selector2) {
 10489      if (selector1 == null || selector2 == null) {
 10490        return selector1 == null && selector2 == null;
 10491      } else {
 10492        return selector1.sameText(selector2);
 10493      }
 10494    },
 10495    eventMatches: function eventMatches(ele, listener, eventObj) {
 10496      var selector = listener.qualifier;
 10497  
 10498      if (selector != null) {
 10499        return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
 10500      }
 10501  
 10502      return true;
 10503    },
 10504    addEventFields: function addEventFields(ele, evt) {
 10505      evt.cy = ele.cy();
 10506      evt.target = ele;
 10507    },
 10508    callbackContext: function callbackContext(ele, listener, eventObj) {
 10509      return listener.qualifier != null ? eventObj.target : ele;
 10510    },
 10511    beforeEmit: function beforeEmit(context, listener
 10512    /*, eventObj*/
 10513    ) {
 10514      if (listener.conf && listener.conf.once) {
 10515        listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
 10516      }
 10517    },
 10518    bubble: function bubble() {
 10519      return true;
 10520    },
 10521    parent: function parent(ele) {
 10522      return ele.isChild() ? ele.parent() : ele.cy();
 10523    }
 10524  };
 10525  
 10526  var argSelector = function argSelector(arg) {
 10527    if (string(arg)) {
 10528      return new Selector(arg);
 10529    } else {
 10530      return arg;
 10531    }
 10532  };
 10533  
 10534  var elesfn$m = {
 10535    createEmitter: function createEmitter() {
 10536      for (var i = 0; i < this.length; i++) {
 10537        var ele = this[i];
 10538        var _p = ele._private;
 10539  
 10540        if (!_p.emitter) {
 10541          _p.emitter = new Emitter(emitterOptions, ele);
 10542        }
 10543      }
 10544  
 10545      return this;
 10546    },
 10547    emitter: function emitter() {
 10548      return this._private.emitter;
 10549    },
 10550    on: function on(events, selector, callback) {
 10551      var argSel = argSelector(selector);
 10552  
 10553      for (var i = 0; i < this.length; i++) {
 10554        var ele = this[i];
 10555        ele.emitter().on(events, argSel, callback);
 10556      }
 10557  
 10558      return this;
 10559    },
 10560    removeListener: function removeListener(events, selector, callback) {
 10561      var argSel = argSelector(selector);
 10562  
 10563      for (var i = 0; i < this.length; i++) {
 10564        var ele = this[i];
 10565        ele.emitter().removeListener(events, argSel, callback);
 10566      }
 10567  
 10568      return this;
 10569    },
 10570    removeAllListeners: function removeAllListeners() {
 10571      for (var i = 0; i < this.length; i++) {
 10572        var ele = this[i];
 10573        ele.emitter().removeAllListeners();
 10574      }
 10575  
 10576      return this;
 10577    },
 10578    one: function one(events, selector, callback) {
 10579      var argSel = argSelector(selector);
 10580  
 10581      for (var i = 0; i < this.length; i++) {
 10582        var ele = this[i];
 10583        ele.emitter().one(events, argSel, callback);
 10584      }
 10585  
 10586      return this;
 10587    },
 10588    once: function once(events, selector, callback) {
 10589      var argSel = argSelector(selector);
 10590  
 10591      for (var i = 0; i < this.length; i++) {
 10592        var ele = this[i];
 10593        ele.emitter().on(events, argSel, callback, {
 10594          once: true,
 10595          onceCollection: this
 10596        });
 10597      }
 10598    },
 10599    emit: function emit(events, extraParams) {
 10600      for (var i = 0; i < this.length; i++) {
 10601        var ele = this[i];
 10602        ele.emitter().emit(events, extraParams);
 10603      }
 10604  
 10605      return this;
 10606    },
 10607    emitAndNotify: function emitAndNotify(event, extraParams) {
 10608      // for internal use only
 10609      if (this.length === 0) {
 10610        return;
 10611      } // empty collections don't need to notify anything
 10612      // notify renderer
 10613  
 10614  
 10615      this.cy().notify(event, this);
 10616      this.emit(event, extraParams);
 10617      return this;
 10618    }
 10619  };
 10620  define$3.eventAliasesOn(elesfn$m);
 10621  
 10622  var elesfn$n = {
 10623    nodes: function nodes(selector) {
 10624      return this.filter(function (ele) {
 10625        return ele.isNode();
 10626      }).filter(selector);
 10627    },
 10628    edges: function edges(selector) {
 10629      return this.filter(function (ele) {
 10630        return ele.isEdge();
 10631      }).filter(selector);
 10632    },
 10633    // internal helper to get nodes and edges as separate collections with single iteration over elements
 10634    byGroup: function byGroup() {
 10635      var nodes = this.spawn();
 10636      var edges = this.spawn();
 10637  
 10638      for (var i = 0; i < this.length; i++) {
 10639        var ele = this[i];
 10640  
 10641        if (ele.isNode()) {
 10642          nodes.merge(ele);
 10643        } else {
 10644          edges.merge(ele);
 10645        }
 10646      }
 10647  
 10648      return {
 10649        nodes: nodes,
 10650        edges: edges
 10651      };
 10652    },
 10653    filter: function filter(_filter, thisArg) {
 10654      if (_filter === undefined) {
 10655        // check this first b/c it's the most common/performant case
 10656        return this;
 10657      } else if (string(_filter) || elementOrCollection(_filter)) {
 10658        return new Selector(_filter).filter(this);
 10659      } else if (fn(_filter)) {
 10660        var filterEles = this.spawn();
 10661        var eles = this;
 10662  
 10663        for (var i = 0; i < eles.length; i++) {
 10664          var ele = eles[i];
 10665          var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
 10666  
 10667          if (include) {
 10668            filterEles.merge(ele);
 10669          }
 10670        }
 10671  
 10672        return filterEles;
 10673      }
 10674  
 10675      return this.spawn(); // if not handled by above, give 'em an empty collection
 10676    },
 10677    not: function not(toRemove) {
 10678      if (!toRemove) {
 10679        return this;
 10680      } else {
 10681        if (string(toRemove)) {
 10682          toRemove = this.filter(toRemove);
 10683        }
 10684  
 10685        var elements = [];
 10686        var rMap = toRemove._private.map;
 10687  
 10688        for (var i = 0; i < this.length; i++) {
 10689          var element = this[i];
 10690          var remove = rMap.has(element.id());
 10691  
 10692          if (!remove) {
 10693            elements.push(element);
 10694          }
 10695        }
 10696  
 10697        return this.spawn(elements);
 10698      }
 10699    },
 10700    absoluteComplement: function absoluteComplement() {
 10701      var cy = this.cy();
 10702      return cy.mutableElements().not(this);
 10703    },
 10704    intersect: function intersect(other) {
 10705      // if a selector is specified, then filter by it instead
 10706      if (string(other)) {
 10707        var selector = other;
 10708        return this.filter(selector);
 10709      }
 10710  
 10711      var elements = [];
 10712      var col1 = this;
 10713      var col2 = other;
 10714      var col1Smaller = this.length < other.length;
 10715      var map2 = col1Smaller ? col2._private.map : col1._private.map;
 10716      var col = col1Smaller ? col1 : col2;
 10717  
 10718      for (var i = 0; i < col.length; i++) {
 10719        var id = col[i]._private.data.id;
 10720        var entry = map2.get(id);
 10721  
 10722        if (entry) {
 10723          elements.push(entry.ele);
 10724        }
 10725      }
 10726  
 10727      return this.spawn(elements);
 10728    },
 10729    xor: function xor(other) {
 10730      var cy = this._private.cy;
 10731  
 10732      if (string(other)) {
 10733        other = cy.$(other);
 10734      }
 10735  
 10736      var elements = [];
 10737      var col1 = this;
 10738      var col2 = other;
 10739  
 10740      var add = function add(col, other) {
 10741        for (var i = 0; i < col.length; i++) {
 10742          var ele = col[i];
 10743          var id = ele._private.data.id;
 10744          var inOther = other.hasElementWithId(id);
 10745  
 10746          if (!inOther) {
 10747            elements.push(ele);
 10748          }
 10749        }
 10750      };
 10751  
 10752      add(col1, col2);
 10753      add(col2, col1);
 10754      return this.spawn(elements);
 10755    },
 10756    diff: function diff(other) {
 10757      var cy = this._private.cy;
 10758  
 10759      if (string(other)) {
 10760        other = cy.$(other);
 10761      }
 10762  
 10763      var left = [];
 10764      var right = [];
 10765      var both = [];
 10766      var col1 = this;
 10767      var col2 = other;
 10768  
 10769      var add = function add(col, other, retEles) {
 10770        for (var i = 0; i < col.length; i++) {
 10771          var ele = col[i];
 10772          var id = ele._private.data.id;
 10773          var inOther = other.hasElementWithId(id);
 10774  
 10775          if (inOther) {
 10776            both.push(ele);
 10777          } else {
 10778            retEles.push(ele);
 10779          }
 10780        }
 10781      };
 10782  
 10783      add(col1, col2, left);
 10784      add(col2, col1, right);
 10785      return {
 10786        left: this.spawn(left, {
 10787          unique: true
 10788        }),
 10789        right: this.spawn(right, {
 10790          unique: true
 10791        }),
 10792        both: this.spawn(both, {
 10793          unique: true
 10794        })
 10795      };
 10796    },
 10797    add: function add(toAdd) {
 10798      var cy = this._private.cy;
 10799  
 10800      if (!toAdd) {
 10801        return this;
 10802      }
 10803  
 10804      if (string(toAdd)) {
 10805        var selector = toAdd;
 10806        toAdd = cy.mutableElements().filter(selector);
 10807      }
 10808  
 10809      var elements = [];
 10810  
 10811      for (var i = 0; i < this.length; i++) {
 10812        elements.push(this[i]);
 10813      }
 10814  
 10815      var map = this._private.map;
 10816  
 10817      for (var _i = 0; _i < toAdd.length; _i++) {
 10818        var add = !map.has(toAdd[_i].id());
 10819  
 10820        if (add) {
 10821          elements.push(toAdd[_i]);
 10822        }
 10823      }
 10824  
 10825      return this.spawn(elements);
 10826    },
 10827    // in place merge on calling collection
 10828    merge: function merge(toAdd) {
 10829      var _p = this._private;
 10830      var cy = _p.cy;
 10831  
 10832      if (!toAdd) {
 10833        return this;
 10834      }
 10835  
 10836      if (toAdd && string(toAdd)) {
 10837        var selector = toAdd;
 10838        toAdd = cy.mutableElements().filter(selector);
 10839      }
 10840  
 10841      var map = _p.map;
 10842  
 10843      for (var i = 0; i < toAdd.length; i++) {
 10844        var toAddEle = toAdd[i];
 10845        var id = toAddEle._private.data.id;
 10846        var add = !map.has(id);
 10847  
 10848        if (add) {
 10849          var index = this.length++;
 10850          this[index] = toAddEle;
 10851          map.set(id, {
 10852            ele: toAddEle,
 10853            index: index
 10854          });
 10855        } else {
 10856          // replace
 10857          var _index = map.get(id).index;
 10858          this[_index] = toAddEle;
 10859          map.set(id, {
 10860            ele: toAddEle,
 10861            index: _index
 10862          });
 10863        }
 10864      }
 10865  
 10866      return this; // chaining
 10867    },
 10868    unmergeAt: function unmergeAt(i) {
 10869      var ele = this[i];
 10870      var id = ele.id();
 10871      var _p = this._private;
 10872      var map = _p.map; // remove ele
 10873  
 10874      this[i] = undefined;
 10875      map["delete"](id);
 10876      var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
 10877  
 10878      if (this.length > 1 && !unmergedLastEle) {
 10879        var lastEleI = this.length - 1;
 10880        var lastEle = this[lastEleI];
 10881        var lastEleId = lastEle._private.data.id;
 10882        this[lastEleI] = undefined;
 10883        this[i] = lastEle;
 10884        map.set(lastEleId, {
 10885          ele: lastEle,
 10886          index: i
 10887        });
 10888      } // the collection is now 1 ele smaller
 10889  
 10890  
 10891      this.length--;
 10892      return this;
 10893    },
 10894    // remove single ele in place in calling collection
 10895    unmergeOne: function unmergeOne(ele) {
 10896      ele = ele[0];
 10897      var _p = this._private;
 10898      var id = ele._private.data.id;
 10899      var map = _p.map;
 10900      var entry = map.get(id);
 10901  
 10902      if (!entry) {
 10903        return this; // no need to remove
 10904      }
 10905  
 10906      var i = entry.index;
 10907      this.unmergeAt(i);
 10908      return this;
 10909    },
 10910    // remove eles in place on calling collection
 10911    unmerge: function unmerge(toRemove) {
 10912      var cy = this._private.cy;
 10913  
 10914      if (!toRemove) {
 10915        return this;
 10916      }
 10917  
 10918      if (toRemove && string(toRemove)) {
 10919        var selector = toRemove;
 10920        toRemove = cy.mutableElements().filter(selector);
 10921      }
 10922  
 10923      for (var i = 0; i < toRemove.length; i++) {
 10924        this.unmergeOne(toRemove[i]);
 10925      }
 10926  
 10927      return this; // chaining
 10928    },
 10929    unmergeBy: function unmergeBy(toRmFn) {
 10930      for (var i = this.length - 1; i >= 0; i--) {
 10931        var ele = this[i];
 10932  
 10933        if (toRmFn(ele)) {
 10934          this.unmergeAt(i);
 10935        }
 10936      }
 10937  
 10938      return this;
 10939    },
 10940    map: function map(mapFn, thisArg) {
 10941      var arr = [];
 10942      var eles = this;
 10943  
 10944      for (var i = 0; i < eles.length; i++) {
 10945        var ele = eles[i];
 10946        var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
 10947        arr.push(ret);
 10948      }
 10949  
 10950      return arr;
 10951    },
 10952    reduce: function reduce(fn, initialValue) {
 10953      var val = initialValue;
 10954      var eles = this;
 10955  
 10956      for (var i = 0; i < eles.length; i++) {
 10957        val = fn(val, eles[i], i, eles);
 10958      }
 10959  
 10960      return val;
 10961    },
 10962    max: function max(valFn, thisArg) {
 10963      var max = -Infinity;
 10964      var maxEle;
 10965      var eles = this;
 10966  
 10967      for (var i = 0; i < eles.length; i++) {
 10968        var ele = eles[i];
 10969        var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
 10970  
 10971        if (val > max) {
 10972          max = val;
 10973          maxEle = ele;
 10974        }
 10975      }
 10976  
 10977      return {
 10978        value: max,
 10979        ele: maxEle
 10980      };
 10981    },
 10982    min: function min(valFn, thisArg) {
 10983      var min = Infinity;
 10984      var minEle;
 10985      var eles = this;
 10986  
 10987      for (var i = 0; i < eles.length; i++) {
 10988        var ele = eles[i];
 10989        var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
 10990  
 10991        if (val < min) {
 10992          min = val;
 10993          minEle = ele;
 10994        }
 10995      }
 10996  
 10997      return {
 10998        value: min,
 10999        ele: minEle
 11000      };
 11001    }
 11002  }; // aliases
 11003  
 11004  var fn$5 = elesfn$n;
 11005  fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
 11006  fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
 11007  fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
 11008  fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
 11009  fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
 11010  fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
 11011  
 11012  var elesfn$o = {
 11013    isNode: function isNode() {
 11014      return this.group() === 'nodes';
 11015    },
 11016    isEdge: function isEdge() {
 11017      return this.group() === 'edges';
 11018    },
 11019    isLoop: function isLoop() {
 11020      return this.isEdge() && this.source()[0] === this.target()[0];
 11021    },
 11022    isSimple: function isSimple() {
 11023      return this.isEdge() && this.source()[0] !== this.target()[0];
 11024    },
 11025    group: function group() {
 11026      var ele = this[0];
 11027  
 11028      if (ele) {
 11029        return ele._private.group;
 11030      }
 11031    }
 11032  };
 11033  
 11034  /**
 11035   *  Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
 11036   *  and z-index (low to high).  These styles affect how this applies:
 11037   *
 11038   *  z-compound-depth: May be `bottom | orphan | auto | top`.  The first drawn is `bottom`, then `orphan` which is the
 11039   *      same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
 11040   *      root to leaves of the compound graph.  The last drawn is `top`.
 11041   *  z-index-compare: May be `auto | manual`.  The default value is `auto` which always draws edges under nodes.
 11042   *      `manual` ignores this convention and draws based on the `z-index` value setting.
 11043   *  z-index: An integer value that affects the relative draw order of elements.  In general, an element with a higher
 11044   *      `z-index` will be drawn on top of an element with a lower `z-index`.
 11045   */
 11046  
 11047  var zIndexSort = function zIndexSort(a, b) {
 11048    var cy = a.cy();
 11049    var hasCompoundNodes = cy.hasCompoundNodes();
 11050  
 11051    function getDepth(ele) {
 11052      var style = ele.pstyle('z-compound-depth');
 11053  
 11054      if (style.value === 'auto') {
 11055        return hasCompoundNodes ? ele.zDepth() : 0;
 11056      } else if (style.value === 'bottom') {
 11057        return -1;
 11058      } else if (style.value === 'top') {
 11059        return MAX_INT;
 11060      } // 'orphan'
 11061  
 11062  
 11063      return 0;
 11064    }
 11065  
 11066    var depthDiff = getDepth(a) - getDepth(b);
 11067  
 11068    if (depthDiff !== 0) {
 11069      return depthDiff;
 11070    }
 11071  
 11072    function getEleDepth(ele) {
 11073      var style = ele.pstyle('z-index-compare');
 11074  
 11075      if (style.value === 'auto') {
 11076        return ele.isNode() ? 1 : 0;
 11077      } // 'manual'
 11078  
 11079  
 11080      return 0;
 11081    }
 11082  
 11083    var eleDiff = getEleDepth(a) - getEleDepth(b);
 11084  
 11085    if (eleDiff !== 0) {
 11086      return eleDiff;
 11087    }
 11088  
 11089    var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
 11090  
 11091    if (zDiff !== 0) {
 11092      return zDiff;
 11093    } // compare indices in the core (order added to graph w/ last on top)
 11094  
 11095  
 11096    return a.poolIndex() - b.poolIndex();
 11097  };
 11098  
 11099  var elesfn$p = {
 11100    forEach: function forEach(fn$1, thisArg) {
 11101      if (fn(fn$1)) {
 11102        var N = this.length;
 11103  
 11104        for (var i = 0; i < N; i++) {
 11105          var ele = this[i];
 11106          var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
 11107  
 11108          if (ret === false) {
 11109            break;
 11110          } // exit each early on return false
 11111  
 11112        }
 11113      }
 11114  
 11115      return this;
 11116    },
 11117    toArray: function toArray() {
 11118      var array = [];
 11119  
 11120      for (var i = 0; i < this.length; i++) {
 11121        array.push(this[i]);
 11122      }
 11123  
 11124      return array;
 11125    },
 11126    slice: function slice(start, end) {
 11127      var array = [];
 11128      var thisSize = this.length;
 11129  
 11130      if (end == null) {
 11131        end = thisSize;
 11132      }
 11133  
 11134      if (start == null) {
 11135        start = 0;
 11136      }
 11137  
 11138      if (start < 0) {
 11139        start = thisSize + start;
 11140      }
 11141  
 11142      if (end < 0) {
 11143        end = thisSize + end;
 11144      }
 11145  
 11146      for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
 11147        array.push(this[i]);
 11148      }
 11149  
 11150      return this.spawn(array);
 11151    },
 11152    size: function size() {
 11153      return this.length;
 11154    },
 11155    eq: function eq(i) {
 11156      return this[i] || this.spawn();
 11157    },
 11158    first: function first() {
 11159      return this[0] || this.spawn();
 11160    },
 11161    last: function last() {
 11162      return this[this.length - 1] || this.spawn();
 11163    },
 11164    empty: function empty() {
 11165      return this.length === 0;
 11166    },
 11167    nonempty: function nonempty() {
 11168      return !this.empty();
 11169    },
 11170    sort: function sort(sortFn) {
 11171      if (!fn(sortFn)) {
 11172        return this;
 11173      }
 11174  
 11175      var sorted = this.toArray().sort(sortFn);
 11176      return this.spawn(sorted);
 11177    },
 11178    sortByZIndex: function sortByZIndex() {
 11179      return this.sort(zIndexSort);
 11180    },
 11181    zDepth: function zDepth() {
 11182      var ele = this[0];
 11183  
 11184      if (!ele) {
 11185        return undefined;
 11186      } // let cy = ele.cy();
 11187  
 11188  
 11189      var _p = ele._private;
 11190      var group = _p.group;
 11191  
 11192      if (group === 'nodes') {
 11193        var depth = _p.data.parent ? ele.parents().size() : 0;
 11194  
 11195        if (!ele.isParent()) {
 11196          return MAX_INT - 1; // childless nodes always on top
 11197        }
 11198  
 11199        return depth;
 11200      } else {
 11201        var src = _p.source;
 11202        var tgt = _p.target;
 11203        var srcDepth = src.zDepth();
 11204        var tgtDepth = tgt.zDepth();
 11205        return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
 11206      }
 11207    }
 11208  };
 11209  elesfn$p.each = elesfn$p.forEach;
 11210  
 11211  var defineSymbolIterator = function defineSymbolIterator() {
 11212    var typeofUndef =  "undefined" ;
 11213    var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
 11214  
 11215    if (isIteratorSupported) {
 11216      elesfn$p[Symbol.iterator] = function () {
 11217        var _this = this;
 11218  
 11219        // eslint-disable-line no-undef
 11220        var entry = {
 11221          value: undefined,
 11222          done: false
 11223        };
 11224        var i = 0;
 11225        var length = this.length;
 11226        return _defineProperty({
 11227          next: function next() {
 11228            if (i < length) {
 11229              entry.value = _this[i++];
 11230            } else {
 11231              entry.value = undefined;
 11232              entry.done = true;
 11233            }
 11234  
 11235            return entry;
 11236          }
 11237        }, Symbol.iterator, function () {
 11238          // eslint-disable-line no-undef
 11239          return this;
 11240        });
 11241      };
 11242    }
 11243  };
 11244  
 11245  defineSymbolIterator();
 11246  
 11247  var getLayoutDimensionOptions = defaults({
 11248    nodeDimensionsIncludeLabels: false
 11249  });
 11250  var elesfn$q = {
 11251    // Calculates and returns node dimensions { x, y } based on options given
 11252    layoutDimensions: function layoutDimensions(options) {
 11253      options = getLayoutDimensionOptions(options);
 11254      var dims;
 11255  
 11256      if (!this.takesUpSpace()) {
 11257        dims = {
 11258          w: 0,
 11259          h: 0
 11260        };
 11261      } else if (options.nodeDimensionsIncludeLabels) {
 11262        var bbDim = this.boundingBox();
 11263        dims = {
 11264          w: bbDim.w,
 11265          h: bbDim.h
 11266        };
 11267      } else {
 11268        dims = {
 11269          w: this.outerWidth(),
 11270          h: this.outerHeight()
 11271        };
 11272      } // sanitise the dimensions for external layouts (avoid division by zero)
 11273  
 11274  
 11275      if (dims.w === 0 || dims.h === 0) {
 11276        dims.w = dims.h = 1;
 11277      }
 11278  
 11279      return dims;
 11280    },
 11281    // using standard layout options, apply position function (w/ or w/o animation)
 11282    layoutPositions: function layoutPositions(layout, options, fn) {
 11283      var nodes = this.nodes();
 11284      var cy = this.cy();
 11285      var layoutEles = options.eles; // nodes & edges
 11286  
 11287      var getMemoizeKey = function getMemoizeKey(node) {
 11288        return node.id();
 11289      };
 11290  
 11291      var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
 11292  
 11293      layout.emit({
 11294        type: 'layoutstart',
 11295        layout: layout
 11296      });
 11297      layout.animations = [];
 11298  
 11299      var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
 11300        var center = {
 11301          x: nodesBb.x1 + nodesBb.w / 2,
 11302          y: nodesBb.y1 + nodesBb.h / 2
 11303        };
 11304        var spacingVector = {
 11305          // scale from center of bounding box (not necessarily 0,0)
 11306          x: (pos.x - center.x) * spacing,
 11307          y: (pos.y - center.y) * spacing
 11308        };
 11309        return {
 11310          x: center.x + spacingVector.x,
 11311          y: center.y + spacingVector.y
 11312        };
 11313      };
 11314  
 11315      var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
 11316  
 11317      var spacingBb = function spacingBb() {
 11318        if (!useSpacingFactor) {
 11319          return null;
 11320        }
 11321  
 11322        var bb = makeBoundingBox();
 11323  
 11324        for (var i = 0; i < nodes.length; i++) {
 11325          var node = nodes[i];
 11326          var pos = fnMem(node, i);
 11327          expandBoundingBoxByPoint(bb, pos.x, pos.y);
 11328        }
 11329  
 11330        return bb;
 11331      };
 11332  
 11333      var bb = spacingBb();
 11334      var getFinalPos = memoize(function (node, i) {
 11335        var newPos = fnMem(node, i);
 11336  
 11337        if (useSpacingFactor) {
 11338          var spacing = Math.abs(options.spacingFactor);
 11339          newPos = calculateSpacing(spacing, bb, newPos);
 11340        }
 11341  
 11342        if (options.transform != null) {
 11343          newPos = options.transform(node, newPos);
 11344        }
 11345  
 11346        return newPos;
 11347      }, getMemoizeKey);
 11348  
 11349      if (options.animate) {
 11350        for (var i = 0; i < nodes.length; i++) {
 11351          var node = nodes[i];
 11352          var newPos = getFinalPos(node, i);
 11353          var animateNode = options.animateFilter == null || options.animateFilter(node, i);
 11354  
 11355          if (animateNode) {
 11356            var ani = node.animation({
 11357              position: newPos,
 11358              duration: options.animationDuration,
 11359              easing: options.animationEasing
 11360            });
 11361            layout.animations.push(ani);
 11362          } else {
 11363            node.position(newPos);
 11364          }
 11365        }
 11366  
 11367        if (options.fit) {
 11368          var fitAni = cy.animation({
 11369            fit: {
 11370              boundingBox: layoutEles.boundingBoxAt(getFinalPos),
 11371              padding: options.padding
 11372            },
 11373            duration: options.animationDuration,
 11374            easing: options.animationEasing
 11375          });
 11376          layout.animations.push(fitAni);
 11377        } else if (options.zoom !== undefined && options.pan !== undefined) {
 11378          var zoomPanAni = cy.animation({
 11379            zoom: options.zoom,
 11380            pan: options.pan,
 11381            duration: options.animationDuration,
 11382            easing: options.animationEasing
 11383          });
 11384          layout.animations.push(zoomPanAni);
 11385        }
 11386  
 11387        layout.animations.forEach(function (ani) {
 11388          return ani.play();
 11389        });
 11390        layout.one('layoutready', options.ready);
 11391        layout.emit({
 11392          type: 'layoutready',
 11393          layout: layout
 11394        });
 11395        Promise$1.all(layout.animations.map(function (ani) {
 11396          return ani.promise();
 11397        })).then(function () {
 11398          layout.one('layoutstop', options.stop);
 11399          layout.emit({
 11400            type: 'layoutstop',
 11401            layout: layout
 11402          });
 11403        });
 11404      } else {
 11405        nodes.positions(getFinalPos);
 11406  
 11407        if (options.fit) {
 11408          cy.fit(options.eles, options.padding);
 11409        }
 11410  
 11411        if (options.zoom != null) {
 11412          cy.zoom(options.zoom);
 11413        }
 11414  
 11415        if (options.pan) {
 11416          cy.pan(options.pan);
 11417        }
 11418  
 11419        layout.one('layoutready', options.ready);
 11420        layout.emit({
 11421          type: 'layoutready',
 11422          layout: layout
 11423        });
 11424        layout.one('layoutstop', options.stop);
 11425        layout.emit({
 11426          type: 'layoutstop',
 11427          layout: layout
 11428        });
 11429      }
 11430  
 11431      return this; // chaining
 11432    },
 11433    layout: function layout(options) {
 11434      var cy = this.cy();
 11435      return cy.makeLayout(extend({}, options, {
 11436        eles: this
 11437      }));
 11438    }
 11439  }; // aliases:
 11440  
 11441  elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
 11442  
 11443  function styleCache(key, fn, ele) {
 11444    var _p = ele._private;
 11445    var cache = _p.styleCache = _p.styleCache || [];
 11446    var val;
 11447  
 11448    if ((val = cache[key]) != null) {
 11449      return val;
 11450    } else {
 11451      val = cache[key] = fn(ele);
 11452      return val;
 11453    }
 11454  }
 11455  
 11456  function cacheStyleFunction(key, fn) {
 11457    key = hashString(key);
 11458    return function cachedStyleFunction(ele) {
 11459      return styleCache(key, fn, ele);
 11460    };
 11461  }
 11462  
 11463  function cachePrototypeStyleFunction(key, fn) {
 11464    key = hashString(key);
 11465  
 11466    var selfFn = function selfFn(ele) {
 11467      return fn.call(ele);
 11468    };
 11469  
 11470    return function cachedPrototypeStyleFunction() {
 11471      var ele = this[0];
 11472  
 11473      if (ele) {
 11474        return styleCache(key, selfFn, ele);
 11475      }
 11476    };
 11477  }
 11478  
 11479  var elesfn$r = {
 11480    recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
 11481      var cy = this.cy();
 11482      var renderer = cy.renderer();
 11483      var styleEnabled = cy.styleEnabled();
 11484  
 11485      if (renderer && styleEnabled) {
 11486        renderer.recalculateRenderedStyle(this, useCache);
 11487      }
 11488  
 11489      return this;
 11490    },
 11491    dirtyStyleCache: function dirtyStyleCache() {
 11492      var cy = this.cy();
 11493  
 11494      var dirty = function dirty(ele) {
 11495        return ele._private.styleCache = null;
 11496      };
 11497  
 11498      if (cy.hasCompoundNodes()) {
 11499        var eles;
 11500        eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
 11501        eles.merge(eles.connectedEdges());
 11502        eles.forEach(dirty);
 11503      } else {
 11504        this.forEach(function (ele) {
 11505          dirty(ele);
 11506          ele.connectedEdges().forEach(dirty);
 11507        });
 11508      }
 11509  
 11510      return this;
 11511    },
 11512    // fully updates (recalculates) the style for the elements
 11513    updateStyle: function updateStyle(notifyRenderer) {
 11514      var cy = this._private.cy;
 11515  
 11516      if (!cy.styleEnabled()) {
 11517        return this;
 11518      }
 11519  
 11520      if (cy.batching()) {
 11521        var bEles = cy._private.batchStyleEles;
 11522        bEles.merge(this);
 11523        return this; // chaining and exit early when batching
 11524      }
 11525  
 11526      var hasCompounds = cy.hasCompoundNodes();
 11527      var style = cy.style();
 11528      var updatedEles = this;
 11529      notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
 11530  
 11531      if (hasCompounds) {
 11532        // then add everything up and down for compound selector checks
 11533        updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
 11534      }
 11535  
 11536      var changedEles = style.apply(updatedEles);
 11537  
 11538      if (notifyRenderer) {
 11539        changedEles.emitAndNotify('style'); // let renderer know we changed style
 11540      } else {
 11541        changedEles.emit('style'); // just fire the event
 11542      }
 11543  
 11544      return this; // chaining
 11545    },
 11546    // get the internal parsed style object for the specified property
 11547    parsedStyle: function parsedStyle(property) {
 11548      var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
 11549      var ele = this[0];
 11550      var cy = ele.cy();
 11551  
 11552      if (!cy.styleEnabled()) {
 11553        return;
 11554      }
 11555  
 11556      if (ele) {
 11557        var overriddenStyle = ele._private.style[property];
 11558  
 11559        if (overriddenStyle != null) {
 11560          return overriddenStyle;
 11561        } else if (includeNonDefault) {
 11562          return cy.style().getDefaultProperty(property);
 11563        } else {
 11564          return null;
 11565        }
 11566      }
 11567    },
 11568    numericStyle: function numericStyle(property) {
 11569      var ele = this[0];
 11570  
 11571      if (!ele.cy().styleEnabled()) {
 11572        return;
 11573      }
 11574  
 11575      if (ele) {
 11576        var pstyle = ele.pstyle(property);
 11577        return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
 11578      }
 11579    },
 11580    numericStyleUnits: function numericStyleUnits(property) {
 11581      var ele = this[0];
 11582  
 11583      if (!ele.cy().styleEnabled()) {
 11584        return;
 11585      }
 11586  
 11587      if (ele) {
 11588        return ele.pstyle(property).units;
 11589      }
 11590    },
 11591    // get the specified css property as a rendered value (i.e. on-screen value)
 11592    // or get the whole rendered style if no property specified (NB doesn't allow setting)
 11593    renderedStyle: function renderedStyle(property) {
 11594      var cy = this.cy();
 11595  
 11596      if (!cy.styleEnabled()) {
 11597        return this;
 11598      }
 11599  
 11600      var ele = this[0];
 11601  
 11602      if (ele) {
 11603        return cy.style().getRenderedStyle(ele, property);
 11604      }
 11605    },
 11606    // read the calculated css style of the element or override the style (via a bypass)
 11607    style: function style(name, value) {
 11608      var cy = this.cy();
 11609  
 11610      if (!cy.styleEnabled()) {
 11611        return this;
 11612      }
 11613  
 11614      var updateTransitions = false;
 11615      var style = cy.style();
 11616  
 11617      if (plainObject(name)) {
 11618        // then extend the bypass
 11619        var props = name;
 11620        style.applyBypass(this, props, updateTransitions);
 11621        this.emitAndNotify('style'); // let the renderer know we've updated style
 11622      } else if (string(name)) {
 11623        if (value === undefined) {
 11624          // then get the property from the style
 11625          var ele = this[0];
 11626  
 11627          if (ele) {
 11628            return style.getStylePropertyValue(ele, name);
 11629          } else {
 11630            // empty collection => can't get any value
 11631            return;
 11632          }
 11633        } else {
 11634          // then set the bypass with the property value
 11635          style.applyBypass(this, name, value, updateTransitions);
 11636          this.emitAndNotify('style'); // let the renderer know we've updated style
 11637        }
 11638      } else if (name === undefined) {
 11639        var _ele = this[0];
 11640  
 11641        if (_ele) {
 11642          return style.getRawStyle(_ele);
 11643        } else {
 11644          // empty collection => can't get any value
 11645          return;
 11646        }
 11647      }
 11648  
 11649      return this; // chaining
 11650    },
 11651    removeStyle: function removeStyle(names) {
 11652      var cy = this.cy();
 11653  
 11654      if (!cy.styleEnabled()) {
 11655        return this;
 11656      }
 11657  
 11658      var updateTransitions = false;
 11659      var style = cy.style();
 11660      var eles = this;
 11661  
 11662      if (names === undefined) {
 11663        for (var i = 0; i < eles.length; i++) {
 11664          var ele = eles[i];
 11665          style.removeAllBypasses(ele, updateTransitions);
 11666        }
 11667      } else {
 11668        names = names.split(/\s+/);
 11669  
 11670        for (var _i = 0; _i < eles.length; _i++) {
 11671          var _ele2 = eles[_i];
 11672          style.removeBypasses(_ele2, names, updateTransitions);
 11673        }
 11674      }
 11675  
 11676      this.emitAndNotify('style'); // let the renderer know we've updated style
 11677  
 11678      return this; // chaining
 11679    },
 11680    show: function show() {
 11681      this.css('display', 'element');
 11682      return this; // chaining
 11683    },
 11684    hide: function hide() {
 11685      this.css('display', 'none');
 11686      return this; // chaining
 11687    },
 11688    effectiveOpacity: function effectiveOpacity() {
 11689      var cy = this.cy();
 11690  
 11691      if (!cy.styleEnabled()) {
 11692        return 1;
 11693      }
 11694  
 11695      var hasCompoundNodes = cy.hasCompoundNodes();
 11696      var ele = this[0];
 11697  
 11698      if (ele) {
 11699        var _p = ele._private;
 11700        var parentOpacity = ele.pstyle('opacity').value;
 11701  
 11702        if (!hasCompoundNodes) {
 11703          return parentOpacity;
 11704        }
 11705  
 11706        var parents = !_p.data.parent ? null : ele.parents();
 11707  
 11708        if (parents) {
 11709          for (var i = 0; i < parents.length; i++) {
 11710            var parent = parents[i];
 11711            var opacity = parent.pstyle('opacity').value;
 11712            parentOpacity = opacity * parentOpacity;
 11713          }
 11714        }
 11715  
 11716        return parentOpacity;
 11717      }
 11718    },
 11719    transparent: function transparent() {
 11720      var cy = this.cy();
 11721  
 11722      if (!cy.styleEnabled()) {
 11723        return false;
 11724      }
 11725  
 11726      var ele = this[0];
 11727      var hasCompoundNodes = ele.cy().hasCompoundNodes();
 11728  
 11729      if (ele) {
 11730        if (!hasCompoundNodes) {
 11731          return ele.pstyle('opacity').value === 0;
 11732        } else {
 11733          return ele.effectiveOpacity() === 0;
 11734        }
 11735      }
 11736    },
 11737    backgrounding: function backgrounding() {
 11738      var cy = this.cy();
 11739  
 11740      if (!cy.styleEnabled()) {
 11741        return false;
 11742      }
 11743  
 11744      var ele = this[0];
 11745      return ele._private.backgrounding ? true : false;
 11746    }
 11747  };
 11748  
 11749  function checkCompound(ele, parentOk) {
 11750    var _p = ele._private;
 11751    var parents = _p.data.parent ? ele.parents() : null;
 11752  
 11753    if (parents) {
 11754      for (var i = 0; i < parents.length; i++) {
 11755        var parent = parents[i];
 11756  
 11757        if (!parentOk(parent)) {
 11758          return false;
 11759        }
 11760      }
 11761    }
 11762  
 11763    return true;
 11764  }
 11765  
 11766  function defineDerivedStateFunction(specs) {
 11767    var ok = specs.ok;
 11768    var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
 11769    var parentOk = specs.parentOk || specs.ok;
 11770    return function () {
 11771      var cy = this.cy();
 11772  
 11773      if (!cy.styleEnabled()) {
 11774        return true;
 11775      }
 11776  
 11777      var ele = this[0];
 11778      var hasCompoundNodes = cy.hasCompoundNodes();
 11779  
 11780      if (ele) {
 11781        var _p = ele._private;
 11782  
 11783        if (!ok(ele)) {
 11784          return false;
 11785        }
 11786  
 11787        if (ele.isNode()) {
 11788          return !hasCompoundNodes || checkCompound(ele, parentOk);
 11789        } else {
 11790          var src = _p.source;
 11791          var tgt = _p.target;
 11792          return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
 11793        }
 11794      }
 11795    };
 11796  }
 11797  
 11798  var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
 11799    return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
 11800  });
 11801  elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
 11802    ok: eleTakesUpSpace
 11803  }));
 11804  var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
 11805    return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
 11806  });
 11807  var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
 11808    return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
 11809  });
 11810  elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
 11811    ok: eleInteractive,
 11812    parentOk: parentInteractive,
 11813    edgeOkViaNode: eleTakesUpSpace
 11814  }));
 11815  
 11816  elesfn$r.noninteractive = function () {
 11817    var ele = this[0];
 11818  
 11819    if (ele) {
 11820      return !ele.interactive();
 11821    }
 11822  };
 11823  
 11824  var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
 11825    return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
 11826  });
 11827  var edgeVisibleViaNode = eleTakesUpSpace;
 11828  elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
 11829    ok: eleVisible,
 11830    edgeOkViaNode: edgeVisibleViaNode
 11831  }));
 11832  
 11833  elesfn$r.hidden = function () {
 11834    var ele = this[0];
 11835  
 11836    if (ele) {
 11837      return !ele.visible();
 11838    }
 11839  };
 11840  
 11841  elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
 11842    if (!this.cy().styleEnabled()) {
 11843      return false;
 11844    }
 11845  
 11846    return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
 11847  });
 11848  elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
 11849  elesfn$r.renderedCss = elesfn$r.renderedStyle;
 11850  elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
 11851  elesfn$r.pstyle = elesfn$r.parsedStyle;
 11852  
 11853  var elesfn$s = {};
 11854  
 11855  function defineSwitchFunction(params) {
 11856    return function () {
 11857      var args = arguments;
 11858      var changedEles = []; // e.g. cy.nodes().select( data, handler )
 11859  
 11860      if (args.length === 2) {
 11861        var data = args[0];
 11862        var handler = args[1];
 11863        this.on(params.event, data, handler);
 11864      } // e.g. cy.nodes().select( handler )
 11865      else if (args.length === 1 && fn(args[0])) {
 11866          var _handler = args[0];
 11867          this.on(params.event, _handler);
 11868        } // e.g. cy.nodes().select()
 11869        // e.g. (private) cy.nodes().select(['tapselect'])
 11870        else if (args.length === 0 || args.length === 1 && array(args[0])) {
 11871            var addlEvents = args.length === 1 ? args[0] : null;
 11872  
 11873            for (var i = 0; i < this.length; i++) {
 11874              var ele = this[i];
 11875              var able = !params.ableField || ele._private[params.ableField];
 11876              var changed = ele._private[params.field] != params.value;
 11877  
 11878              if (params.overrideAble) {
 11879                var overrideAble = params.overrideAble(ele);
 11880  
 11881                if (overrideAble !== undefined) {
 11882                  able = overrideAble;
 11883  
 11884                  if (!overrideAble) {
 11885                    return this;
 11886                  } // to save cycles assume not able for all on override
 11887  
 11888                }
 11889              }
 11890  
 11891              if (able) {
 11892                ele._private[params.field] = params.value;
 11893  
 11894                if (changed) {
 11895                  changedEles.push(ele);
 11896                }
 11897              }
 11898            }
 11899  
 11900            var changedColl = this.spawn(changedEles);
 11901            changedColl.updateStyle(); // change of state => possible change of style
 11902  
 11903            changedColl.emit(params.event);
 11904  
 11905            if (addlEvents) {
 11906              changedColl.emit(addlEvents);
 11907            }
 11908          }
 11909  
 11910      return this;
 11911    };
 11912  }
 11913  
 11914  function defineSwitchSet(params) {
 11915    elesfn$s[params.field] = function () {
 11916      var ele = this[0];
 11917  
 11918      if (ele) {
 11919        if (params.overrideField) {
 11920          var val = params.overrideField(ele);
 11921  
 11922          if (val !== undefined) {
 11923            return val;
 11924          }
 11925        }
 11926  
 11927        return ele._private[params.field];
 11928      }
 11929    };
 11930  
 11931    elesfn$s[params.on] = defineSwitchFunction({
 11932      event: params.on,
 11933      field: params.field,
 11934      ableField: params.ableField,
 11935      overrideAble: params.overrideAble,
 11936      value: true
 11937    });
 11938    elesfn$s[params.off] = defineSwitchFunction({
 11939      event: params.off,
 11940      field: params.field,
 11941      ableField: params.ableField,
 11942      overrideAble: params.overrideAble,
 11943      value: false
 11944    });
 11945  }
 11946  
 11947  defineSwitchSet({
 11948    field: 'locked',
 11949    overrideField: function overrideField(ele) {
 11950      return ele.cy().autolock() ? true : undefined;
 11951    },
 11952    on: 'lock',
 11953    off: 'unlock'
 11954  });
 11955  defineSwitchSet({
 11956    field: 'grabbable',
 11957    overrideField: function overrideField(ele) {
 11958      return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
 11959    },
 11960    on: 'grabify',
 11961    off: 'ungrabify'
 11962  });
 11963  defineSwitchSet({
 11964    field: 'selected',
 11965    ableField: 'selectable',
 11966    overrideAble: function overrideAble(ele) {
 11967      return ele.cy().autounselectify() ? false : undefined;
 11968    },
 11969    on: 'select',
 11970    off: 'unselect'
 11971  });
 11972  defineSwitchSet({
 11973    field: 'selectable',
 11974    overrideField: function overrideField(ele) {
 11975      return ele.cy().autounselectify() ? false : undefined;
 11976    },
 11977    on: 'selectify',
 11978    off: 'unselectify'
 11979  });
 11980  elesfn$s.deselect = elesfn$s.unselect;
 11981  
 11982  elesfn$s.grabbed = function () {
 11983    var ele = this[0];
 11984  
 11985    if (ele) {
 11986      return ele._private.grabbed;
 11987    }
 11988  };
 11989  
 11990  defineSwitchSet({
 11991    field: 'active',
 11992    on: 'activate',
 11993    off: 'unactivate'
 11994  });
 11995  defineSwitchSet({
 11996    field: 'pannable',
 11997    on: 'panify',
 11998    off: 'unpanify'
 11999  });
 12000  
 12001  elesfn$s.inactive = function () {
 12002    var ele = this[0];
 12003  
 12004    if (ele) {
 12005      return !ele._private.active;
 12006    }
 12007  };
 12008  
 12009  var elesfn$t = {}; // DAG functions
 12010  ////////////////
 12011  
 12012  var defineDagExtremity = function defineDagExtremity(params) {
 12013    return function dagExtremityImpl(selector) {
 12014      var eles = this;
 12015      var ret = [];
 12016  
 12017      for (var i = 0; i < eles.length; i++) {
 12018        var ele = eles[i];
 12019  
 12020        if (!ele.isNode()) {
 12021          continue;
 12022        }
 12023  
 12024        var disqualified = false;
 12025        var edges = ele.connectedEdges();
 12026  
 12027        for (var j = 0; j < edges.length; j++) {
 12028          var edge = edges[j];
 12029          var src = edge.source();
 12030          var tgt = edge.target();
 12031  
 12032          if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
 12033            disqualified = true;
 12034            break;
 12035          }
 12036        }
 12037  
 12038        if (!disqualified) {
 12039          ret.push(ele);
 12040        }
 12041      }
 12042  
 12043      return this.spawn(ret, {
 12044        unique: true
 12045      }).filter(selector);
 12046    };
 12047  };
 12048  
 12049  var defineDagOneHop = function defineDagOneHop(params) {
 12050    return function (selector) {
 12051      var eles = this;
 12052      var oEles = [];
 12053  
 12054      for (var i = 0; i < eles.length; i++) {
 12055        var ele = eles[i];
 12056  
 12057        if (!ele.isNode()) {
 12058          continue;
 12059        }
 12060  
 12061        var edges = ele.connectedEdges();
 12062  
 12063        for (var j = 0; j < edges.length; j++) {
 12064          var edge = edges[j];
 12065          var src = edge.source();
 12066          var tgt = edge.target();
 12067  
 12068          if (params.outgoing && src === ele) {
 12069            oEles.push(edge);
 12070            oEles.push(tgt);
 12071          } else if (params.incoming && tgt === ele) {
 12072            oEles.push(edge);
 12073            oEles.push(src);
 12074          }
 12075        }
 12076      }
 12077  
 12078      return this.spawn(oEles, {
 12079        unique: true
 12080      }).filter(selector);
 12081    };
 12082  };
 12083  
 12084  var defineDagAllHops = function defineDagAllHops(params) {
 12085    return function (selector) {
 12086      var eles = this;
 12087      var sEles = [];
 12088      var sElesIds = {};
 12089  
 12090      for (;;) {
 12091        var next = params.outgoing ? eles.outgoers() : eles.incomers();
 12092  
 12093        if (next.length === 0) {
 12094          break;
 12095        } // done if none left
 12096  
 12097  
 12098        var newNext = false;
 12099  
 12100        for (var i = 0; i < next.length; i++) {
 12101          var n = next[i];
 12102          var nid = n.id();
 12103  
 12104          if (!sElesIds[nid]) {
 12105            sElesIds[nid] = true;
 12106            sEles.push(n);
 12107            newNext = true;
 12108          }
 12109        }
 12110  
 12111        if (!newNext) {
 12112          break;
 12113        } // done if touched all outgoers already
 12114  
 12115  
 12116        eles = next;
 12117      }
 12118  
 12119      return this.spawn(sEles, {
 12120        unique: true
 12121      }).filter(selector);
 12122    };
 12123  };
 12124  
 12125  elesfn$t.clearTraversalCache = function () {
 12126    for (var i = 0; i < this.length; i++) {
 12127      this[i]._private.traversalCache = null;
 12128    }
 12129  };
 12130  
 12131  extend(elesfn$t, {
 12132    // get the root nodes in the DAG
 12133    roots: defineDagExtremity({
 12134      noIncomingEdges: true
 12135    }),
 12136    // get the leaf nodes in the DAG
 12137    leaves: defineDagExtremity({
 12138      noOutgoingEdges: true
 12139    }),
 12140    // normally called children in graph theory
 12141    // these nodes =edges=> outgoing nodes
 12142    outgoers: cache(defineDagOneHop({
 12143      outgoing: true
 12144    }), 'outgoers'),
 12145    // aka DAG descendants
 12146    successors: defineDagAllHops({
 12147      outgoing: true
 12148    }),
 12149    // normally called parents in graph theory
 12150    // these nodes <=edges= incoming nodes
 12151    incomers: cache(defineDagOneHop({
 12152      incoming: true
 12153    }), 'incomers'),
 12154    // aka DAG ancestors
 12155    predecessors: defineDagAllHops({
 12156      incoming: true
 12157    })
 12158  }); // Neighbourhood functions
 12159  //////////////////////////
 12160  
 12161  extend(elesfn$t, {
 12162    neighborhood: cache(function (selector) {
 12163      var elements = [];
 12164      var nodes = this.nodes();
 12165  
 12166      for (var i = 0; i < nodes.length; i++) {
 12167        // for all nodes
 12168        var node = nodes[i];
 12169        var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
 12170  
 12171        for (var j = 0; j < connectedEdges.length; j++) {
 12172          var edge = connectedEdges[j];
 12173          var src = edge.source();
 12174          var tgt = edge.target();
 12175          var otherNode = node === src ? tgt : src; // need check in case of loop
 12176  
 12177          if (otherNode.length > 0) {
 12178            elements.push(otherNode[0]); // add node 1 hop away
 12179          } // add connected edge
 12180  
 12181  
 12182          elements.push(edge[0]);
 12183        }
 12184      }
 12185  
 12186      return this.spawn(elements, {
 12187        unique: true
 12188      }).filter(selector);
 12189    }, 'neighborhood'),
 12190    closedNeighborhood: function closedNeighborhood(selector) {
 12191      return this.neighborhood().add(this).filter(selector);
 12192    },
 12193    openNeighborhood: function openNeighborhood(selector) {
 12194      return this.neighborhood(selector);
 12195    }
 12196  }); // aliases
 12197  
 12198  elesfn$t.neighbourhood = elesfn$t.neighborhood;
 12199  elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
 12200  elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
 12201  /////////////////
 12202  
 12203  extend(elesfn$t, {
 12204    source: cache(function sourceImpl(selector) {
 12205      var ele = this[0];
 12206      var src;
 12207  
 12208      if (ele) {
 12209        src = ele._private.source || ele.cy().collection();
 12210      }
 12211  
 12212      return src && selector ? src.filter(selector) : src;
 12213    }, 'source'),
 12214    target: cache(function targetImpl(selector) {
 12215      var ele = this[0];
 12216      var tgt;
 12217  
 12218      if (ele) {
 12219        tgt = ele._private.target || ele.cy().collection();
 12220      }
 12221  
 12222      return tgt && selector ? tgt.filter(selector) : tgt;
 12223    }, 'target'),
 12224    sources: defineSourceFunction({
 12225      attr: 'source'
 12226    }),
 12227    targets: defineSourceFunction({
 12228      attr: 'target'
 12229    })
 12230  });
 12231  
 12232  function defineSourceFunction(params) {
 12233    return function sourceImpl(selector) {
 12234      var sources = [];
 12235  
 12236      for (var i = 0; i < this.length; i++) {
 12237        var ele = this[i];
 12238        var src = ele._private[params.attr];
 12239  
 12240        if (src) {
 12241          sources.push(src);
 12242        }
 12243      }
 12244  
 12245      return this.spawn(sources, {
 12246        unique: true
 12247      }).filter(selector);
 12248    };
 12249  }
 12250  
 12251  extend(elesfn$t, {
 12252    edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
 12253    edgesTo: cache(defineEdgesWithFunction({
 12254      thisIsSrc: true
 12255    }), 'edgesTo')
 12256  });
 12257  
 12258  function defineEdgesWithFunction(params) {
 12259    return function edgesWithImpl(otherNodes) {
 12260      var elements = [];
 12261      var cy = this._private.cy;
 12262      var p = params || {}; // get elements if a selector is specified
 12263  
 12264      if (string(otherNodes)) {
 12265        otherNodes = cy.$(otherNodes);
 12266      }
 12267  
 12268      for (var h = 0; h < otherNodes.length; h++) {
 12269        var edges = otherNodes[h]._private.edges;
 12270  
 12271        for (var i = 0; i < edges.length; i++) {
 12272          var edge = edges[i];
 12273          var edgeData = edge._private.data;
 12274          var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
 12275          var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
 12276          var edgeConnectsThisAndOther = thisToOther || otherToThis;
 12277  
 12278          if (!edgeConnectsThisAndOther) {
 12279            continue;
 12280          }
 12281  
 12282          if (p.thisIsSrc || p.thisIsTgt) {
 12283            if (p.thisIsSrc && !thisToOther) {
 12284              continue;
 12285            }
 12286  
 12287            if (p.thisIsTgt && !otherToThis) {
 12288              continue;
 12289            }
 12290          }
 12291  
 12292          elements.push(edge);
 12293        }
 12294      }
 12295  
 12296      return this.spawn(elements, {
 12297        unique: true
 12298      });
 12299    };
 12300  }
 12301  
 12302  extend(elesfn$t, {
 12303    connectedEdges: cache(function (selector) {
 12304      var retEles = [];
 12305      var eles = this;
 12306  
 12307      for (var i = 0; i < eles.length; i++) {
 12308        var node = eles[i];
 12309  
 12310        if (!node.isNode()) {
 12311          continue;
 12312        }
 12313  
 12314        var edges = node._private.edges;
 12315  
 12316        for (var j = 0; j < edges.length; j++) {
 12317          var edge = edges[j];
 12318          retEles.push(edge);
 12319        }
 12320      }
 12321  
 12322      return this.spawn(retEles, {
 12323        unique: true
 12324      }).filter(selector);
 12325    }, 'connectedEdges'),
 12326    connectedNodes: cache(function (selector) {
 12327      var retEles = [];
 12328      var eles = this;
 12329  
 12330      for (var i = 0; i < eles.length; i++) {
 12331        var edge = eles[i];
 12332  
 12333        if (!edge.isEdge()) {
 12334          continue;
 12335        }
 12336  
 12337        retEles.push(edge.source()[0]);
 12338        retEles.push(edge.target()[0]);
 12339      }
 12340  
 12341      return this.spawn(retEles, {
 12342        unique: true
 12343      }).filter(selector);
 12344    }, 'connectedNodes'),
 12345    parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
 12346    codirectedEdges: cache(defineParallelEdgesFunction({
 12347      codirected: true
 12348    }), 'codirectedEdges')
 12349  });
 12350  
 12351  function defineParallelEdgesFunction(params) {
 12352    var defaults = {
 12353      codirected: false
 12354    };
 12355    params = extend({}, defaults, params);
 12356    return function parallelEdgesImpl(selector) {
 12357      // micro-optimised for renderer
 12358      var elements = [];
 12359      var edges = this.edges();
 12360      var p = params; // look at all the edges in the collection
 12361  
 12362      for (var i = 0; i < edges.length; i++) {
 12363        var edge1 = edges[i];
 12364        var edge1_p = edge1._private;
 12365        var src1 = edge1_p.source;
 12366        var srcid1 = src1._private.data.id;
 12367        var tgtid1 = edge1_p.data.target;
 12368        var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
 12369  
 12370        for (var j = 0; j < srcEdges1.length; j++) {
 12371          var edge2 = srcEdges1[j];
 12372          var edge2data = edge2._private.data;
 12373          var tgtid2 = edge2data.target;
 12374          var srcid2 = edge2data.source;
 12375          var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
 12376          var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
 12377  
 12378          if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
 12379            elements.push(edge2);
 12380          }
 12381        }
 12382      }
 12383  
 12384      return this.spawn(elements, {
 12385        unique: true
 12386      }).filter(selector);
 12387    };
 12388  } // Misc functions
 12389  /////////////////
 12390  
 12391  
 12392  extend(elesfn$t, {
 12393    components: function components(root) {
 12394      var self = this;
 12395      var cy = self.cy();
 12396      var visited = cy.collection();
 12397      var unvisited = root == null ? self.nodes() : root.nodes();
 12398      var components = [];
 12399  
 12400      if (root != null && unvisited.empty()) {
 12401        // root may contain only edges
 12402        unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
 12403      }
 12404  
 12405      var visitInComponent = function visitInComponent(node, component) {
 12406        visited.merge(node);
 12407        unvisited.unmerge(node);
 12408        component.merge(node);
 12409      };
 12410  
 12411      if (unvisited.empty()) {
 12412        return self.spawn();
 12413      }
 12414  
 12415      var _loop = function _loop() {
 12416        // each iteration yields a component
 12417        var cmpt = cy.collection();
 12418        components.push(cmpt);
 12419        var root = unvisited[0];
 12420        visitInComponent(root, cmpt);
 12421        self.bfs({
 12422          directed: false,
 12423          roots: root,
 12424          visit: function visit(v) {
 12425            return visitInComponent(v, cmpt);
 12426          }
 12427        });
 12428        cmpt.forEach(function (node) {
 12429          node.connectedEdges().forEach(function (e) {
 12430            // connectedEdges() usually cached
 12431            if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
 12432              // has() is cheap
 12433              cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
 12434            }
 12435          });
 12436        });
 12437      };
 12438  
 12439      do {
 12440        _loop();
 12441      } while (unvisited.length > 0);
 12442  
 12443      return components;
 12444    },
 12445    component: function component() {
 12446      var ele = this[0];
 12447      return ele.cy().mutableElements().components(ele)[0];
 12448    }
 12449  });
 12450  elesfn$t.componentsOf = elesfn$t.components;
 12451  
 12452  var idFactory = {
 12453    generate: function generate(cy, element, tryThisId) {
 12454      var id = tryThisId != null ? tryThisId : uuid();
 12455  
 12456      while (cy.hasElementWithId(id)) {
 12457        id = uuid();
 12458      }
 12459  
 12460      return id;
 12461    }
 12462  }; // represents a set of nodes, edges, or both together
 12463  
 12464  var Collection = function Collection(cy, elements, options) {
 12465    if (cy === undefined || !core(cy)) {
 12466      error('A collection must have a reference to the core');
 12467      return;
 12468    }
 12469  
 12470    var map = new Map$1();
 12471    var createdElements = false;
 12472  
 12473    if (!elements) {
 12474      elements = [];
 12475    } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
 12476      createdElements = true; // make elements from json and restore all at once later
 12477  
 12478      var eles = [];
 12479      var elesIds = new Set$1();
 12480  
 12481      for (var i = 0, l = elements.length; i < l; i++) {
 12482        var json = elements[i];
 12483  
 12484        if (json.data == null) {
 12485          json.data = {};
 12486        }
 12487  
 12488        var _data = json.data; // make sure newly created elements have valid ids
 12489  
 12490        if (_data.id == null) {
 12491          _data.id = idFactory.generate(cy, json);
 12492        } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
 12493          continue; // can't create element if prior id already exists
 12494        }
 12495  
 12496        var ele = new Element(cy, json, false);
 12497        eles.push(ele);
 12498        elesIds.add(_data.id);
 12499      }
 12500  
 12501      elements = eles;
 12502    }
 12503  
 12504    this.length = 0;
 12505  
 12506    for (var _i = 0, _l = elements.length; _i < _l; _i++) {
 12507      var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
 12508  
 12509      if (element$1 == null) {
 12510        continue;
 12511      }
 12512  
 12513      var id = element$1._private.data.id;
 12514  
 12515      if (options == null || options.unique && !map.has(id)) {
 12516        map.set(id, {
 12517          index: this.length,
 12518          ele: element$1
 12519        });
 12520        this[this.length] = element$1;
 12521        this.length++;
 12522      }
 12523    }
 12524  
 12525    this._private = {
 12526      cy: cy,
 12527      map: map
 12528    }; // restore the elements if we created them from json
 12529  
 12530    if (createdElements) {
 12531      this.restore();
 12532    }
 12533  }; // Functions
 12534  ////////////////////////////////////////////////////////////////////////////////////////////////////
 12535  // keep the prototypes in sync (an element has the same functions as a collection)
 12536  // and use elefn and elesfn as shorthands to the prototypes
 12537  
 12538  
 12539  var elesfn$u = Element.prototype = Collection.prototype;
 12540  
 12541  elesfn$u.instanceString = function () {
 12542    return 'collection';
 12543  };
 12544  
 12545  elesfn$u.spawn = function (cy, eles, opts) {
 12546    if (!core(cy)) {
 12547      // cy is optional
 12548      opts = eles;
 12549      eles = cy;
 12550      cy = this.cy();
 12551    }
 12552  
 12553    return new Collection(cy, eles, opts);
 12554  };
 12555  
 12556  elesfn$u.spawnSelf = function () {
 12557    return this.spawn(this);
 12558  };
 12559  
 12560  elesfn$u.cy = function () {
 12561    return this._private.cy;
 12562  };
 12563  
 12564  elesfn$u.renderer = function () {
 12565    return this._private.cy.renderer();
 12566  };
 12567  
 12568  elesfn$u.element = function () {
 12569    return this[0];
 12570  };
 12571  
 12572  elesfn$u.collection = function () {
 12573    if (collection(this)) {
 12574      return this;
 12575    } else {
 12576      // an element
 12577      return new Collection(this._private.cy, [this]);
 12578    }
 12579  };
 12580  
 12581  elesfn$u.unique = function () {
 12582    return new Collection(this._private.cy, this, {
 12583      unique: true
 12584    });
 12585  };
 12586  
 12587  elesfn$u.hasElementWithId = function (id) {
 12588    id = '' + id; // id must be string
 12589  
 12590    return this._private.map.has(id);
 12591  };
 12592  
 12593  elesfn$u.getElementById = function (id) {
 12594    id = '' + id; // id must be string
 12595  
 12596    var cy = this._private.cy;
 12597  
 12598    var entry = this._private.map.get(id);
 12599  
 12600    return entry ? entry.ele : new Collection(cy); // get ele or empty collection
 12601  };
 12602  
 12603  elesfn$u.$id = elesfn$u.getElementById;
 12604  
 12605  elesfn$u.poolIndex = function () {
 12606    var cy = this._private.cy;
 12607    var eles = cy._private.elements;
 12608    var id = this[0]._private.data.id;
 12609    return eles._private.map.get(id).index;
 12610  };
 12611  
 12612  elesfn$u.indexOf = function (ele) {
 12613    var id = ele[0]._private.data.id;
 12614    return this._private.map.get(id).index;
 12615  };
 12616  
 12617  elesfn$u.indexOfId = function (id) {
 12618    id = '' + id; // id must be string
 12619  
 12620    return this._private.map.get(id).index;
 12621  };
 12622  
 12623  elesfn$u.json = function (obj) {
 12624    var ele = this.element();
 12625    var cy = this.cy();
 12626  
 12627    if (ele == null && obj) {
 12628      return this;
 12629    } // can't set to no eles
 12630  
 12631  
 12632    if (ele == null) {
 12633      return undefined;
 12634    } // can't get from no eles
 12635  
 12636  
 12637    var p = ele._private;
 12638  
 12639    if (plainObject(obj)) {
 12640      // set
 12641      cy.startBatch();
 12642  
 12643      if (obj.data) {
 12644        ele.data(obj.data);
 12645        var _data2 = p.data;
 12646  
 12647        if (ele.isEdge()) {
 12648          // source and target are immutable via data()
 12649          var move = false;
 12650          var spec = {};
 12651          var src = obj.data.source;
 12652          var tgt = obj.data.target;
 12653  
 12654          if (src != null && src != _data2.source) {
 12655            spec.source = '' + src; // id must be string
 12656  
 12657            move = true;
 12658          }
 12659  
 12660          if (tgt != null && tgt != _data2.target) {
 12661            spec.target = '' + tgt; // id must be string
 12662  
 12663            move = true;
 12664          }
 12665  
 12666          if (move) {
 12667            ele = ele.move(spec);
 12668          }
 12669        } else {
 12670          // parent is immutable via data()
 12671          var newParentValSpecd = 'parent' in obj.data;
 12672          var parent = obj.data.parent;
 12673  
 12674          if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
 12675            if (parent === undefined) {
 12676              // can't set undefined imperatively, so use null
 12677              parent = null;
 12678            }
 12679  
 12680            if (parent != null) {
 12681              parent = '' + parent; // id must be string
 12682            }
 12683  
 12684            ele = ele.move({
 12685              parent: parent
 12686            });
 12687          }
 12688        }
 12689      }
 12690  
 12691      if (obj.position) {
 12692        ele.position(obj.position);
 12693      } // ignore group -- immutable
 12694  
 12695  
 12696      var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
 12697        var obj_k = obj[k];
 12698  
 12699        if (obj_k != null && obj_k !== p[k]) {
 12700          if (obj_k) {
 12701            ele[trueFnName]();
 12702          } else {
 12703            ele[falseFnName]();
 12704          }
 12705        }
 12706      };
 12707  
 12708      checkSwitch('removed', 'remove', 'restore');
 12709      checkSwitch('selected', 'select', 'unselect');
 12710      checkSwitch('selectable', 'selectify', 'unselectify');
 12711      checkSwitch('locked', 'lock', 'unlock');
 12712      checkSwitch('grabbable', 'grabify', 'ungrabify');
 12713      checkSwitch('pannable', 'panify', 'unpanify');
 12714  
 12715      if (obj.classes != null) {
 12716        ele.classes(obj.classes);
 12717      }
 12718  
 12719      cy.endBatch();
 12720      return this;
 12721    } else if (obj === undefined) {
 12722      // get
 12723      var json = {
 12724        data: copy(p.data),
 12725        position: copy(p.position),
 12726        group: p.group,
 12727        removed: p.removed,
 12728        selected: p.selected,
 12729        selectable: p.selectable,
 12730        locked: p.locked,
 12731        grabbable: p.grabbable,
 12732        pannable: p.pannable,
 12733        classes: null
 12734      };
 12735      json.classes = '';
 12736      var i = 0;
 12737      p.classes.forEach(function (cls) {
 12738        return json.classes += i++ === 0 ? cls : ' ' + cls;
 12739      });
 12740      return json;
 12741    }
 12742  };
 12743  
 12744  elesfn$u.jsons = function () {
 12745    var jsons = [];
 12746  
 12747    for (var i = 0; i < this.length; i++) {
 12748      var ele = this[i];
 12749      var json = ele.json();
 12750      jsons.push(json);
 12751    }
 12752  
 12753    return jsons;
 12754  };
 12755  
 12756  elesfn$u.clone = function () {
 12757    var cy = this.cy();
 12758    var elesArr = [];
 12759  
 12760    for (var i = 0; i < this.length; i++) {
 12761      var ele = this[i];
 12762      var json = ele.json();
 12763      var clone = new Element(cy, json, false); // NB no restore
 12764  
 12765      elesArr.push(clone);
 12766    }
 12767  
 12768    return new Collection(cy, elesArr);
 12769  };
 12770  
 12771  elesfn$u.copy = elesfn$u.clone;
 12772  
 12773  elesfn$u.restore = function () {
 12774    var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
 12775    var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
 12776    var self = this;
 12777    var cy = self.cy();
 12778    var cy_p = cy._private; // create arrays of nodes and edges, since we need to
 12779    // restore the nodes first
 12780  
 12781    var nodes = [];
 12782    var edges = [];
 12783    var elements;
 12784  
 12785    for (var _i2 = 0, l = self.length; _i2 < l; _i2++) {
 12786      var ele = self[_i2];
 12787  
 12788      if (addToPool && !ele.removed()) {
 12789        // don't need to handle this ele
 12790        continue;
 12791      } // keep nodes first in the array and edges after
 12792  
 12793  
 12794      if (ele.isNode()) {
 12795        // put to front of array if node
 12796        nodes.push(ele);
 12797      } else {
 12798        // put to end of array if edge
 12799        edges.push(ele);
 12800      }
 12801    }
 12802  
 12803    elements = nodes.concat(edges);
 12804    var i;
 12805  
 12806    var removeFromElements = function removeFromElements() {
 12807      elements.splice(i, 1);
 12808      i--;
 12809    }; // now, restore each element
 12810  
 12811  
 12812    for (i = 0; i < elements.length; i++) {
 12813      var _ele = elements[i];
 12814      var _private = _ele._private;
 12815      var _data3 = _private.data; // the traversal cache should start fresh when ele is added
 12816  
 12817      _ele.clearTraversalCache(); // set id and validate
 12818  
 12819  
 12820      if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
 12821        _data3.id = idFactory.generate(cy, _ele);
 12822      } else if (number(_data3.id)) {
 12823        _data3.id = '' + _data3.id; // now it's a string
 12824      } else if (emptyString(_data3.id) || !string(_data3.id)) {
 12825        error('Can not create element with invalid string ID `' + _data3.id + '`'); // can't create element if it has empty string as id or non-string id
 12826  
 12827        removeFromElements();
 12828        continue;
 12829      } else if (cy.hasElementWithId(_data3.id)) {
 12830        error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
 12831  
 12832        removeFromElements();
 12833        continue;
 12834      }
 12835  
 12836      var id = _data3.id; // id is finalised, now let's keep a ref
 12837  
 12838      if (_ele.isNode()) {
 12839        // extra checks for nodes
 12840        var pos = _private.position; // make sure the nodes have a defined position
 12841  
 12842        if (pos.x == null) {
 12843          pos.x = 0;
 12844        }
 12845  
 12846        if (pos.y == null) {
 12847          pos.y = 0;
 12848        }
 12849      }
 12850  
 12851      if (_ele.isEdge()) {
 12852        // extra checks for edges
 12853        var edge = _ele;
 12854        var fields = ['source', 'target'];
 12855        var fieldsLength = fields.length;
 12856        var badSourceOrTarget = false;
 12857  
 12858        for (var j = 0; j < fieldsLength; j++) {
 12859          var field = fields[j];
 12860          var val = _data3[field];
 12861  
 12862          if (number(val)) {
 12863            val = _data3[field] = '' + _data3[field]; // now string
 12864          }
 12865  
 12866          if (val == null || val === '') {
 12867            // can't create if source or target is not defined properly
 12868            error('Can not create edge `' + id + '` with unspecified ' + field);
 12869            badSourceOrTarget = true;
 12870          } else if (!cy.hasElementWithId(val)) {
 12871            // can't create edge if one of its nodes doesn't exist
 12872            error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
 12873            badSourceOrTarget = true;
 12874          }
 12875        }
 12876  
 12877        if (badSourceOrTarget) {
 12878          removeFromElements();
 12879          continue;
 12880        } // can't create this
 12881  
 12882  
 12883        var src = cy.getElementById(_data3.source);
 12884        var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
 12885  
 12886        if (src.same(tgt)) {
 12887          src._private.edges.push(edge);
 12888        } else {
 12889          src._private.edges.push(edge);
 12890  
 12891          tgt._private.edges.push(edge);
 12892        }
 12893  
 12894        edge._private.source = src;
 12895        edge._private.target = tgt;
 12896      } // if is edge
 12897      // create mock ids / indexes maps for element so it can be used like collections
 12898  
 12899  
 12900      _private.map = new Map$1();
 12901  
 12902      _private.map.set(id, {
 12903        ele: _ele,
 12904        index: 0
 12905      });
 12906  
 12907      _private.removed = false;
 12908  
 12909      if (addToPool) {
 12910        cy.addToPool(_ele);
 12911      }
 12912    } // for each element
 12913    // do compound node sanity checks
 12914  
 12915  
 12916    for (var _i3 = 0; _i3 < nodes.length; _i3++) {
 12917      // each node
 12918      var node = nodes[_i3];
 12919      var _data4 = node._private.data;
 12920  
 12921      if (number(_data4.parent)) {
 12922        // then automake string
 12923        _data4.parent = '' + _data4.parent;
 12924      }
 12925  
 12926      var parentId = _data4.parent;
 12927      var specifiedParent = parentId != null;
 12928  
 12929      if (specifiedParent) {
 12930        var parent = cy.getElementById(parentId);
 12931  
 12932        if (parent.empty()) {
 12933          // non-existant parent; just remove it
 12934          _data4.parent = undefined;
 12935        } else {
 12936          var selfAsParent = false;
 12937          var ancestor = parent;
 12938  
 12939          while (!ancestor.empty()) {
 12940            if (node.same(ancestor)) {
 12941              // mark self as parent and remove from data
 12942              selfAsParent = true;
 12943              _data4.parent = undefined; // remove parent reference
 12944              // exit or we loop forever
 12945  
 12946              break;
 12947            }
 12948  
 12949            ancestor = ancestor.parent();
 12950          }
 12951  
 12952          if (!selfAsParent) {
 12953            // connect with children
 12954            parent[0]._private.children.push(node);
 12955  
 12956            node._private.parent = parent[0]; // let the core know we have a compound graph
 12957  
 12958            cy_p.hasCompoundNodes = true;
 12959          }
 12960        } // else
 12961  
 12962      } // if specified parent
 12963  
 12964    } // for each node
 12965  
 12966  
 12967    if (elements.length > 0) {
 12968      var restored = new Collection(cy, elements);
 12969  
 12970      for (var _i4 = 0; _i4 < restored.length; _i4++) {
 12971        var _ele2 = restored[_i4];
 12972  
 12973        if (_ele2.isNode()) {
 12974          continue;
 12975        } // adding an edge invalidates the traversal caches for the parallel edges
 12976  
 12977  
 12978        _ele2.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
 12979  
 12980  
 12981        _ele2.source().clearTraversalCache();
 12982  
 12983        _ele2.target().clearTraversalCache();
 12984      }
 12985  
 12986      var toUpdateStyle;
 12987  
 12988      if (cy_p.hasCompoundNodes) {
 12989        toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
 12990      } else {
 12991        toUpdateStyle = restored;
 12992      }
 12993  
 12994      toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
 12995  
 12996      if (notifyRenderer) {
 12997        restored.emitAndNotify('add');
 12998      } else if (addToPool) {
 12999        restored.emit('add');
 13000      }
 13001    }
 13002  
 13003    return self; // chainability
 13004  };
 13005  
 13006  elesfn$u.removed = function () {
 13007    var ele = this[0];
 13008    return ele && ele._private.removed;
 13009  };
 13010  
 13011  elesfn$u.inside = function () {
 13012    var ele = this[0];
 13013    return ele && !ele._private.removed;
 13014  };
 13015  
 13016  elesfn$u.remove = function () {
 13017    var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
 13018    var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
 13019    var self = this;
 13020    var elesToRemove = [];
 13021    var elesToRemoveIds = {};
 13022    var cy = self._private.cy; // add connected edges
 13023  
 13024    function addConnectedEdges(node) {
 13025      var edges = node._private.edges;
 13026  
 13027      for (var i = 0; i < edges.length; i++) {
 13028        add(edges[i]);
 13029      }
 13030    } // add descendant nodes
 13031  
 13032  
 13033    function addChildren(node) {
 13034      var children = node._private.children;
 13035  
 13036      for (var i = 0; i < children.length; i++) {
 13037        add(children[i]);
 13038      }
 13039    }
 13040  
 13041    function add(ele) {
 13042      var alreadyAdded = elesToRemoveIds[ele.id()];
 13043  
 13044      if (removeFromPool && ele.removed() || alreadyAdded) {
 13045        return;
 13046      } else {
 13047        elesToRemoveIds[ele.id()] = true;
 13048      }
 13049  
 13050      if (ele.isNode()) {
 13051        elesToRemove.push(ele); // nodes are removed last
 13052  
 13053        addConnectedEdges(ele);
 13054        addChildren(ele);
 13055      } else {
 13056        elesToRemove.unshift(ele); // edges are removed first
 13057      }
 13058    } // make the list of elements to remove
 13059    // (may be removing more than specified due to connected edges etc)
 13060  
 13061  
 13062    for (var i = 0, l = self.length; i < l; i++) {
 13063      var ele = self[i];
 13064      add(ele);
 13065    }
 13066  
 13067    function removeEdgeRef(node, edge) {
 13068      var connectedEdges = node._private.edges;
 13069      removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
 13070  
 13071      node.clearTraversalCache();
 13072    }
 13073  
 13074    function removeParallelRef(pllEdge) {
 13075      // removing an edge invalidates the traversal caches for the parallel edges
 13076      pllEdge.clearTraversalCache();
 13077    }
 13078  
 13079    var alteredParents = [];
 13080    alteredParents.ids = {};
 13081  
 13082    function removeChildRef(parent, ele) {
 13083      ele = ele[0];
 13084      parent = parent[0];
 13085      var children = parent._private.children;
 13086      var pid = parent.id();
 13087      removeFromArray(children, ele); // remove parent => child ref
 13088  
 13089      ele._private.parent = null; // remove child => parent ref
 13090  
 13091      if (!alteredParents.ids[pid]) {
 13092        alteredParents.ids[pid] = true;
 13093        alteredParents.push(parent);
 13094      }
 13095    }
 13096  
 13097    self.dirtyCompoundBoundsCache();
 13098  
 13099    if (removeFromPool) {
 13100      cy.removeFromPool(elesToRemove); // remove from core pool
 13101    }
 13102  
 13103    for (var _i5 = 0; _i5 < elesToRemove.length; _i5++) {
 13104      var _ele3 = elesToRemove[_i5];
 13105  
 13106      if (_ele3.isEdge()) {
 13107        // remove references to this edge in its connected nodes
 13108        var src = _ele3.source()[0];
 13109  
 13110        var tgt = _ele3.target()[0];
 13111  
 13112        removeEdgeRef(src, _ele3);
 13113        removeEdgeRef(tgt, _ele3);
 13114  
 13115        var pllEdges = _ele3.parallelEdges();
 13116  
 13117        for (var j = 0; j < pllEdges.length; j++) {
 13118          var pllEdge = pllEdges[j];
 13119          removeParallelRef(pllEdge);
 13120  
 13121          if (pllEdge.isBundledBezier()) {
 13122            pllEdge.dirtyBoundingBoxCache();
 13123          }
 13124        }
 13125      } else {
 13126        // remove reference to parent
 13127        var parent = _ele3.parent();
 13128  
 13129        if (parent.length !== 0) {
 13130          removeChildRef(parent, _ele3);
 13131        }
 13132      }
 13133  
 13134      if (removeFromPool) {
 13135        // mark as removed
 13136        _ele3._private.removed = true;
 13137      }
 13138    } // check to see if we have a compound graph or not
 13139  
 13140  
 13141    var elesStillInside = cy._private.elements;
 13142    cy._private.hasCompoundNodes = false;
 13143  
 13144    for (var _i6 = 0; _i6 < elesStillInside.length; _i6++) {
 13145      var _ele4 = elesStillInside[_i6];
 13146  
 13147      if (_ele4.isParent()) {
 13148        cy._private.hasCompoundNodes = true;
 13149        break;
 13150      }
 13151    }
 13152  
 13153    var removedElements = new Collection(this.cy(), elesToRemove);
 13154  
 13155    if (removedElements.size() > 0) {
 13156      // must manually notify since trigger won't do this automatically once removed
 13157      if (notifyRenderer) {
 13158        removedElements.emitAndNotify('remove');
 13159      } else if (removeFromPool) {
 13160        removedElements.emit('remove');
 13161      }
 13162    } // the parents who were modified by the removal need their style updated
 13163  
 13164  
 13165    for (var _i7 = 0; _i7 < alteredParents.length; _i7++) {
 13166      var _ele5 = alteredParents[_i7];
 13167  
 13168      if (!removeFromPool || !_ele5.removed()) {
 13169        _ele5.updateStyle();
 13170      }
 13171    }
 13172  
 13173    return removedElements;
 13174  };
 13175  
 13176  elesfn$u.move = function (struct) {
 13177    var cy = this._private.cy;
 13178    var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
 13179    // (our calls to remove/restore do not remove from the graph or make events)
 13180  
 13181    var notifyRenderer = false;
 13182    var modifyPool = false;
 13183  
 13184    var toString = function toString(id) {
 13185      return id == null ? id : '' + id;
 13186    }; // id must be string
 13187  
 13188  
 13189    if (struct.source !== undefined || struct.target !== undefined) {
 13190      var srcId = toString(struct.source);
 13191      var tgtId = toString(struct.target);
 13192      var srcExists = srcId != null && cy.hasElementWithId(srcId);
 13193      var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
 13194  
 13195      if (srcExists || tgtExists) {
 13196        cy.batch(function () {
 13197          // avoid duplicate style updates
 13198          eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
 13199  
 13200          eles.emitAndNotify('moveout');
 13201  
 13202          for (var i = 0; i < eles.length; i++) {
 13203            var ele = eles[i];
 13204            var _data5 = ele._private.data;
 13205  
 13206            if (ele.isEdge()) {
 13207              if (srcExists) {
 13208                _data5.source = srcId;
 13209              }
 13210  
 13211              if (tgtExists) {
 13212                _data5.target = tgtId;
 13213              }
 13214            }
 13215          }
 13216  
 13217          eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
 13218        });
 13219        eles.emitAndNotify('move');
 13220      }
 13221    } else if (struct.parent !== undefined) {
 13222      // move node to new parent
 13223      var parentId = toString(struct.parent);
 13224      var parentExists = parentId === null || cy.hasElementWithId(parentId);
 13225  
 13226      if (parentExists) {
 13227        var pidToAssign = parentId === null ? undefined : parentId;
 13228        cy.batch(function () {
 13229          // avoid duplicate style updates
 13230          var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
 13231  
 13232          updated.emitAndNotify('moveout');
 13233  
 13234          for (var i = 0; i < eles.length; i++) {
 13235            var ele = eles[i];
 13236            var _data6 = ele._private.data;
 13237  
 13238            if (ele.isNode()) {
 13239              _data6.parent = pidToAssign;
 13240            }
 13241          }
 13242  
 13243          updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
 13244        });
 13245        eles.emitAndNotify('move');
 13246      }
 13247    }
 13248  
 13249    return this;
 13250  };
 13251  
 13252  [elesfn$c, elesfn$d, elesfn$e, elesfn$f, elesfn$g, data$1, elesfn$i, dimensions, elesfn$m, elesfn$n, elesfn$o, elesfn$p, elesfn$q, elesfn$r, elesfn$s, elesfn$t].forEach(function (props) {
 13253    extend(elesfn$u, props);
 13254  });
 13255  
 13256  var corefn = {
 13257    add: function add(opts) {
 13258      var elements;
 13259      var cy = this; // add the elements
 13260  
 13261      if (elementOrCollection(opts)) {
 13262        var eles = opts;
 13263  
 13264        if (eles._private.cy === cy) {
 13265          // same instance => just restore
 13266          elements = eles.restore();
 13267        } else {
 13268          // otherwise, copy from json
 13269          var jsons = [];
 13270  
 13271          for (var i = 0; i < eles.length; i++) {
 13272            var ele = eles[i];
 13273            jsons.push(ele.json());
 13274          }
 13275  
 13276          elements = new Collection(cy, jsons);
 13277        }
 13278      } // specify an array of options
 13279      else if (array(opts)) {
 13280          var _jsons = opts;
 13281          elements = new Collection(cy, _jsons);
 13282        } // specify via opts.nodes and opts.edges
 13283        else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
 13284            var elesByGroup = opts;
 13285            var _jsons2 = [];
 13286            var grs = ['nodes', 'edges'];
 13287  
 13288            for (var _i = 0, il = grs.length; _i < il; _i++) {
 13289              var group = grs[_i];
 13290              var elesArray = elesByGroup[group];
 13291  
 13292              if (array(elesArray)) {
 13293                for (var j = 0, jl = elesArray.length; j < jl; j++) {
 13294                  var json = extend({
 13295                    group: group
 13296                  }, elesArray[j]);
 13297  
 13298                  _jsons2.push(json);
 13299                }
 13300              }
 13301            }
 13302  
 13303            elements = new Collection(cy, _jsons2);
 13304          } // specify options for one element
 13305          else {
 13306              var _json = opts;
 13307              elements = new Element(cy, _json).collection();
 13308            }
 13309  
 13310      return elements;
 13311    },
 13312    remove: function remove(collection) {
 13313      if (elementOrCollection(collection)) ; else if (string(collection)) {
 13314        var selector = collection;
 13315        collection = this.$(selector);
 13316      }
 13317  
 13318      return collection.remove();
 13319    }
 13320  };
 13321  
 13322  /* global Float32Array */
 13323  
 13324  /*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
 13325  function generateCubicBezier(mX1, mY1, mX2, mY2) {
 13326    var NEWTON_ITERATIONS = 4,
 13327        NEWTON_MIN_SLOPE = 0.001,
 13328        SUBDIVISION_PRECISION = 0.0000001,
 13329        SUBDIVISION_MAX_ITERATIONS = 10,
 13330        kSplineTableSize = 11,
 13331        kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
 13332        float32ArraySupported = typeof Float32Array !== 'undefined';
 13333    /* Must contain four arguments. */
 13334  
 13335    if (arguments.length !== 4) {
 13336      return false;
 13337    }
 13338    /* Arguments must be numbers. */
 13339  
 13340  
 13341    for (var i = 0; i < 4; ++i) {
 13342      if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
 13343        return false;
 13344      }
 13345    }
 13346    /* X values must be in the [0, 1] range. */
 13347  
 13348  
 13349    mX1 = Math.min(mX1, 1);
 13350    mX2 = Math.min(mX2, 1);
 13351    mX1 = Math.max(mX1, 0);
 13352    mX2 = Math.max(mX2, 0);
 13353    var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
 13354  
 13355    function A(aA1, aA2) {
 13356      return 1.0 - 3.0 * aA2 + 3.0 * aA1;
 13357    }
 13358  
 13359    function B(aA1, aA2) {
 13360      return 3.0 * aA2 - 6.0 * aA1;
 13361    }
 13362  
 13363    function C(aA1) {
 13364      return 3.0 * aA1;
 13365    }
 13366  
 13367    function calcBezier(aT, aA1, aA2) {
 13368      return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
 13369    }
 13370  
 13371    function getSlope(aT, aA1, aA2) {
 13372      return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
 13373    }
 13374  
 13375    function newtonRaphsonIterate(aX, aGuessT) {
 13376      for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
 13377        var currentSlope = getSlope(aGuessT, mX1, mX2);
 13378  
 13379        if (currentSlope === 0.0) {
 13380          return aGuessT;
 13381        }
 13382  
 13383        var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
 13384        aGuessT -= currentX / currentSlope;
 13385      }
 13386  
 13387      return aGuessT;
 13388    }
 13389  
 13390    function calcSampleValues() {
 13391      for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
 13392        mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
 13393      }
 13394    }
 13395  
 13396    function binarySubdivide(aX, aA, aB) {
 13397      var currentX,
 13398          currentT,
 13399          i = 0;
 13400  
 13401      do {
 13402        currentT = aA + (aB - aA) / 2.0;
 13403        currentX = calcBezier(currentT, mX1, mX2) - aX;
 13404  
 13405        if (currentX > 0.0) {
 13406          aB = currentT;
 13407        } else {
 13408          aA = currentT;
 13409        }
 13410      } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
 13411  
 13412      return currentT;
 13413    }
 13414  
 13415    function getTForX(aX) {
 13416      var intervalStart = 0.0,
 13417          currentSample = 1,
 13418          lastSample = kSplineTableSize - 1;
 13419  
 13420      for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
 13421        intervalStart += kSampleStepSize;
 13422      }
 13423  
 13424      --currentSample;
 13425      var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
 13426          guessForT = intervalStart + dist * kSampleStepSize,
 13427          initialSlope = getSlope(guessForT, mX1, mX2);
 13428  
 13429      if (initialSlope >= NEWTON_MIN_SLOPE) {
 13430        return newtonRaphsonIterate(aX, guessForT);
 13431      } else if (initialSlope === 0.0) {
 13432        return guessForT;
 13433      } else {
 13434        return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
 13435      }
 13436    }
 13437  
 13438    var _precomputed = false;
 13439  
 13440    function precompute() {
 13441      _precomputed = true;
 13442  
 13443      if (mX1 !== mY1 || mX2 !== mY2) {
 13444        calcSampleValues();
 13445      }
 13446    }
 13447  
 13448    var f = function f(aX) {
 13449      if (!_precomputed) {
 13450        precompute();
 13451      }
 13452  
 13453      if (mX1 === mY1 && mX2 === mY2) {
 13454        return aX;
 13455      }
 13456  
 13457      if (aX === 0) {
 13458        return 0;
 13459      }
 13460  
 13461      if (aX === 1) {
 13462        return 1;
 13463      }
 13464  
 13465      return calcBezier(getTForX(aX), mY1, mY2);
 13466    };
 13467  
 13468    f.getControlPoints = function () {
 13469      return [{
 13470        x: mX1,
 13471        y: mY1
 13472      }, {
 13473        x: mX2,
 13474        y: mY2
 13475      }];
 13476    };
 13477  
 13478    var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
 13479  
 13480    f.toString = function () {
 13481      return str;
 13482    };
 13483  
 13484    return f;
 13485  }
 13486  
 13487  /*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
 13488  
 13489  /* 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
 13490     then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
 13491  var generateSpringRK4 = function () {
 13492    function springAccelerationForState(state) {
 13493      return -state.tension * state.x - state.friction * state.v;
 13494    }
 13495  
 13496    function springEvaluateStateWithDerivative(initialState, dt, derivative) {
 13497      var state = {
 13498        x: initialState.x + derivative.dx * dt,
 13499        v: initialState.v + derivative.dv * dt,
 13500        tension: initialState.tension,
 13501        friction: initialState.friction
 13502      };
 13503      return {
 13504        dx: state.v,
 13505        dv: springAccelerationForState(state)
 13506      };
 13507    }
 13508  
 13509    function springIntegrateState(state, dt) {
 13510      var a = {
 13511        dx: state.v,
 13512        dv: springAccelerationForState(state)
 13513      },
 13514          b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
 13515          c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
 13516          d = springEvaluateStateWithDerivative(state, dt, c),
 13517          dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
 13518          dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
 13519      state.x = state.x + dxdt * dt;
 13520      state.v = state.v + dvdt * dt;
 13521      return state;
 13522    }
 13523  
 13524    return function springRK4Factory(tension, friction, duration) {
 13525      var initState = {
 13526        x: -1,
 13527        v: 0,
 13528        tension: null,
 13529        friction: null
 13530      },
 13531          path = [0],
 13532          time_lapsed = 0,
 13533          tolerance = 1 / 10000,
 13534          DT = 16 / 1000,
 13535          have_duration,
 13536          dt,
 13537          last_state;
 13538      tension = parseFloat(tension) || 500;
 13539      friction = parseFloat(friction) || 20;
 13540      duration = duration || null;
 13541      initState.tension = tension;
 13542      initState.friction = friction;
 13543      have_duration = duration !== null;
 13544      /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
 13545  
 13546      if (have_duration) {
 13547        /* Run the simulation without a duration. */
 13548        time_lapsed = springRK4Factory(tension, friction);
 13549        /* Compute the adjusted time delta. */
 13550  
 13551        dt = time_lapsed / duration * DT;
 13552      } else {
 13553        dt = DT;
 13554      }
 13555  
 13556      for (;;) {
 13557        /* Next/step function .*/
 13558        last_state = springIntegrateState(last_state || initState, dt);
 13559        /* Store the position. */
 13560  
 13561        path.push(1 + last_state.x);
 13562        time_lapsed += 16;
 13563        /* If the change threshold is reached, break. */
 13564  
 13565        if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
 13566          break;
 13567        }
 13568      }
 13569      /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
 13570         computed path and returns a snapshot of the position according to a given percentComplete. */
 13571  
 13572  
 13573      return !have_duration ? time_lapsed : function (percentComplete) {
 13574        return path[percentComplete * (path.length - 1) | 0];
 13575      };
 13576    };
 13577  }();
 13578  
 13579  var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
 13580    var bezier = generateCubicBezier(t1, p1, t2, p2);
 13581    return function (start, end, percent) {
 13582      return start + (end - start) * bezier(percent);
 13583    };
 13584  };
 13585  
 13586  var easings = {
 13587    'linear': function linear(start, end, percent) {
 13588      return start + (end - start) * percent;
 13589    },
 13590    // default easings
 13591    'ease': cubicBezier(0.25, 0.1, 0.25, 1),
 13592    'ease-in': cubicBezier(0.42, 0, 1, 1),
 13593    'ease-out': cubicBezier(0, 0, 0.58, 1),
 13594    'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
 13595    // sine
 13596    'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
 13597    'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
 13598    'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
 13599    // quad
 13600    'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
 13601    'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
 13602    'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
 13603    // cubic
 13604    'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
 13605    'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
 13606    'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
 13607    // quart
 13608    'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
 13609    'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
 13610    'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
 13611    // quint
 13612    'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
 13613    'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
 13614    'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
 13615    // expo
 13616    'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
 13617    'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
 13618    'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
 13619    // circ
 13620    'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
 13621    'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
 13622    'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
 13623    // user param easings...
 13624    'spring': function spring(tension, friction, duration) {
 13625      if (duration === 0) {
 13626        // can't get a spring w/ duration 0
 13627        return easings.linear; // duration 0 => jump to end so impl doesn't matter
 13628      }
 13629  
 13630      var spring = generateSpringRK4(tension, friction, duration);
 13631      return function (start, end, percent) {
 13632        return start + (end - start) * spring(percent);
 13633      };
 13634    },
 13635    'cubic-bezier': cubicBezier
 13636  };
 13637  
 13638  function getEasedValue(type, start, end, percent, easingFn) {
 13639    if (percent === 1) {
 13640      return end;
 13641    }
 13642  
 13643    if (start === end) {
 13644      return end;
 13645    }
 13646  
 13647    var val = easingFn(start, end, percent);
 13648  
 13649    if (type == null) {
 13650      return val;
 13651    }
 13652  
 13653    if (type.roundValue || type.color) {
 13654      val = Math.round(val);
 13655    }
 13656  
 13657    if (type.min !== undefined) {
 13658      val = Math.max(val, type.min);
 13659    }
 13660  
 13661    if (type.max !== undefined) {
 13662      val = Math.min(val, type.max);
 13663    }
 13664  
 13665    return val;
 13666  }
 13667  
 13668  function getValue(prop, spec) {
 13669    if (prop.pfValue != null || prop.value != null) {
 13670      if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
 13671        return prop.pfValue;
 13672      } else {
 13673        return prop.value;
 13674      }
 13675    } else {
 13676      return prop;
 13677    }
 13678  }
 13679  
 13680  function ease(startProp, endProp, percent, easingFn, propSpec) {
 13681    var type = propSpec != null ? propSpec.type : null;
 13682  
 13683    if (percent < 0) {
 13684      percent = 0;
 13685    } else if (percent > 1) {
 13686      percent = 1;
 13687    }
 13688  
 13689    var start = getValue(startProp, propSpec);
 13690    var end = getValue(endProp, propSpec);
 13691  
 13692    if (number(start) && number(end)) {
 13693      return getEasedValue(type, start, end, percent, easingFn);
 13694    } else if (array(start) && array(end)) {
 13695      var easedArr = [];
 13696  
 13697      for (var i = 0; i < end.length; i++) {
 13698        var si = start[i];
 13699        var ei = end[i];
 13700  
 13701        if (si != null && ei != null) {
 13702          var val = getEasedValue(type, si, ei, percent, easingFn);
 13703          easedArr.push(val);
 13704        } else {
 13705          easedArr.push(ei);
 13706        }
 13707      }
 13708  
 13709      return easedArr;
 13710    }
 13711  
 13712    return undefined;
 13713  }
 13714  
 13715  function step(self, ani, now, isCore) {
 13716    var isEles = !isCore;
 13717    var _p = self._private;
 13718    var ani_p = ani._private;
 13719    var pEasing = ani_p.easing;
 13720    var startTime = ani_p.startTime;
 13721    var cy = isCore ? self : self.cy();
 13722    var style = cy.style();
 13723  
 13724    if (!ani_p.easingImpl) {
 13725      if (pEasing == null) {
 13726        // use default
 13727        ani_p.easingImpl = easings['linear'];
 13728      } else {
 13729        // then define w/ name
 13730        var easingVals;
 13731  
 13732        if (string(pEasing)) {
 13733          var easingProp = style.parse('transition-timing-function', pEasing);
 13734          easingVals = easingProp.value;
 13735        } else {
 13736          // then assume preparsed array
 13737          easingVals = pEasing;
 13738        }
 13739  
 13740        var name, args;
 13741  
 13742        if (string(easingVals)) {
 13743          name = easingVals;
 13744          args = [];
 13745        } else {
 13746          name = easingVals[1];
 13747          args = easingVals.slice(2).map(function (n) {
 13748            return +n;
 13749          });
 13750        }
 13751  
 13752        if (args.length > 0) {
 13753          // create with args
 13754          if (name === 'spring') {
 13755            args.push(ani_p.duration); // need duration to generate spring
 13756          }
 13757  
 13758          ani_p.easingImpl = easings[name].apply(null, args);
 13759        } else {
 13760          // static impl by name
 13761          ani_p.easingImpl = easings[name];
 13762        }
 13763      }
 13764    }
 13765  
 13766    var easing = ani_p.easingImpl;
 13767    var percent;
 13768  
 13769    if (ani_p.duration === 0) {
 13770      percent = 1;
 13771    } else {
 13772      percent = (now - startTime) / ani_p.duration;
 13773    }
 13774  
 13775    if (ani_p.applying) {
 13776      percent = ani_p.progress;
 13777    }
 13778  
 13779    if (percent < 0) {
 13780      percent = 0;
 13781    } else if (percent > 1) {
 13782      percent = 1;
 13783    }
 13784  
 13785    if (ani_p.delay == null) {
 13786      // then update
 13787      var startPos = ani_p.startPosition;
 13788      var endPos = ani_p.position;
 13789  
 13790      if (endPos && isEles && !self.locked()) {
 13791        var newPos = {};
 13792  
 13793        if (valid(startPos.x, endPos.x)) {
 13794          newPos.x = ease(startPos.x, endPos.x, percent, easing);
 13795        }
 13796  
 13797        if (valid(startPos.y, endPos.y)) {
 13798          newPos.y = ease(startPos.y, endPos.y, percent, easing);
 13799        }
 13800  
 13801        self.position(newPos);
 13802      }
 13803  
 13804      var startPan = ani_p.startPan;
 13805      var endPan = ani_p.pan;
 13806      var pan = _p.pan;
 13807      var animatingPan = endPan != null && isCore;
 13808  
 13809      if (animatingPan) {
 13810        if (valid(startPan.x, endPan.x)) {
 13811          pan.x = ease(startPan.x, endPan.x, percent, easing);
 13812        }
 13813  
 13814        if (valid(startPan.y, endPan.y)) {
 13815          pan.y = ease(startPan.y, endPan.y, percent, easing);
 13816        }
 13817  
 13818        self.emit('pan');
 13819      }
 13820  
 13821      var startZoom = ani_p.startZoom;
 13822      var endZoom = ani_p.zoom;
 13823      var animatingZoom = endZoom != null && isCore;
 13824  
 13825      if (animatingZoom) {
 13826        if (valid(startZoom, endZoom)) {
 13827          _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
 13828        }
 13829  
 13830        self.emit('zoom');
 13831      }
 13832  
 13833      if (animatingPan || animatingZoom) {
 13834        self.emit('viewport');
 13835      }
 13836  
 13837      var props = ani_p.style;
 13838  
 13839      if (props && props.length > 0 && isEles) {
 13840        for (var i = 0; i < props.length; i++) {
 13841          var prop = props[i];
 13842          var _name = prop.name;
 13843          var end = prop;
 13844          var start = ani_p.startStyle[_name];
 13845          var propSpec = style.properties[start.name];
 13846          var easedVal = ease(start, end, percent, easing, propSpec);
 13847          style.overrideBypass(self, _name, easedVal);
 13848        } // for props
 13849  
 13850  
 13851        self.emit('style');
 13852      } // if
 13853  
 13854    }
 13855  
 13856    ani_p.progress = percent;
 13857    return percent;
 13858  }
 13859  
 13860  function valid(start, end) {
 13861    if (start == null || end == null) {
 13862      return false;
 13863    }
 13864  
 13865    if (number(start) && number(end)) {
 13866      return true;
 13867    } else if (start && end) {
 13868      return true;
 13869    }
 13870  
 13871    return false;
 13872  }
 13873  
 13874  function startAnimation(self, ani, now, isCore) {
 13875    var ani_p = ani._private;
 13876    ani_p.started = true;
 13877    ani_p.startTime = now - ani_p.progress * ani_p.duration;
 13878  }
 13879  
 13880  function stepAll(now, cy) {
 13881    var eles = cy._private.aniEles;
 13882    var doneEles = [];
 13883  
 13884    function stepOne(ele, isCore) {
 13885      var _p = ele._private;
 13886      var current = _p.animation.current;
 13887      var queue = _p.animation.queue;
 13888      var ranAnis = false; // cancel all animations on display:none ele
 13889  
 13890      if (!isCore && ele.pstyle('display').value === 'none') {
 13891        // put all current and queue animations in this tick's current list
 13892        // and empty the lists for the element
 13893        current = current.splice(0, current.length).concat(queue.splice(0, queue.length)); // stop all animations
 13894  
 13895        for (var i = 0; i < current.length; i++) {
 13896          current[i].stop();
 13897        }
 13898      } // if nothing currently animating, get something from the queue
 13899  
 13900  
 13901      if (current.length === 0) {
 13902        var next = queue.shift();
 13903  
 13904        if (next) {
 13905          current.push(next);
 13906        }
 13907      }
 13908  
 13909      var callbacks = function callbacks(_callbacks) {
 13910        for (var j = _callbacks.length - 1; j >= 0; j--) {
 13911          var cb = _callbacks[j];
 13912          cb();
 13913        }
 13914  
 13915        _callbacks.splice(0, _callbacks.length);
 13916      }; // step and remove if done
 13917  
 13918  
 13919      for (var _i = current.length - 1; _i >= 0; _i--) {
 13920        var ani = current[_i];
 13921        var ani_p = ani._private;
 13922  
 13923        if (ani_p.stopped) {
 13924          current.splice(_i, 1);
 13925          ani_p.hooked = false;
 13926          ani_p.playing = false;
 13927          ani_p.started = false;
 13928          callbacks(ani_p.frames);
 13929          continue;
 13930        }
 13931  
 13932        if (!ani_p.playing && !ani_p.applying) {
 13933          continue;
 13934        } // an apply() while playing shouldn't do anything
 13935  
 13936  
 13937        if (ani_p.playing && ani_p.applying) {
 13938          ani_p.applying = false;
 13939        }
 13940  
 13941        if (!ani_p.started) {
 13942          startAnimation(ele, ani, now);
 13943        }
 13944  
 13945        step(ele, ani, now, isCore);
 13946  
 13947        if (ani_p.applying) {
 13948          ani_p.applying = false;
 13949        }
 13950  
 13951        callbacks(ani_p.frames);
 13952  
 13953        if (ani_p.step != null) {
 13954          ani_p.step(now);
 13955        }
 13956  
 13957        if (ani.completed()) {
 13958          current.splice(_i, 1);
 13959          ani_p.hooked = false;
 13960          ani_p.playing = false;
 13961          ani_p.started = false;
 13962          callbacks(ani_p.completes);
 13963        }
 13964  
 13965        ranAnis = true;
 13966      }
 13967  
 13968      if (!isCore && current.length === 0 && queue.length === 0) {
 13969        doneEles.push(ele);
 13970      }
 13971  
 13972      return ranAnis;
 13973    } // stepElement
 13974    // handle all eles
 13975  
 13976  
 13977    var ranEleAni = false;
 13978  
 13979    for (var e = 0; e < eles.length; e++) {
 13980      var ele = eles[e];
 13981      var handledThisEle = stepOne(ele);
 13982      ranEleAni = ranEleAni || handledThisEle;
 13983    } // each element
 13984  
 13985  
 13986    var ranCoreAni = stepOne(cy, true); // notify renderer
 13987  
 13988    if (ranEleAni || ranCoreAni) {
 13989      if (eles.length > 0) {
 13990        cy.notify('draw', eles);
 13991      } else {
 13992        cy.notify('draw');
 13993      }
 13994    } // remove elements from list of currently animating if its queues are empty
 13995  
 13996  
 13997    eles.unmerge(doneEles);
 13998    cy.emit('step');
 13999  } // stepAll
 14000  
 14001  var corefn$1 = {
 14002    // pull in animation functions
 14003    animate: define$3.animate(),
 14004    animation: define$3.animation(),
 14005    animated: define$3.animated(),
 14006    clearQueue: define$3.clearQueue(),
 14007    delay: define$3.delay(),
 14008    delayAnimation: define$3.delayAnimation(),
 14009    stop: define$3.stop(),
 14010    addToAnimationPool: function addToAnimationPool(eles) {
 14011      var cy = this;
 14012  
 14013      if (!cy.styleEnabled()) {
 14014        return;
 14015      } // save cycles when no style used
 14016  
 14017  
 14018      cy._private.aniEles.merge(eles);
 14019    },
 14020    stopAnimationLoop: function stopAnimationLoop() {
 14021      this._private.animationsRunning = false;
 14022    },
 14023    startAnimationLoop: function startAnimationLoop() {
 14024      var cy = this;
 14025      cy._private.animationsRunning = true;
 14026  
 14027      if (!cy.styleEnabled()) {
 14028        return;
 14029      } // save cycles when no style used
 14030      // NB the animation loop will exec in headless environments if style enabled
 14031      // and explicit cy.destroy() is necessary to stop the loop
 14032  
 14033  
 14034      function headlessStep() {
 14035        if (!cy._private.animationsRunning) {
 14036          return;
 14037        }
 14038  
 14039        requestAnimationFrame(function animationStep(now) {
 14040          stepAll(now, cy);
 14041          headlessStep();
 14042        });
 14043      }
 14044  
 14045      var renderer = cy.renderer();
 14046  
 14047      if (renderer && renderer.beforeRender) {
 14048        // let the renderer schedule animations
 14049        renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
 14050          stepAll(now, cy);
 14051        }, renderer.beforeRenderPriorities.animations);
 14052      } else {
 14053        // manage the animation loop ourselves
 14054        headlessStep(); // first call
 14055      }
 14056    }
 14057  };
 14058  
 14059  var emitterOptions$1 = {
 14060    qualifierCompare: function qualifierCompare(selector1, selector2) {
 14061      if (selector1 == null || selector2 == null) {
 14062        return selector1 == null && selector2 == null;
 14063      } else {
 14064        return selector1.sameText(selector2);
 14065      }
 14066    },
 14067    eventMatches: function eventMatches(cy, listener, eventObj) {
 14068      var selector = listener.qualifier;
 14069  
 14070      if (selector != null) {
 14071        return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
 14072      }
 14073  
 14074      return true;
 14075    },
 14076    addEventFields: function addEventFields(cy, evt) {
 14077      evt.cy = cy;
 14078      evt.target = cy;
 14079    },
 14080    callbackContext: function callbackContext(cy, listener, eventObj) {
 14081      return listener.qualifier != null ? eventObj.target : cy;
 14082    }
 14083  };
 14084  
 14085  var argSelector$1 = function argSelector(arg) {
 14086    if (string(arg)) {
 14087      return new Selector(arg);
 14088    } else {
 14089      return arg;
 14090    }
 14091  };
 14092  
 14093  var elesfn$v = {
 14094    createEmitter: function createEmitter() {
 14095      var _p = this._private;
 14096  
 14097      if (!_p.emitter) {
 14098        _p.emitter = new Emitter(emitterOptions$1, this);
 14099      }
 14100  
 14101      return this;
 14102    },
 14103    emitter: function emitter() {
 14104      return this._private.emitter;
 14105    },
 14106    on: function on(events, selector, callback) {
 14107      this.emitter().on(events, argSelector$1(selector), callback);
 14108      return this;
 14109    },
 14110    removeListener: function removeListener(events, selector, callback) {
 14111      this.emitter().removeListener(events, argSelector$1(selector), callback);
 14112      return this;
 14113    },
 14114    removeAllListeners: function removeAllListeners() {
 14115      this.emitter().removeAllListeners();
 14116      return this;
 14117    },
 14118    one: function one(events, selector, callback) {
 14119      this.emitter().one(events, argSelector$1(selector), callback);
 14120      return this;
 14121    },
 14122    once: function once(events, selector, callback) {
 14123      this.emitter().one(events, argSelector$1(selector), callback);
 14124      return this;
 14125    },
 14126    emit: function emit(events, extraParams) {
 14127      this.emitter().emit(events, extraParams);
 14128      return this;
 14129    },
 14130    emitAndNotify: function emitAndNotify(event, eles) {
 14131      this.emit(event);
 14132      this.notify(event, eles);
 14133      return this;
 14134    }
 14135  };
 14136  define$3.eventAliasesOn(elesfn$v);
 14137  
 14138  var corefn$2 = {
 14139    png: function png(options) {
 14140      var renderer = this._private.renderer;
 14141      options = options || {};
 14142      return renderer.png(options);
 14143    },
 14144    jpg: function jpg(options) {
 14145      var renderer = this._private.renderer;
 14146      options = options || {};
 14147      options.bg = options.bg || '#fff';
 14148      return renderer.jpg(options);
 14149    }
 14150  };
 14151  corefn$2.jpeg = corefn$2.jpg;
 14152  
 14153  var corefn$3 = {
 14154    layout: function layout(options) {
 14155      var cy = this;
 14156  
 14157      if (options == null) {
 14158        error('Layout options must be specified to make a layout');
 14159        return;
 14160      }
 14161  
 14162      if (options.name == null) {
 14163        error('A `name` must be specified to make a layout');
 14164        return;
 14165      }
 14166  
 14167      var name = options.name;
 14168      var Layout = cy.extension('layout', name);
 14169  
 14170      if (Layout == null) {
 14171        error('No such layout `' + name + '` found.  Did you forget to import it and `cytoscape.use()` it?');
 14172        return;
 14173      }
 14174  
 14175      var eles;
 14176  
 14177      if (string(options.eles)) {
 14178        eles = cy.$(options.eles);
 14179      } else {
 14180        eles = options.eles != null ? options.eles : cy.$();
 14181      }
 14182  
 14183      var layout = new Layout(extend({}, options, {
 14184        cy: cy,
 14185        eles: eles
 14186      }));
 14187      return layout;
 14188    }
 14189  };
 14190  corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
 14191  
 14192  var corefn$4 = {
 14193    notify: function notify(eventName, eventEles) {
 14194      var _p = this._private;
 14195  
 14196      if (this.batching()) {
 14197        _p.batchNotifications = _p.batchNotifications || {};
 14198        var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
 14199  
 14200        if (eventEles != null) {
 14201          eles.merge(eventEles);
 14202        }
 14203  
 14204        return; // notifications are disabled during batching
 14205      }
 14206  
 14207      if (!_p.notificationsEnabled) {
 14208        return;
 14209      } // exit on disabled
 14210  
 14211  
 14212      var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
 14213  
 14214      if (this.destroyed() || !renderer) {
 14215        return;
 14216      }
 14217  
 14218      renderer.notify(eventName, eventEles);
 14219    },
 14220    notifications: function notifications(bool) {
 14221      var p = this._private;
 14222  
 14223      if (bool === undefined) {
 14224        return p.notificationsEnabled;
 14225      } else {
 14226        p.notificationsEnabled = bool ? true : false;
 14227      }
 14228  
 14229      return this;
 14230    },
 14231    noNotifications: function noNotifications(callback) {
 14232      this.notifications(false);
 14233      callback();
 14234      this.notifications(true);
 14235    },
 14236    batching: function batching() {
 14237      return this._private.batchCount > 0;
 14238    },
 14239    startBatch: function startBatch() {
 14240      var _p = this._private;
 14241  
 14242      if (_p.batchCount == null) {
 14243        _p.batchCount = 0;
 14244      }
 14245  
 14246      if (_p.batchCount === 0) {
 14247        _p.batchStyleEles = this.collection();
 14248        _p.batchNotifications = {};
 14249      }
 14250  
 14251      _p.batchCount++;
 14252      return this;
 14253    },
 14254    endBatch: function endBatch() {
 14255      var _p = this._private;
 14256  
 14257      if (_p.batchCount === 0) {
 14258        return this;
 14259      }
 14260  
 14261      _p.batchCount--;
 14262  
 14263      if (_p.batchCount === 0) {
 14264        // update style for dirty eles
 14265        _p.batchStyleEles.updateStyle();
 14266  
 14267        var renderer = this.renderer(); // notify the renderer of queued eles and event types
 14268  
 14269        Object.keys(_p.batchNotifications).forEach(function (eventName) {
 14270          var eles = _p.batchNotifications[eventName];
 14271  
 14272          if (eles.empty()) {
 14273            renderer.notify(eventName);
 14274          } else {
 14275            renderer.notify(eventName, eles);
 14276          }
 14277        });
 14278      }
 14279  
 14280      return this;
 14281    },
 14282    batch: function batch(callback) {
 14283      this.startBatch();
 14284      callback();
 14285      this.endBatch();
 14286      return this;
 14287    },
 14288    // for backwards compatibility
 14289    batchData: function batchData(map) {
 14290      var cy = this;
 14291      return this.batch(function () {
 14292        var ids = Object.keys(map);
 14293  
 14294        for (var i = 0; i < ids.length; i++) {
 14295          var id = ids[i];
 14296          var data = map[id];
 14297          var ele = cy.getElementById(id);
 14298          ele.data(data);
 14299        }
 14300      });
 14301    }
 14302  };
 14303  
 14304  var rendererDefaults = defaults({
 14305    hideEdgesOnViewport: false,
 14306    textureOnViewport: false,
 14307    motionBlur: false,
 14308    motionBlurOpacity: 0.05,
 14309    pixelRatio: undefined,
 14310    desktopTapThreshold: 4,
 14311    touchTapThreshold: 8,
 14312    wheelSensitivity: 1,
 14313    debug: false,
 14314    showFps: false
 14315  });
 14316  var corefn$5 = {
 14317    renderTo: function renderTo(context, zoom, pan, pxRatio) {
 14318      var r = this._private.renderer;
 14319      r.renderTo(context, zoom, pan, pxRatio);
 14320      return this;
 14321    },
 14322    renderer: function renderer() {
 14323      return this._private.renderer;
 14324    },
 14325    forceRender: function forceRender() {
 14326      this.notify('draw');
 14327      return this;
 14328    },
 14329    resize: function resize() {
 14330      this.invalidateSize();
 14331      this.emitAndNotify('resize');
 14332      return this;
 14333    },
 14334    initRenderer: function initRenderer(options) {
 14335      var cy = this;
 14336      var RendererProto = cy.extension('renderer', options.name);
 14337  
 14338      if (RendererProto == null) {
 14339        error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
 14340        return;
 14341      }
 14342  
 14343      if (options.wheelSensitivity !== undefined) {
 14344        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.");
 14345      }
 14346  
 14347      var rOpts = rendererDefaults(options);
 14348      rOpts.cy = cy;
 14349      cy._private.renderer = new RendererProto(rOpts);
 14350      this.notify('init');
 14351    },
 14352    destroyRenderer: function destroyRenderer() {
 14353      var cy = this;
 14354      cy.notify('destroy'); // destroy the renderer
 14355  
 14356      var domEle = cy.container();
 14357  
 14358      if (domEle) {
 14359        domEle._cyreg = null;
 14360  
 14361        while (domEle.childNodes.length > 0) {
 14362          domEle.removeChild(domEle.childNodes[0]);
 14363        }
 14364      }
 14365  
 14366      cy._private.renderer = null; // to be extra safe, remove the ref
 14367  
 14368      cy.mutableElements().forEach(function (ele) {
 14369        var _p = ele._private;
 14370        _p.rscratch = {};
 14371        _p.rstyle = {};
 14372        _p.animation.current = [];
 14373        _p.animation.queue = [];
 14374      });
 14375    },
 14376    onRender: function onRender(fn) {
 14377      return this.on('render', fn);
 14378    },
 14379    offRender: function offRender(fn) {
 14380      return this.off('render', fn);
 14381    }
 14382  };
 14383  corefn$5.invalidateDimensions = corefn$5.resize;
 14384  
 14385  var corefn$6 = {
 14386    // get a collection
 14387    // - empty collection on no args
 14388    // - collection of elements in the graph on selector arg
 14389    // - guarantee a returned collection when elements or collection specified
 14390    collection: function collection(eles, opts) {
 14391      if (string(eles)) {
 14392        return this.$(eles);
 14393      } else if (elementOrCollection(eles)) {
 14394        return eles.collection();
 14395      } else if (array(eles)) {
 14396        return new Collection(this, eles, opts);
 14397      }
 14398  
 14399      return new Collection(this);
 14400    },
 14401    nodes: function nodes(selector) {
 14402      var nodes = this.$(function (ele) {
 14403        return ele.isNode();
 14404      });
 14405  
 14406      if (selector) {
 14407        return nodes.filter(selector);
 14408      }
 14409  
 14410      return nodes;
 14411    },
 14412    edges: function edges(selector) {
 14413      var edges = this.$(function (ele) {
 14414        return ele.isEdge();
 14415      });
 14416  
 14417      if (selector) {
 14418        return edges.filter(selector);
 14419      }
 14420  
 14421      return edges;
 14422    },
 14423    // search the graph like jQuery
 14424    $: function $(selector) {
 14425      var eles = this._private.elements;
 14426  
 14427      if (selector) {
 14428        return eles.filter(selector);
 14429      } else {
 14430        return eles.spawnSelf();
 14431      }
 14432    },
 14433    mutableElements: function mutableElements() {
 14434      return this._private.elements;
 14435    }
 14436  }; // aliases
 14437  
 14438  corefn$6.elements = corefn$6.filter = corefn$6.$;
 14439  
 14440  var styfn = {}; // keys for style blocks, e.g. ttfftt
 14441  
 14442  var TRUE = 't';
 14443  var FALSE = 'f'; // (potentially expensive calculation)
 14444  // apply the style to the element based on
 14445  // - its bypass
 14446  // - what selectors match it
 14447  
 14448  styfn.apply = function (eles) {
 14449    var self = this;
 14450    var _p = self._private;
 14451    var cy = _p.cy;
 14452    var updatedEles = cy.collection();
 14453  
 14454    if (_p.newStyle) {
 14455      // clear style caches
 14456      _p.contextStyles = {};
 14457      _p.propDiffs = {};
 14458      self.cleanElements(eles, true);
 14459    }
 14460  
 14461    for (var ie = 0; ie < eles.length; ie++) {
 14462      var ele = eles[ie];
 14463      var cxtMeta = self.getContextMeta(ele);
 14464  
 14465      if (cxtMeta.empty) {
 14466        continue;
 14467      }
 14468  
 14469      var cxtStyle = self.getContextStyle(cxtMeta);
 14470      var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
 14471  
 14472      if (!_p.newStyle) {
 14473        self.updateTransitions(ele, app.diffProps);
 14474      }
 14475  
 14476      var hintsDiff = self.updateStyleHints(ele);
 14477  
 14478      if (hintsDiff) {
 14479        updatedEles.merge(ele);
 14480      }
 14481    } // for elements
 14482  
 14483  
 14484    _p.newStyle = false;
 14485    return updatedEles;
 14486  };
 14487  
 14488  styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
 14489    var self = this;
 14490    var cache = self._private.propDiffs = self._private.propDiffs || {};
 14491    var dualCxtKey = oldCxtKey + '-' + newCxtKey;
 14492    var cachedVal = cache[dualCxtKey];
 14493  
 14494    if (cachedVal) {
 14495      return cachedVal;
 14496    }
 14497  
 14498    var diffProps = [];
 14499    var addedProp = {};
 14500  
 14501    for (var i = 0; i < self.length; i++) {
 14502      var cxt = self[i];
 14503      var oldHasCxt = oldCxtKey[i] === TRUE;
 14504      var newHasCxt = newCxtKey[i] === TRUE;
 14505      var cxtHasDiffed = oldHasCxt !== newHasCxt;
 14506      var cxtHasMappedProps = cxt.mappedProperties.length > 0;
 14507  
 14508      if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
 14509        var props = void 0;
 14510  
 14511        if (cxtHasDiffed && cxtHasMappedProps) {
 14512          props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
 14513        } else if (cxtHasDiffed) {
 14514          props = cxt.properties; // need to check them all
 14515        } else if (cxtHasMappedProps) {
 14516          props = cxt.mappedProperties; // only need to check mapped
 14517        }
 14518  
 14519        for (var j = 0; j < props.length; j++) {
 14520          var prop = props[j];
 14521          var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
 14522          // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
 14523          // is cached)
 14524  
 14525          var laterCxtOverrides = false;
 14526  
 14527          for (var k = i + 1; k < self.length; k++) {
 14528            var laterCxt = self[k];
 14529            var hasLaterCxt = newCxtKey[k] === TRUE;
 14530  
 14531            if (!hasLaterCxt) {
 14532              continue;
 14533            } // can't override unless the context is active
 14534  
 14535  
 14536            laterCxtOverrides = laterCxt.properties[prop.name] != null;
 14537  
 14538            if (laterCxtOverrides) {
 14539              break;
 14540            } // exit early as long as one later context overrides
 14541  
 14542          }
 14543  
 14544          if (!addedProp[name] && !laterCxtOverrides) {
 14545            addedProp[name] = true;
 14546            diffProps.push(name);
 14547          }
 14548        } // for props
 14549  
 14550      } // if
 14551  
 14552    } // for contexts
 14553  
 14554  
 14555    cache[dualCxtKey] = diffProps;
 14556    return diffProps;
 14557  };
 14558  
 14559  styfn.getContextMeta = function (ele) {
 14560    var self = this;
 14561    var cxtKey = '';
 14562    var diffProps;
 14563    var prevKey = ele._private.styleCxtKey || '';
 14564  
 14565    if (self._private.newStyle) {
 14566      prevKey = ''; // since we need to apply all style if a fresh stylesheet
 14567    } // get the cxt key
 14568  
 14569  
 14570    for (var i = 0; i < self.length; i++) {
 14571      var context = self[i];
 14572      var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
 14573  
 14574      if (contextSelectorMatches) {
 14575        cxtKey += TRUE;
 14576      } else {
 14577        cxtKey += FALSE;
 14578      }
 14579    } // for context
 14580  
 14581  
 14582    diffProps = self.getPropertiesDiff(prevKey, cxtKey);
 14583    ele._private.styleCxtKey = cxtKey;
 14584    return {
 14585      key: cxtKey,
 14586      diffPropNames: diffProps,
 14587      empty: diffProps.length === 0
 14588    };
 14589  }; // gets a computed ele style object based on matched contexts
 14590  
 14591  
 14592  styfn.getContextStyle = function (cxtMeta) {
 14593    var cxtKey = cxtMeta.key;
 14594    var self = this;
 14595    var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
 14596  
 14597    if (cxtStyles[cxtKey]) {
 14598      return cxtStyles[cxtKey];
 14599    }
 14600  
 14601    var style = {
 14602      _private: {
 14603        key: cxtKey
 14604      }
 14605    };
 14606  
 14607    for (var i = 0; i < self.length; i++) {
 14608      var cxt = self[i];
 14609      var hasCxt = cxtKey[i] === TRUE;
 14610  
 14611      if (!hasCxt) {
 14612        continue;
 14613      }
 14614  
 14615      for (var j = 0; j < cxt.properties.length; j++) {
 14616        var prop = cxt.properties[j];
 14617        style[prop.name] = prop;
 14618      }
 14619    }
 14620  
 14621    cxtStyles[cxtKey] = style;
 14622    return style;
 14623  };
 14624  
 14625  styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
 14626    var self = this;
 14627    var diffProps = cxtMeta.diffPropNames;
 14628    var retDiffProps = {};
 14629    var types = self.types;
 14630  
 14631    for (var i = 0; i < diffProps.length; i++) {
 14632      var diffPropName = diffProps[i];
 14633      var cxtProp = cxtStyle[diffPropName];
 14634      var eleProp = ele.pstyle(diffPropName);
 14635  
 14636      if (!cxtProp) {
 14637        // no context prop means delete
 14638        if (!eleProp) {
 14639          continue; // no existing prop means nothing needs to be removed
 14640          // nb affects initial application on mapped values like control-point-distances
 14641        } else if (eleProp.bypass) {
 14642          cxtProp = {
 14643            name: diffPropName,
 14644            deleteBypassed: true
 14645          };
 14646        } else {
 14647          cxtProp = {
 14648            name: diffPropName,
 14649            "delete": true
 14650          };
 14651        }
 14652      } // save cycles when the context prop doesn't need to be applied
 14653  
 14654  
 14655      if (eleProp === cxtProp) {
 14656        continue;
 14657      } // save cycles when a mapped context prop doesn't need to be applied
 14658  
 14659  
 14660      if (cxtProp.mapped === types.fn // context prop is function mapper
 14661      && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
 14662      && eleProp.mapping != null // ele prop is a concrete value from from a mapper
 14663      && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
 14664      ) {
 14665          // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
 14666          var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
 14667  
 14668          var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
 14669  
 14670          if (fnValue === mapping.prevFnValue) {
 14671            continue;
 14672          }
 14673        }
 14674  
 14675      var retDiffProp = retDiffProps[diffPropName] = {
 14676        prev: eleProp
 14677      };
 14678      self.applyParsedProperty(ele, cxtProp);
 14679      retDiffProp.next = ele.pstyle(diffPropName);
 14680  
 14681      if (retDiffProp.next && retDiffProp.next.bypass) {
 14682        retDiffProp.next = retDiffProp.next.bypassed;
 14683      }
 14684    }
 14685  
 14686    return {
 14687      diffProps: retDiffProps
 14688    };
 14689  };
 14690  
 14691  styfn.updateStyleHints = function (ele) {
 14692    var _p = ele._private;
 14693    var self = this;
 14694    var propNames = self.propertyGroupNames;
 14695    var propGrKeys = self.propertyGroupKeys;
 14696  
 14697    var propHash = function propHash(ele, propNames, seedKey) {
 14698      return self.getPropertiesHash(ele, propNames, seedKey);
 14699    };
 14700  
 14701    var oldStyleKey = _p.styleKey;
 14702  
 14703    if (ele.removed()) {
 14704      return false;
 14705    }
 14706  
 14707    var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
 14708    // but lazily -- only use non-default prop values to reduce the number of hashes
 14709    //
 14710  
 14711    var overriddenStyles = ele._private.style;
 14712    propNames = Object.keys(overriddenStyles);
 14713  
 14714    for (var i = 0; i < propGrKeys.length; i++) {
 14715      var grKey = propGrKeys[i];
 14716      _p.styleKeys[grKey] = 0;
 14717    }
 14718  
 14719    var updateGrKey = function updateGrKey(val, grKey) {
 14720      return _p.styleKeys[grKey] = hashInt(val, _p.styleKeys[grKey]);
 14721    };
 14722  
 14723    var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
 14724      for (var j = 0; j < strVal.length; j++) {
 14725        updateGrKey(strVal.charCodeAt(j), grKey);
 14726      }
 14727    }; // - hashing works on 32 bit ints b/c we use bitwise ops
 14728    // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
 14729    // - raise up small numbers so more significant digits are seen by hashing
 14730    // - make small numbers larger than a normal value to avoid collisions
 14731    // - works in practice and it's relatively cheap
 14732  
 14733  
 14734    var N = 2000000000;
 14735  
 14736    var cleanNum = function cleanNum(val) {
 14737      return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
 14738    };
 14739  
 14740    for (var _i = 0; _i < propNames.length; _i++) {
 14741      var name = propNames[_i];
 14742      var parsedProp = overriddenStyles[name];
 14743  
 14744      if (parsedProp == null) {
 14745        continue;
 14746      }
 14747  
 14748      var propInfo = this.properties[name];
 14749      var type = propInfo.type;
 14750      var _grKey = propInfo.groupKey;
 14751      var normalizedNumberVal = void 0;
 14752  
 14753      if (propInfo.hashOverride != null) {
 14754        normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
 14755      } else if (parsedProp.pfValue != null) {
 14756        normalizedNumberVal = parsedProp.pfValue;
 14757      } // might not be a number if it allows enums
 14758  
 14759  
 14760      var numberVal = propInfo.enums == null ? parsedProp.value : null;
 14761      var haveNormNum = normalizedNumberVal != null;
 14762      var haveUnitedNum = numberVal != null;
 14763      var haveNum = haveNormNum || haveUnitedNum;
 14764      var units = parsedProp.units; // numbers are cheaper to hash than strings
 14765      // 1 hash op vs n hash ops (for length n string)
 14766  
 14767      if (type.number && haveNum) {
 14768        var v = haveNormNum ? normalizedNumberVal : numberVal;
 14769  
 14770        if (type.multiple) {
 14771          for (var _i2 = 0; _i2 < v.length; _i2++) {
 14772            updateGrKey(cleanNum(v[_i2]), _grKey);
 14773          }
 14774        } else {
 14775          updateGrKey(cleanNum(v), _grKey);
 14776        }
 14777  
 14778        if (!haveNormNum && units != null) {
 14779          updateGrKeyWStr(units, _grKey);
 14780        }
 14781      } else {
 14782        updateGrKeyWStr(parsedProp.strValue, _grKey);
 14783      }
 14784    } // overall style key
 14785    //
 14786  
 14787  
 14788    var hash = 0;
 14789  
 14790    for (var _i3 = 0; _i3 < propGrKeys.length; _i3++) {
 14791      var _grKey2 = propGrKeys[_i3];
 14792      var grHash = _p.styleKeys[_grKey2];
 14793      hash = hashInt(grHash, hash);
 14794    }
 14795  
 14796    _p.styleKey = hash; // label dims
 14797    //
 14798  
 14799    var labelDimsKey = _p.labelDimsKey = _p.styleKeys.labelDimensions;
 14800    _p.labelKey = propHash(ele, ['label'], labelDimsKey);
 14801    _p.labelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.labelKey);
 14802  
 14803    if (!isNode) {
 14804      _p.sourceLabelKey = propHash(ele, ['source-label'], labelDimsKey);
 14805      _p.sourceLabelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.sourceLabelKey);
 14806      _p.targetLabelKey = propHash(ele, ['target-label'], labelDimsKey);
 14807      _p.targetLabelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.targetLabelKey);
 14808    } // node
 14809    //
 14810  
 14811  
 14812    if (isNode) {
 14813      var _p$styleKeys = _p.styleKeys,
 14814          nodeBody = _p$styleKeys.nodeBody,
 14815          nodeBorder = _p$styleKeys.nodeBorder,
 14816          backgroundImage = _p$styleKeys.backgroundImage,
 14817          compound = _p$styleKeys.compound,
 14818          pie = _p$styleKeys.pie;
 14819      _p.nodeKey = hashIntsArray([nodeBorder, backgroundImage, compound, pie], nodeBody);
 14820      _p.hasPie = pie != 0;
 14821    }
 14822  
 14823    return oldStyleKey !== _p.styleKey;
 14824  };
 14825  
 14826  styfn.clearStyleHints = function (ele) {
 14827    var _p = ele._private;
 14828    _p.styleKeys = {};
 14829    _p.styleKey = null;
 14830    _p.labelKey = null;
 14831    _p.labelStyleKey = null;
 14832    _p.sourceLabelKey = null;
 14833    _p.sourceLabelStyleKey = null;
 14834    _p.targetLabelKey = null;
 14835    _p.targetLabelStyleKey = null;
 14836    _p.nodeKey = null;
 14837    _p.hasPie = null;
 14838  }; // apply a property to the style (for internal use)
 14839  // returns whether application was successful
 14840  //
 14841  // now, this function flattens the property, and here's how:
 14842  //
 14843  // for parsedProp:{ bypass: true, deleteBypass: true }
 14844  // no property is generated, instead the bypass property in the
 14845  // element's style is replaced by what's pointed to by the `bypassed`
 14846  // field in the bypass property (i.e. restoring the property the
 14847  // bypass was overriding)
 14848  //
 14849  // for parsedProp:{ mapped: truthy }
 14850  // the generated flattenedProp:{ mapping: prop }
 14851  //
 14852  // for parsedProp:{ bypass: true }
 14853  // the generated flattenedProp:{ bypassed: parsedProp }
 14854  
 14855  
 14856  styfn.applyParsedProperty = function (ele, parsedProp) {
 14857    var self = this;
 14858    var prop = parsedProp;
 14859    var style = ele._private.style;
 14860    var flatProp;
 14861    var types = self.types;
 14862    var type = self.properties[prop.name].type;
 14863    var propIsBypass = prop.bypass;
 14864    var origProp = style[prop.name];
 14865    var origPropIsBypass = origProp && origProp.bypass;
 14866    var _p = ele._private;
 14867    var flatPropMapping = 'mapping';
 14868  
 14869    var getVal = function getVal(p) {
 14870      if (p == null) {
 14871        return null;
 14872      } else if (p.pfValue != null) {
 14873        return p.pfValue;
 14874      } else {
 14875        return p.value;
 14876      }
 14877    };
 14878  
 14879    var checkTriggers = function checkTriggers() {
 14880      var fromVal = getVal(origProp);
 14881      var toVal = getVal(prop);
 14882      self.checkTriggers(ele, prop.name, fromVal, toVal);
 14883    }; // edge sanity checks to prevent the client from making serious mistakes
 14884  
 14885  
 14886    if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
 14887    parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
 14888    parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
 14889      prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
 14890    }
 14891  
 14892    if (prop["delete"]) {
 14893      // delete the property and use the default value on falsey value
 14894      style[prop.name] = undefined;
 14895      checkTriggers();
 14896      return true;
 14897    }
 14898  
 14899    if (prop.deleteBypassed) {
 14900      // delete the property that the
 14901      if (!origProp) {
 14902        checkTriggers();
 14903        return true; // can't delete if no prop
 14904      } else if (origProp.bypass) {
 14905        // delete bypassed
 14906        origProp.bypassed = undefined;
 14907        checkTriggers();
 14908        return true;
 14909      } else {
 14910        return false; // we're unsuccessful deleting the bypassed
 14911      }
 14912    } // check if we need to delete the current bypass
 14913  
 14914  
 14915    if (prop.deleteBypass) {
 14916      // then this property is just here to indicate we need to delete
 14917      if (!origProp) {
 14918        checkTriggers();
 14919        return true; // property is already not defined
 14920      } else if (origProp.bypass) {
 14921        // then replace the bypass property with the original
 14922        // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
 14923        style[prop.name] = origProp.bypassed;
 14924        checkTriggers();
 14925        return true;
 14926      } else {
 14927        return false; // we're unsuccessful deleting the bypass
 14928      }
 14929    }
 14930  
 14931    var printMappingErr = function printMappingErr() {
 14932      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');
 14933    }; // put the property in the style objects
 14934  
 14935  
 14936    switch (prop.mapped) {
 14937      // flatten the property if mapped
 14938      case types.mapData:
 14939        {
 14940          // flatten the field (e.g. data.foo.bar)
 14941          var fields = prop.field.split('.');
 14942          var fieldVal = _p.data;
 14943  
 14944          for (var i = 0; i < fields.length && fieldVal; i++) {
 14945            var field = fields[i];
 14946            fieldVal = fieldVal[field];
 14947          }
 14948  
 14949          if (fieldVal == null) {
 14950            printMappingErr();
 14951            return false;
 14952          }
 14953  
 14954          var percent;
 14955  
 14956          if (!number(fieldVal)) {
 14957            // then don't apply and fall back on the existing style
 14958            warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
 14959            return false;
 14960          } else {
 14961            var fieldWidth = prop.fieldMax - prop.fieldMin;
 14962  
 14963            if (fieldWidth === 0) {
 14964              // safety check -- not strictly necessary as no props of zero range should be passed here
 14965              percent = 0;
 14966            } else {
 14967              percent = (fieldVal - prop.fieldMin) / fieldWidth;
 14968            }
 14969          } // make sure to bound percent value
 14970  
 14971  
 14972          if (percent < 0) {
 14973            percent = 0;
 14974          } else if (percent > 1) {
 14975            percent = 1;
 14976          }
 14977  
 14978          if (type.color) {
 14979            var r1 = prop.valueMin[0];
 14980            var r2 = prop.valueMax[0];
 14981            var g1 = prop.valueMin[1];
 14982            var g2 = prop.valueMax[1];
 14983            var b1 = prop.valueMin[2];
 14984            var b2 = prop.valueMax[2];
 14985            var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
 14986            var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
 14987            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)];
 14988            flatProp = {
 14989              // colours are simple, so just create the flat property instead of expensive string parsing
 14990              bypass: prop.bypass,
 14991              // we're a bypass if the mapping property is a bypass
 14992              name: prop.name,
 14993              value: clr,
 14994              strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
 14995            };
 14996          } else if (type.number) {
 14997            var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
 14998            flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
 14999          } else {
 15000            return false; // can only map to colours and numbers
 15001          }
 15002  
 15003          if (!flatProp) {
 15004            // if we can't flatten the property, then don't apply the property and fall back on the existing style
 15005            printMappingErr();
 15006            return false;
 15007          }
 15008  
 15009          flatProp.mapping = prop; // keep a reference to the mapping
 15010  
 15011          prop = flatProp; // the flattened (mapped) property is the one we want
 15012  
 15013          break;
 15014        }
 15015      // direct mapping
 15016  
 15017      case types.data:
 15018        {
 15019          // flatten the field (e.g. data.foo.bar)
 15020          var _fields = prop.field.split('.');
 15021  
 15022          var _fieldVal = _p.data;
 15023  
 15024          for (var _i4 = 0; _i4 < _fields.length && _fieldVal; _i4++) {
 15025            var _field = _fields[_i4];
 15026            _fieldVal = _fieldVal[_field];
 15027          }
 15028  
 15029          if (_fieldVal != null) {
 15030            flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
 15031          }
 15032  
 15033          if (!flatProp) {
 15034            // if we can't flatten the property, then don't apply and fall back on the existing style
 15035            printMappingErr();
 15036            return false;
 15037          }
 15038  
 15039          flatProp.mapping = prop; // keep a reference to the mapping
 15040  
 15041          prop = flatProp; // the flattened (mapped) property is the one we want
 15042  
 15043          break;
 15044        }
 15045  
 15046      case types.fn:
 15047        {
 15048          var fn = prop.value;
 15049          var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
 15050  
 15051          prop.prevFnValue = fnRetVal;
 15052  
 15053          if (fnRetVal == null) {
 15054            warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
 15055            return false;
 15056          }
 15057  
 15058          flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
 15059  
 15060          if (!flatProp) {
 15061            warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
 15062            return false;
 15063          }
 15064  
 15065          flatProp.mapping = copy(prop); // keep a reference to the mapping
 15066  
 15067          prop = flatProp; // the flattened (mapped) property is the one we want
 15068  
 15069          break;
 15070        }
 15071  
 15072      case undefined:
 15073        break;
 15074      // just set the property
 15075  
 15076      default:
 15077        return false;
 15078      // not a valid mapping
 15079    } // if the property is a bypass property, then link the resultant property to the original one
 15080  
 15081  
 15082    if (propIsBypass) {
 15083      if (origPropIsBypass) {
 15084        // then this bypass overrides the existing one
 15085        prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
 15086      } else {
 15087        // then link the orig prop to the new bypass
 15088        prop.bypassed = origProp;
 15089      }
 15090  
 15091      style[prop.name] = prop; // and set
 15092    } else {
 15093      // prop is not bypass
 15094      if (origPropIsBypass) {
 15095        // then keep the orig prop (since it's a bypass) and link to the new prop
 15096        origProp.bypassed = prop;
 15097      } else {
 15098        // then just replace the old prop with the new one
 15099        style[prop.name] = prop;
 15100      }
 15101    }
 15102  
 15103    checkTriggers();
 15104    return true;
 15105  };
 15106  
 15107  styfn.cleanElements = function (eles, keepBypasses) {
 15108    for (var i = 0; i < eles.length; i++) {
 15109      var ele = eles[i];
 15110      this.clearStyleHints(ele);
 15111      ele.dirtyCompoundBoundsCache();
 15112      ele.dirtyBoundingBoxCache();
 15113  
 15114      if (!keepBypasses) {
 15115        ele._private.style = {};
 15116      } else {
 15117        var style = ele._private.style;
 15118        var propNames = Object.keys(style);
 15119  
 15120        for (var j = 0; j < propNames.length; j++) {
 15121          var propName = propNames[j];
 15122          var eleProp = style[propName];
 15123  
 15124          if (eleProp != null) {
 15125            if (eleProp.bypass) {
 15126              eleProp.bypassed = null;
 15127            } else {
 15128              style[propName] = null;
 15129            }
 15130          }
 15131        }
 15132      }
 15133    }
 15134  }; // updates the visual style for all elements (useful for manual style modification after init)
 15135  
 15136  
 15137  styfn.update = function () {
 15138    var cy = this._private.cy;
 15139    var eles = cy.mutableElements();
 15140    eles.updateStyle();
 15141  }; // diffProps : { name => { prev, next } }
 15142  
 15143  
 15144  styfn.updateTransitions = function (ele, diffProps) {
 15145    var self = this;
 15146    var _p = ele._private;
 15147    var props = ele.pstyle('transition-property').value;
 15148    var duration = ele.pstyle('transition-duration').pfValue;
 15149    var delay = ele.pstyle('transition-delay').pfValue;
 15150  
 15151    if (props.length > 0 && duration > 0) {
 15152      var style = {}; // build up the style to animate towards
 15153  
 15154      var anyPrev = false;
 15155  
 15156      for (var i = 0; i < props.length; i++) {
 15157        var prop = props[i];
 15158        var styProp = ele.pstyle(prop);
 15159        var diffProp = diffProps[prop];
 15160  
 15161        if (!diffProp) {
 15162          continue;
 15163        }
 15164  
 15165        var prevProp = diffProp.prev;
 15166        var fromProp = prevProp;
 15167        var toProp = diffProp.next != null ? diffProp.next : styProp;
 15168        var diff = false;
 15169        var initVal = void 0;
 15170        var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
 15171  
 15172        if (!fromProp) {
 15173          continue;
 15174        } // consider px values
 15175  
 15176  
 15177        if (number(fromProp.pfValue) && number(toProp.pfValue)) {
 15178          diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
 15179  
 15180          initVal = fromProp.pfValue + initDt * diff; // consider numerical values
 15181        } else if (number(fromProp.value) && number(toProp.value)) {
 15182          diff = toProp.value - fromProp.value; // nonzero is truthy
 15183  
 15184          initVal = fromProp.value + initDt * diff; // consider colour values
 15185        } else if (array(fromProp.value) && array(toProp.value)) {
 15186          diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
 15187          initVal = fromProp.strValue;
 15188        } // the previous value is good for an animation only if it's different
 15189  
 15190  
 15191        if (diff) {
 15192          style[prop] = toProp.strValue; // to val
 15193  
 15194          this.applyBypass(ele, prop, initVal); // from val
 15195  
 15196          anyPrev = true;
 15197        }
 15198      } // end if props allow ani
 15199      // can't transition if there's nothing previous to transition from
 15200  
 15201  
 15202      if (!anyPrev) {
 15203        return;
 15204      }
 15205  
 15206      _p.transitioning = true;
 15207      new Promise$1(function (resolve) {
 15208        if (delay > 0) {
 15209          ele.delayAnimation(delay).play().promise().then(resolve);
 15210        } else {
 15211          resolve();
 15212        }
 15213      }).then(function () {
 15214        return ele.animation({
 15215          style: style,
 15216          duration: duration,
 15217          easing: ele.pstyle('transition-timing-function').value,
 15218          queue: false
 15219        }).play().promise();
 15220      }).then(function () {
 15221        // if( !isBypass ){
 15222        self.removeBypasses(ele, props);
 15223        ele.emitAndNotify('style'); // }
 15224  
 15225        _p.transitioning = false;
 15226      });
 15227    } else if (_p.transitioning) {
 15228      this.removeBypasses(ele, props);
 15229      ele.emitAndNotify('style');
 15230      _p.transitioning = false;
 15231    }
 15232  };
 15233  
 15234  styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
 15235    var prop = this.properties[name];
 15236    var triggerCheck = getTrigger(prop);
 15237  
 15238    if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
 15239      onTrigger(prop);
 15240    }
 15241  };
 15242  
 15243  styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
 15244    var _this = this;
 15245  
 15246    this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
 15247      return prop.triggersZOrder;
 15248    }, function () {
 15249      _this._private.cy.notify('zorder', ele);
 15250    });
 15251  };
 15252  
 15253  styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
 15254    this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
 15255      return prop.triggersBounds;
 15256    }, function (prop) {
 15257      ele.dirtyCompoundBoundsCache();
 15258      ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
 15259      // then dirty the pll edge bb cache as well
 15260  
 15261      if ( // only for beziers -- so performance of other edges isn't affected
 15262      (ele.pstyle('curve-style').value === 'bezier' // already a bezier
 15263      // was just now changed to or from a bezier:
 15264      || name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier')) && prop.triggersBoundsOfParallelBeziers) {
 15265        ele.parallelEdges().forEach(function (pllEdge) {
 15266          if (pllEdge.isBundledBezier()) {
 15267            pllEdge.dirtyBoundingBoxCache();
 15268          }
 15269        });
 15270      }
 15271    });
 15272  };
 15273  
 15274  styfn.checkTriggers = function (ele, name, fromValue, toValue) {
 15275    ele.dirtyStyleCache();
 15276    this.checkZOrderTrigger(ele, name, fromValue, toValue);
 15277    this.checkBoundsTrigger(ele, name, fromValue, toValue);
 15278  };
 15279  
 15280  var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
 15281  // returns true iff application was successful for at least 1 specified property
 15282  
 15283  styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
 15284    var self = this;
 15285    var props = [];
 15286    var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
 15287  
 15288    if (name === '*' || name === '**') {
 15289      // apply to all property names
 15290      if (value !== undefined) {
 15291        for (var i = 0; i < self.properties.length; i++) {
 15292          var prop = self.properties[i];
 15293          var _name = prop.name;
 15294          var parsedProp = this.parse(_name, value, true);
 15295  
 15296          if (parsedProp) {
 15297            props.push(parsedProp);
 15298          }
 15299        }
 15300      }
 15301    } else if (string(name)) {
 15302      // then parse the single property
 15303      var _parsedProp = this.parse(name, value, true);
 15304  
 15305      if (_parsedProp) {
 15306        props.push(_parsedProp);
 15307      }
 15308    } else if (plainObject(name)) {
 15309      // then parse each property
 15310      var specifiedProps = name;
 15311      updateTransitions = value;
 15312      var names = Object.keys(specifiedProps);
 15313  
 15314      for (var _i = 0; _i < names.length; _i++) {
 15315        var _name2 = names[_i];
 15316        var _value = specifiedProps[_name2];
 15317  
 15318        if (_value === undefined) {
 15319          // try camel case name too
 15320          _value = specifiedProps[dash2camel(_name2)];
 15321        }
 15322  
 15323        if (_value !== undefined) {
 15324          var _parsedProp2 = this.parse(_name2, _value, true);
 15325  
 15326          if (_parsedProp2) {
 15327            props.push(_parsedProp2);
 15328          }
 15329        }
 15330      }
 15331    } else {
 15332      // can't do anything without well defined properties
 15333      return false;
 15334    } // we've failed if there are no valid properties
 15335  
 15336  
 15337    if (props.length === 0) {
 15338      return false;
 15339    } // now, apply the bypass properties on the elements
 15340  
 15341  
 15342    var ret = false; // return true if at least one succesful bypass applied
 15343  
 15344    for (var _i2 = 0; _i2 < eles.length; _i2++) {
 15345      // for each ele
 15346      var ele = eles[_i2];
 15347      var diffProps = {};
 15348      var diffProp = void 0;
 15349  
 15350      for (var j = 0; j < props.length; j++) {
 15351        // for each prop
 15352        var _prop = props[j];
 15353  
 15354        if (updateTransitions) {
 15355          var prevProp = ele.pstyle(_prop.name);
 15356          diffProp = diffProps[_prop.name] = {
 15357            prev: prevProp
 15358          };
 15359        }
 15360  
 15361        ret = this.applyParsedProperty(ele, _prop) || ret;
 15362  
 15363        if (updateTransitions) {
 15364          diffProp.next = ele.pstyle(_prop.name);
 15365        }
 15366      } // for props
 15367  
 15368  
 15369      if (ret) {
 15370        this.updateStyleHints(ele);
 15371      }
 15372  
 15373      if (updateTransitions) {
 15374        this.updateTransitions(ele, diffProps, isBypass);
 15375      }
 15376    } // for eles
 15377  
 15378  
 15379    return ret;
 15380  }; // only useful in specific cases like animation
 15381  
 15382  
 15383  styfn$1.overrideBypass = function (eles, name, value) {
 15384    name = camel2dash(name);
 15385  
 15386    for (var i = 0; i < eles.length; i++) {
 15387      var ele = eles[i];
 15388      var prop = ele._private.style[name];
 15389      var type = this.properties[name].type;
 15390      var isColor = type.color;
 15391      var isMulti = type.mutiple;
 15392      var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
 15393  
 15394      if (!prop || !prop.bypass) {
 15395        // need a bypass if one doesn't exist
 15396        this.applyBypass(ele, name, value);
 15397      } else {
 15398        prop.value = value;
 15399  
 15400        if (prop.pfValue != null) {
 15401          prop.pfValue = value;
 15402        }
 15403  
 15404        if (isColor) {
 15405          prop.strValue = 'rgb(' + value.join(',') + ')';
 15406        } else if (isMulti) {
 15407          prop.strValue = value.join(' ');
 15408        } else {
 15409          prop.strValue = '' + value;
 15410        }
 15411  
 15412        this.updateStyleHints(ele);
 15413      }
 15414  
 15415      this.checkTriggers(ele, name, oldValue, value);
 15416    }
 15417  };
 15418  
 15419  styfn$1.removeAllBypasses = function (eles, updateTransitions) {
 15420    return this.removeBypasses(eles, this.propertyNames, updateTransitions);
 15421  };
 15422  
 15423  styfn$1.removeBypasses = function (eles, props, updateTransitions) {
 15424    var isBypass = true;
 15425  
 15426    for (var j = 0; j < eles.length; j++) {
 15427      var ele = eles[j];
 15428      var diffProps = {};
 15429  
 15430      for (var i = 0; i < props.length; i++) {
 15431        var name = props[i];
 15432        var prop = this.properties[name];
 15433        var prevProp = ele.pstyle(prop.name);
 15434  
 15435        if (!prevProp || !prevProp.bypass) {
 15436          // if a bypass doesn't exist for the prop, nothing needs to be removed
 15437          continue;
 15438        }
 15439  
 15440        var value = ''; // empty => remove bypass
 15441  
 15442        var parsedProp = this.parse(name, value, true);
 15443        var diffProp = diffProps[prop.name] = {
 15444          prev: prevProp
 15445        };
 15446        this.applyParsedProperty(ele, parsedProp);
 15447        diffProp.next = ele.pstyle(prop.name);
 15448      } // for props
 15449  
 15450  
 15451      this.updateStyleHints(ele);
 15452  
 15453      if (updateTransitions) {
 15454        this.updateTransitions(ele, diffProps, isBypass);
 15455      }
 15456    } // for eles
 15457  
 15458  };
 15459  
 15460  var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
 15461  
 15462  styfn$2.getEmSizeInPixels = function () {
 15463    var px = this.containerCss('font-size');
 15464  
 15465    if (px != null) {
 15466      return parseFloat(px);
 15467    } else {
 15468      return 1; // for headless
 15469    }
 15470  }; // gets css property from the core container
 15471  
 15472  
 15473  styfn$2.containerCss = function (propName) {
 15474    var cy = this._private.cy;
 15475    var domElement = cy.container();
 15476  
 15477    if (window$1 && domElement && window$1.getComputedStyle) {
 15478      return window$1.getComputedStyle(domElement).getPropertyValue(propName);
 15479    }
 15480  };
 15481  
 15482  var styfn$3 = {}; // gets the rendered style for an element
 15483  
 15484  styfn$3.getRenderedStyle = function (ele, prop) {
 15485    if (prop) {
 15486      return this.getStylePropertyValue(ele, prop, true);
 15487    } else {
 15488      return this.getRawStyle(ele, true);
 15489    }
 15490  }; // gets the raw style for an element
 15491  
 15492  
 15493  styfn$3.getRawStyle = function (ele, isRenderedVal) {
 15494    var self = this;
 15495    ele = ele[0]; // insure it's an element
 15496  
 15497    if (ele) {
 15498      var rstyle = {};
 15499  
 15500      for (var i = 0; i < self.properties.length; i++) {
 15501        var prop = self.properties[i];
 15502        var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
 15503  
 15504        if (val != null) {
 15505          rstyle[prop.name] = val;
 15506          rstyle[dash2camel(prop.name)] = val;
 15507        }
 15508      }
 15509  
 15510      return rstyle;
 15511    }
 15512  };
 15513  
 15514  styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
 15515    var pstyle = ele.pstyle(property)[subproperty][index];
 15516    return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
 15517  };
 15518  
 15519  styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
 15520    var self = this;
 15521    ele = ele[0]; // insure it's an element
 15522  
 15523    if (ele) {
 15524      var prop = self.properties[propName];
 15525  
 15526      if (prop.alias) {
 15527        prop = prop.pointsTo;
 15528      }
 15529  
 15530      var type = prop.type;
 15531      var styleProp = ele.pstyle(prop.name);
 15532  
 15533      if (styleProp) {
 15534        var value = styleProp.value,
 15535            units = styleProp.units,
 15536            strValue = styleProp.strValue;
 15537  
 15538        if (isRenderedVal && type.number && value != null && number(value)) {
 15539          var zoom = ele.cy().zoom();
 15540  
 15541          var getRenderedValue = function getRenderedValue(val) {
 15542            return val * zoom;
 15543          };
 15544  
 15545          var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
 15546            return getRenderedValue(val) + units;
 15547          };
 15548  
 15549          var isArrayValue = array(value);
 15550          var haveUnits = isArrayValue ? units.every(function (u) {
 15551            return u != null;
 15552          }) : units != null;
 15553  
 15554          if (haveUnits) {
 15555            if (isArrayValue) {
 15556              return value.map(function (v, i) {
 15557                return getValueStringWithUnits(v, units[i]);
 15558              }).join(' ');
 15559            } else {
 15560              return getValueStringWithUnits(value, units);
 15561            }
 15562          } else {
 15563            if (isArrayValue) {
 15564              return value.map(function (v) {
 15565                return string(v) ? v : '' + getRenderedValue(v);
 15566              }).join(' ');
 15567            } else {
 15568              return '' + getRenderedValue(value);
 15569            }
 15570          }
 15571        } else if (strValue != null) {
 15572          return strValue;
 15573        }
 15574      }
 15575  
 15576      return null;
 15577    }
 15578  };
 15579  
 15580  styfn$3.getAnimationStartStyle = function (ele, aniProps) {
 15581    var rstyle = {};
 15582  
 15583    for (var i = 0; i < aniProps.length; i++) {
 15584      var aniProp = aniProps[i];
 15585      var name = aniProp.name;
 15586      var styleProp = ele.pstyle(name);
 15587  
 15588      if (styleProp !== undefined) {
 15589        // then make a prop of it
 15590        if (plainObject(styleProp)) {
 15591          styleProp = this.parse(name, styleProp.strValue);
 15592        } else {
 15593          styleProp = this.parse(name, styleProp);
 15594        }
 15595      }
 15596  
 15597      if (styleProp) {
 15598        rstyle[name] = styleProp;
 15599      }
 15600    }
 15601  
 15602    return rstyle;
 15603  };
 15604  
 15605  styfn$3.getPropsList = function (propsObj) {
 15606    var self = this;
 15607    var rstyle = [];
 15608    var style = propsObj;
 15609    var props = self.properties;
 15610  
 15611    if (style) {
 15612      var names = Object.keys(style);
 15613  
 15614      for (var i = 0; i < names.length; i++) {
 15615        var name = names[i];
 15616        var val = style[name];
 15617        var prop = props[name] || props[camel2dash(name)];
 15618        var styleProp = this.parse(prop.name, val);
 15619  
 15620        if (styleProp) {
 15621          rstyle.push(styleProp);
 15622        }
 15623      }
 15624    }
 15625  
 15626    return rstyle;
 15627  };
 15628  
 15629  styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
 15630    var hash = seed;
 15631    var name, val, strVal, chVal;
 15632    var i, j;
 15633  
 15634    for (i = 0; i < propNames.length; i++) {
 15635      name = propNames[i];
 15636      val = ele.pstyle(name, false);
 15637  
 15638      if (val == null) {
 15639        continue;
 15640      } else if (val.pfValue != null) {
 15641        hash = hashInt(chVal, hash);
 15642      } else {
 15643        strVal = val.strValue;
 15644  
 15645        for (j = 0; j < strVal.length; j++) {
 15646          chVal = strVal.charCodeAt(j);
 15647          hash = hashInt(chVal, hash);
 15648        }
 15649      }
 15650    }
 15651  
 15652    return hash;
 15653  };
 15654  
 15655  styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
 15656  
 15657  var styfn$4 = {};
 15658  
 15659  styfn$4.appendFromJson = function (json) {
 15660    var style = this;
 15661  
 15662    for (var i = 0; i < json.length; i++) {
 15663      var context = json[i];
 15664      var selector = context.selector;
 15665      var props = context.style || context.css;
 15666      var names = Object.keys(props);
 15667      style.selector(selector); // apply selector
 15668  
 15669      for (var j = 0; j < names.length; j++) {
 15670        var name = names[j];
 15671        var value = props[name];
 15672        style.css(name, value); // apply property
 15673      }
 15674    }
 15675  
 15676    return style;
 15677  }; // accessible cy.style() function
 15678  
 15679  
 15680  styfn$4.fromJson = function (json) {
 15681    var style = this;
 15682    style.resetToDefault();
 15683    style.appendFromJson(json);
 15684    return style;
 15685  }; // get json from cy.style() api
 15686  
 15687  
 15688  styfn$4.json = function () {
 15689    var json = [];
 15690  
 15691    for (var i = this.defaultLength; i < this.length; i++) {
 15692      var cxt = this[i];
 15693      var selector = cxt.selector;
 15694      var props = cxt.properties;
 15695      var css = {};
 15696  
 15697      for (var j = 0; j < props.length; j++) {
 15698        var prop = props[j];
 15699        css[prop.name] = prop.strValue;
 15700      }
 15701  
 15702      json.push({
 15703        selector: !selector ? 'core' : selector.toString(),
 15704        style: css
 15705      });
 15706    }
 15707  
 15708    return json;
 15709  };
 15710  
 15711  var styfn$5 = {};
 15712  
 15713  styfn$5.appendFromString = function (string) {
 15714    var self = this;
 15715    var style = this;
 15716    var remaining = '' + string;
 15717    var selAndBlockStr;
 15718    var blockRem;
 15719    var propAndValStr; // remove comments from the style string
 15720  
 15721    remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
 15722  
 15723    function removeSelAndBlockFromRemaining() {
 15724      // remove the parsed selector and block from the remaining text to parse
 15725      if (remaining.length > selAndBlockStr.length) {
 15726        remaining = remaining.substr(selAndBlockStr.length);
 15727      } else {
 15728        remaining = '';
 15729      }
 15730    }
 15731  
 15732    function removePropAndValFromRem() {
 15733      // remove the parsed property and value from the remaining block text to parse
 15734      if (blockRem.length > propAndValStr.length) {
 15735        blockRem = blockRem.substr(propAndValStr.length);
 15736      } else {
 15737        blockRem = '';
 15738      }
 15739    }
 15740  
 15741    for (;;) {
 15742      var nothingLeftToParse = remaining.match(/^\s*$/);
 15743  
 15744      if (nothingLeftToParse) {
 15745        break;
 15746      }
 15747  
 15748      var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
 15749  
 15750      if (!selAndBlock) {
 15751        warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
 15752        break;
 15753      }
 15754  
 15755      selAndBlockStr = selAndBlock[0]; // parse the selector
 15756  
 15757      var selectorStr = selAndBlock[1];
 15758  
 15759      if (selectorStr !== 'core') {
 15760        var selector = new Selector(selectorStr);
 15761  
 15762        if (selector.invalid) {
 15763          warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
 15764  
 15765          removeSelAndBlockFromRemaining();
 15766          continue;
 15767        }
 15768      } // parse the block of properties and values
 15769  
 15770  
 15771      var blockStr = selAndBlock[2];
 15772      var invalidBlock = false;
 15773      blockRem = blockStr;
 15774      var props = [];
 15775  
 15776      for (;;) {
 15777        var _nothingLeftToParse = blockRem.match(/^\s*$/);
 15778  
 15779        if (_nothingLeftToParse) {
 15780          break;
 15781        }
 15782  
 15783        var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
 15784  
 15785        if (!propAndVal) {
 15786          warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
 15787          invalidBlock = true;
 15788          break;
 15789        }
 15790  
 15791        propAndValStr = propAndVal[0];
 15792        var propStr = propAndVal[1];
 15793        var valStr = propAndVal[2];
 15794        var prop = self.properties[propStr];
 15795  
 15796        if (!prop) {
 15797          warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
 15798  
 15799          removePropAndValFromRem();
 15800          continue;
 15801        }
 15802  
 15803        var parsedProp = style.parse(propStr, valStr);
 15804  
 15805        if (!parsedProp) {
 15806          warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
 15807  
 15808          removePropAndValFromRem();
 15809          continue;
 15810        }
 15811  
 15812        props.push({
 15813          name: propStr,
 15814          val: valStr
 15815        });
 15816        removePropAndValFromRem();
 15817      }
 15818  
 15819      if (invalidBlock) {
 15820        removeSelAndBlockFromRemaining();
 15821        break;
 15822      } // put the parsed block in the style
 15823  
 15824  
 15825      style.selector(selectorStr);
 15826  
 15827      for (var i = 0; i < props.length; i++) {
 15828        var _prop = props[i];
 15829        style.css(_prop.name, _prop.val);
 15830      }
 15831  
 15832      removeSelAndBlockFromRemaining();
 15833    }
 15834  
 15835    return style;
 15836  };
 15837  
 15838  styfn$5.fromString = function (string) {
 15839    var style = this;
 15840    style.resetToDefault();
 15841    style.appendFromString(string);
 15842    return style;
 15843  };
 15844  
 15845  var styfn$6 = {};
 15846  
 15847  (function () {
 15848    var number = number$1;
 15849    var rgba = rgbaNoBackRefs;
 15850    var hsla = hslaNoBackRefs;
 15851    var hex3$1 = hex3;
 15852    var hex6$1 = hex6;
 15853  
 15854    var data = function data(prefix) {
 15855      return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
 15856    };
 15857  
 15858    var mapData = function mapData(prefix) {
 15859      var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
 15860      return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
 15861    };
 15862  
 15863    var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
 15864  
 15865    styfn$6.types = {
 15866      time: {
 15867        number: true,
 15868        min: 0,
 15869        units: 's|ms',
 15870        implicitUnits: 'ms'
 15871      },
 15872      percent: {
 15873        number: true,
 15874        min: 0,
 15875        max: 100,
 15876        units: '%',
 15877        implicitUnits: '%'
 15878      },
 15879      percentages: {
 15880        number: true,
 15881        min: 0,
 15882        max: 100,
 15883        units: '%',
 15884        implicitUnits: '%',
 15885        multiple: true
 15886      },
 15887      zeroOneNumber: {
 15888        number: true,
 15889        min: 0,
 15890        max: 1,
 15891        unitless: true
 15892      },
 15893      zeroOneNumbers: {
 15894        number: true,
 15895        min: 0,
 15896        max: 1,
 15897        unitless: true,
 15898        multiple: true
 15899      },
 15900      nOneOneNumber: {
 15901        number: true,
 15902        min: -1,
 15903        max: 1,
 15904        unitless: true
 15905      },
 15906      nonNegativeInt: {
 15907        number: true,
 15908        min: 0,
 15909        integer: true,
 15910        unitless: true
 15911      },
 15912      position: {
 15913        enums: ['parent', 'origin']
 15914      },
 15915      nodeSize: {
 15916        number: true,
 15917        min: 0,
 15918        enums: ['label']
 15919      },
 15920      number: {
 15921        number: true,
 15922        unitless: true
 15923      },
 15924      numbers: {
 15925        number: true,
 15926        unitless: true,
 15927        multiple: true
 15928      },
 15929      positiveNumber: {
 15930        number: true,
 15931        unitless: true,
 15932        min: 0,
 15933        strictMin: true
 15934      },
 15935      size: {
 15936        number: true,
 15937        min: 0
 15938      },
 15939      bidirectionalSize: {
 15940        number: true
 15941      },
 15942      // allows negative
 15943      bidirectionalSizes: {
 15944        number: true,
 15945        multiple: true
 15946      },
 15947      // allows negative
 15948      sizeMaybePercent: {
 15949        number: true,
 15950        min: 0,
 15951        allowPercent: true
 15952      },
 15953      axisDirection: {
 15954        enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
 15955      },
 15956      paddingRelativeTo: {
 15957        enums: ['width', 'height', 'average', 'min', 'max']
 15958      },
 15959      bgWH: {
 15960        number: true,
 15961        min: 0,
 15962        allowPercent: true,
 15963        enums: ['auto'],
 15964        multiple: true
 15965      },
 15966      bgPos: {
 15967        number: true,
 15968        allowPercent: true,
 15969        multiple: true
 15970      },
 15971      bgRelativeTo: {
 15972        enums: ['inner', 'include-padding'],
 15973        multiple: true
 15974      },
 15975      bgRepeat: {
 15976        enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
 15977        multiple: true
 15978      },
 15979      bgFit: {
 15980        enums: ['none', 'contain', 'cover'],
 15981        multiple: true
 15982      },
 15983      bgCrossOrigin: {
 15984        enums: ['anonymous', 'use-credentials'],
 15985        multiple: true
 15986      },
 15987      bgClip: {
 15988        enums: ['none', 'node'],
 15989        multiple: true
 15990      },
 15991      color: {
 15992        color: true
 15993      },
 15994      colors: {
 15995        color: true,
 15996        multiple: true
 15997      },
 15998      fill: {
 15999        enums: ['solid', 'linear-gradient', 'radial-gradient']
 16000      },
 16001      bool: {
 16002        enums: ['yes', 'no']
 16003      },
 16004      lineStyle: {
 16005        enums: ['solid', 'dotted', 'dashed']
 16006      },
 16007      lineCap: {
 16008        enums: ['butt', 'round', 'square']
 16009      },
 16010      borderStyle: {
 16011        enums: ['solid', 'dotted', 'dashed', 'double']
 16012      },
 16013      curveStyle: {
 16014        enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'taxi']
 16015      },
 16016      fontFamily: {
 16017        regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
 16018      },
 16019      fontStyle: {
 16020        enums: ['italic', 'normal', 'oblique']
 16021      },
 16022      fontWeight: {
 16023        enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
 16024      },
 16025      textDecoration: {
 16026        enums: ['none', 'underline', 'overline', 'line-through']
 16027      },
 16028      textTransform: {
 16029        enums: ['none', 'uppercase', 'lowercase']
 16030      },
 16031      textWrap: {
 16032        enums: ['none', 'wrap', 'ellipsis']
 16033      },
 16034      textOverflowWrap: {
 16035        enums: ['whitespace', 'anywhere']
 16036      },
 16037      textBackgroundShape: {
 16038        enums: ['rectangle', 'roundrectangle', 'round-rectangle']
 16039      },
 16040      nodeShape: {
 16041        enums: ['rectangle', 'roundrectangle', 'round-rectangle', 'cutrectangle', 'cut-rectangle', 'bottomroundrectangle', 'bottom-round-rectangle', 'barrel', 'ellipse', 'triangle', 'round-triangle', 'square', 'pentagon', 'round-pentagon', 'hexagon', 'round-hexagon', 'concavehexagon', 'concave-hexagon', 'heptagon', 'round-heptagon', 'octagon', 'round-octagon', 'tag', 'round-tag', 'star', 'diamond', 'round-diamond', 'vee', 'rhomboid', 'polygon']
 16042      },
 16043      compoundIncludeLabels: {
 16044        enums: ['include', 'exclude']
 16045      },
 16046      arrowShape: {
 16047        enums: ['tee', 'triangle', 'triangle-tee', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
 16048      },
 16049      arrowFill: {
 16050        enums: ['filled', 'hollow']
 16051      },
 16052      display: {
 16053        enums: ['element', 'none']
 16054      },
 16055      visibility: {
 16056        enums: ['hidden', 'visible']
 16057      },
 16058      zCompoundDepth: {
 16059        enums: ['bottom', 'orphan', 'auto', 'top']
 16060      },
 16061      zIndexCompare: {
 16062        enums: ['auto', 'manual']
 16063      },
 16064      valign: {
 16065        enums: ['top', 'center', 'bottom']
 16066      },
 16067      halign: {
 16068        enums: ['left', 'center', 'right']
 16069      },
 16070      justification: {
 16071        enums: ['left', 'center', 'right', 'auto']
 16072      },
 16073      text: {
 16074        string: true
 16075      },
 16076      data: {
 16077        mapping: true,
 16078        regex: data('data')
 16079      },
 16080      layoutData: {
 16081        mapping: true,
 16082        regex: data('layoutData')
 16083      },
 16084      scratch: {
 16085        mapping: true,
 16086        regex: data('scratch')
 16087      },
 16088      mapData: {
 16089        mapping: true,
 16090        regex: mapData('mapData')
 16091      },
 16092      mapLayoutData: {
 16093        mapping: true,
 16094        regex: mapData('mapLayoutData')
 16095      },
 16096      mapScratch: {
 16097        mapping: true,
 16098        regex: mapData('mapScratch')
 16099      },
 16100      fn: {
 16101        mapping: true,
 16102        fn: true
 16103      },
 16104      url: {
 16105        regexes: urlRegexes,
 16106        singleRegexMatchValue: true
 16107      },
 16108      urls: {
 16109        regexes: urlRegexes,
 16110        singleRegexMatchValue: true,
 16111        multiple: true
 16112      },
 16113      propList: {
 16114        propList: true
 16115      },
 16116      angle: {
 16117        number: true,
 16118        units: 'deg|rad',
 16119        implicitUnits: 'rad'
 16120      },
 16121      textRotation: {
 16122        number: true,
 16123        units: 'deg|rad',
 16124        implicitUnits: 'rad',
 16125        enums: ['none', 'autorotate']
 16126      },
 16127      polygonPointList: {
 16128        number: true,
 16129        multiple: true,
 16130        evenMultiple: true,
 16131        min: -1,
 16132        max: 1,
 16133        unitless: true
 16134      },
 16135      edgeDistances: {
 16136        enums: ['intersection', 'node-position']
 16137      },
 16138      edgeEndpoint: {
 16139        number: true,
 16140        multiple: true,
 16141        units: '%|px|em|deg|rad',
 16142        implicitUnits: 'px',
 16143        enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
 16144        singleEnum: true,
 16145        validate: function validate(valArr, unitsArr) {
 16146          switch (valArr.length) {
 16147            case 2:
 16148              // can be % or px only
 16149              return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
 16150  
 16151            case 1:
 16152              // can be enum, deg, or rad only
 16153              return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
 16154  
 16155            default:
 16156              return false;
 16157          }
 16158        }
 16159      },
 16160      easing: {
 16161        regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
 16162        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']
 16163      },
 16164      gradientDirection: {
 16165        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']
 16166      },
 16167      boundsExpansion: {
 16168        number: true,
 16169        multiple: true,
 16170        min: 0,
 16171        validate: function validate(valArr) {
 16172          var length = valArr.length;
 16173          return length === 1 || length === 2 || length === 4;
 16174        }
 16175      }
 16176    };
 16177    var diff = {
 16178      zeroNonZero: function zeroNonZero(val1, val2) {
 16179        if ((val1 == null || val2 == null) && val1 !== val2) {
 16180          return true; // null cases could represent any value
 16181        }
 16182  
 16183        if (val1 == 0 && val2 != 0) {
 16184          return true;
 16185        } else if (val1 != 0 && val2 == 0) {
 16186          return true;
 16187        } else {
 16188          return false;
 16189        }
 16190      },
 16191      any: function any(val1, val2) {
 16192        return val1 != val2;
 16193      }
 16194    }; // define visual style properties
 16195    //
 16196    // - n.b. adding a new group of props may require updates to updateStyleHints()
 16197    // - adding new props to an existing group gets handled automatically
 16198  
 16199    var t = styfn$6.types;
 16200    var mainLabel = [{
 16201      name: 'label',
 16202      type: t.text,
 16203      triggersBounds: diff.any
 16204    }, {
 16205      name: 'text-rotation',
 16206      type: t.textRotation,
 16207      triggersBounds: diff.any
 16208    }, {
 16209      name: 'text-margin-x',
 16210      type: t.bidirectionalSize,
 16211      triggersBounds: diff.any
 16212    }, {
 16213      name: 'text-margin-y',
 16214      type: t.bidirectionalSize,
 16215      triggersBounds: diff.any
 16216    }];
 16217    var sourceLabel = [{
 16218      name: 'source-label',
 16219      type: t.text,
 16220      triggersBounds: diff.any
 16221    }, {
 16222      name: 'source-text-rotation',
 16223      type: t.textRotation,
 16224      triggersBounds: diff.any
 16225    }, {
 16226      name: 'source-text-margin-x',
 16227      type: t.bidirectionalSize,
 16228      triggersBounds: diff.any
 16229    }, {
 16230      name: 'source-text-margin-y',
 16231      type: t.bidirectionalSize,
 16232      triggersBounds: diff.any
 16233    }, {
 16234      name: 'source-text-offset',
 16235      type: t.size,
 16236      triggersBounds: diff.any
 16237    }];
 16238    var targetLabel = [{
 16239      name: 'target-label',
 16240      type: t.text,
 16241      triggersBounds: diff.any
 16242    }, {
 16243      name: 'target-text-rotation',
 16244      type: t.textRotation,
 16245      triggersBounds: diff.any
 16246    }, {
 16247      name: 'target-text-margin-x',
 16248      type: t.bidirectionalSize,
 16249      triggersBounds: diff.any
 16250    }, {
 16251      name: 'target-text-margin-y',
 16252      type: t.bidirectionalSize,
 16253      triggersBounds: diff.any
 16254    }, {
 16255      name: 'target-text-offset',
 16256      type: t.size,
 16257      triggersBounds: diff.any
 16258    }];
 16259    var labelDimensions = [{
 16260      name: 'font-family',
 16261      type: t.fontFamily,
 16262      triggersBounds: diff.any
 16263    }, {
 16264      name: 'font-style',
 16265      type: t.fontStyle,
 16266      triggersBounds: diff.any
 16267    }, {
 16268      name: 'font-weight',
 16269      type: t.fontWeight,
 16270      triggersBounds: diff.any
 16271    }, {
 16272      name: 'font-size',
 16273      type: t.size,
 16274      triggersBounds: diff.any
 16275    }, {
 16276      name: 'text-transform',
 16277      type: t.textTransform,
 16278      triggersBounds: diff.any
 16279    }, {
 16280      name: 'text-wrap',
 16281      type: t.textWrap,
 16282      triggersBounds: diff.any
 16283    }, {
 16284      name: 'text-overflow-wrap',
 16285      type: t.textOverflowWrap,
 16286      triggersBounds: diff.any
 16287    }, {
 16288      name: 'text-max-width',
 16289      type: t.size,
 16290      triggersBounds: diff.any
 16291    }, {
 16292      name: 'text-outline-width',
 16293      type: t.size,
 16294      triggersBounds: diff.any
 16295    }, {
 16296      name: 'line-height',
 16297      type: t.positiveNumber,
 16298      triggersBounds: diff.any
 16299    }];
 16300    var commonLabel = [{
 16301      name: 'text-valign',
 16302      type: t.valign,
 16303      triggersBounds: diff.any
 16304    }, {
 16305      name: 'text-halign',
 16306      type: t.halign,
 16307      triggersBounds: diff.any
 16308    }, {
 16309      name: 'color',
 16310      type: t.color
 16311    }, {
 16312      name: 'text-outline-color',
 16313      type: t.color
 16314    }, {
 16315      name: 'text-outline-opacity',
 16316      type: t.zeroOneNumber
 16317    }, {
 16318      name: 'text-background-color',
 16319      type: t.color
 16320    }, {
 16321      name: 'text-background-opacity',
 16322      type: t.zeroOneNumber
 16323    }, {
 16324      name: 'text-background-padding',
 16325      type: t.size,
 16326      triggersBounds: diff.any
 16327    }, {
 16328      name: 'text-border-opacity',
 16329      type: t.zeroOneNumber
 16330    }, {
 16331      name: 'text-border-color',
 16332      type: t.color
 16333    }, {
 16334      name: 'text-border-width',
 16335      type: t.size,
 16336      triggersBounds: diff.any
 16337    }, {
 16338      name: 'text-border-style',
 16339      type: t.borderStyle,
 16340      triggersBounds: diff.any
 16341    }, {
 16342      name: 'text-background-shape',
 16343      type: t.textBackgroundShape,
 16344      triggersBounds: diff.any
 16345    }, {
 16346      name: 'text-justification',
 16347      type: t.justification
 16348    }];
 16349    var behavior = [{
 16350      name: 'events',
 16351      type: t.bool
 16352    }, {
 16353      name: 'text-events',
 16354      type: t.bool
 16355    }];
 16356    var visibility = [{
 16357      name: 'display',
 16358      type: t.display,
 16359      triggersZOrder: diff.any,
 16360      triggersBounds: diff.any,
 16361      triggersBoundsOfParallelBeziers: true
 16362    }, {
 16363      name: 'visibility',
 16364      type: t.visibility,
 16365      triggersZOrder: diff.any
 16366    }, {
 16367      name: 'opacity',
 16368      type: t.zeroOneNumber,
 16369      triggersZOrder: diff.zeroNonZero
 16370    }, {
 16371      name: 'text-opacity',
 16372      type: t.zeroOneNumber
 16373    }, {
 16374      name: 'min-zoomed-font-size',
 16375      type: t.size
 16376    }, {
 16377      name: 'z-compound-depth',
 16378      type: t.zCompoundDepth,
 16379      triggersZOrder: diff.any
 16380    }, {
 16381      name: 'z-index-compare',
 16382      type: t.zIndexCompare,
 16383      triggersZOrder: diff.any
 16384    }, {
 16385      name: 'z-index',
 16386      type: t.nonNegativeInt,
 16387      triggersZOrder: diff.any
 16388    }];
 16389    var overlay = [{
 16390      name: 'overlay-padding',
 16391      type: t.size,
 16392      triggersBounds: diff.any
 16393    }, {
 16394      name: 'overlay-color',
 16395      type: t.color
 16396    }, {
 16397      name: 'overlay-opacity',
 16398      type: t.zeroOneNumber,
 16399      triggersBounds: diff.zeroNonZero
 16400    }];
 16401    var transition = [{
 16402      name: 'transition-property',
 16403      type: t.propList
 16404    }, {
 16405      name: 'transition-duration',
 16406      type: t.time
 16407    }, {
 16408      name: 'transition-delay',
 16409      type: t.time
 16410    }, {
 16411      name: 'transition-timing-function',
 16412      type: t.easing
 16413    }];
 16414  
 16415    var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
 16416      if (parsedProp.value === 'label') {
 16417        return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
 16418      } else {
 16419        return parsedProp.pfValue;
 16420      }
 16421    };
 16422  
 16423    var nodeBody = [{
 16424      name: 'height',
 16425      type: t.nodeSize,
 16426      triggersBounds: diff.any,
 16427      hashOverride: nodeSizeHashOverride
 16428    }, {
 16429      name: 'width',
 16430      type: t.nodeSize,
 16431      triggersBounds: diff.any,
 16432      hashOverride: nodeSizeHashOverride
 16433    }, {
 16434      name: 'shape',
 16435      type: t.nodeShape,
 16436      triggersBounds: diff.any
 16437    }, {
 16438      name: 'shape-polygon-points',
 16439      type: t.polygonPointList,
 16440      triggersBounds: diff.any
 16441    }, {
 16442      name: 'background-color',
 16443      type: t.color
 16444    }, {
 16445      name: 'background-fill',
 16446      type: t.fill
 16447    }, {
 16448      name: 'background-opacity',
 16449      type: t.zeroOneNumber
 16450    }, {
 16451      name: 'background-blacken',
 16452      type: t.nOneOneNumber
 16453    }, {
 16454      name: 'background-gradient-stop-colors',
 16455      type: t.colors
 16456    }, {
 16457      name: 'background-gradient-stop-positions',
 16458      type: t.percentages
 16459    }, {
 16460      name: 'background-gradient-direction',
 16461      type: t.gradientDirection
 16462    }, {
 16463      name: 'padding',
 16464      type: t.sizeMaybePercent,
 16465      triggersBounds: diff.any
 16466    }, {
 16467      name: 'padding-relative-to',
 16468      type: t.paddingRelativeTo,
 16469      triggersBounds: diff.any
 16470    }, {
 16471      name: 'bounds-expansion',
 16472      type: t.boundsExpansion,
 16473      triggersBounds: diff.any
 16474    }];
 16475    var nodeBorder = [{
 16476      name: 'border-color',
 16477      type: t.color
 16478    }, {
 16479      name: 'border-opacity',
 16480      type: t.zeroOneNumber
 16481    }, {
 16482      name: 'border-width',
 16483      type: t.size,
 16484      triggersBounds: diff.any
 16485    }, {
 16486      name: 'border-style',
 16487      type: t.borderStyle
 16488    }];
 16489    var backgroundImage = [{
 16490      name: 'background-image',
 16491      type: t.urls
 16492    }, {
 16493      name: 'background-image-crossorigin',
 16494      type: t.bgCrossOrigin
 16495    }, {
 16496      name: 'background-image-opacity',
 16497      type: t.zeroOneNumbers
 16498    }, {
 16499      name: 'background-position-x',
 16500      type: t.bgPos
 16501    }, {
 16502      name: 'background-position-y',
 16503      type: t.bgPos
 16504    }, {
 16505      name: 'background-width-relative-to',
 16506      type: t.bgRelativeTo
 16507    }, {
 16508      name: 'background-height-relative-to',
 16509      type: t.bgRelativeTo
 16510    }, {
 16511      name: 'background-repeat',
 16512      type: t.bgRepeat
 16513    }, {
 16514      name: 'background-fit',
 16515      type: t.bgFit
 16516    }, {
 16517      name: 'background-clip',
 16518      type: t.bgClip
 16519    }, {
 16520      name: 'background-width',
 16521      type: t.bgWH
 16522    }, {
 16523      name: 'background-height',
 16524      type: t.bgWH
 16525    }, {
 16526      name: 'background-offset-x',
 16527      type: t.bgPos
 16528    }, {
 16529      name: 'background-offset-y',
 16530      type: t.bgPos
 16531    }];
 16532    var compound = [{
 16533      name: 'position',
 16534      type: t.position,
 16535      triggersBounds: diff.any
 16536    }, {
 16537      name: 'compound-sizing-wrt-labels',
 16538      type: t.compoundIncludeLabels,
 16539      triggersBounds: diff.any
 16540    }, {
 16541      name: 'min-width',
 16542      type: t.size,
 16543      triggersBounds: diff.any
 16544    }, {
 16545      name: 'min-width-bias-left',
 16546      type: t.sizeMaybePercent,
 16547      triggersBounds: diff.any
 16548    }, {
 16549      name: 'min-width-bias-right',
 16550      type: t.sizeMaybePercent,
 16551      triggersBounds: diff.any
 16552    }, {
 16553      name: 'min-height',
 16554      type: t.size,
 16555      triggersBounds: diff.any
 16556    }, {
 16557      name: 'min-height-bias-top',
 16558      type: t.sizeMaybePercent,
 16559      triggersBounds: diff.any
 16560    }, {
 16561      name: 'min-height-bias-bottom',
 16562      type: t.sizeMaybePercent,
 16563      triggersBounds: diff.any
 16564    }];
 16565    var edgeLine = [{
 16566      name: 'line-style',
 16567      type: t.lineStyle
 16568    }, {
 16569      name: 'line-color',
 16570      type: t.color
 16571    }, {
 16572      name: 'line-fill',
 16573      type: t.fill
 16574    }, {
 16575      name: 'line-cap',
 16576      type: t.lineCap
 16577    }, {
 16578      name: 'line-dash-pattern',
 16579      type: t.numbers
 16580    }, {
 16581      name: 'line-dash-offset',
 16582      type: t.number
 16583    }, {
 16584      name: 'line-gradient-stop-colors',
 16585      type: t.colors
 16586    }, {
 16587      name: 'line-gradient-stop-positions',
 16588      type: t.percentages
 16589    }, {
 16590      name: 'curve-style',
 16591      type: t.curveStyle,
 16592      triggersBounds: diff.any,
 16593      triggersBoundsOfParallelBeziers: true
 16594    }, {
 16595      name: 'haystack-radius',
 16596      type: t.zeroOneNumber,
 16597      triggersBounds: diff.any
 16598    }, {
 16599      name: 'source-endpoint',
 16600      type: t.edgeEndpoint,
 16601      triggersBounds: diff.any
 16602    }, {
 16603      name: 'target-endpoint',
 16604      type: t.edgeEndpoint,
 16605      triggersBounds: diff.any
 16606    }, {
 16607      name: 'control-point-step-size',
 16608      type: t.size,
 16609      triggersBounds: diff.any
 16610    }, {
 16611      name: 'control-point-distances',
 16612      type: t.bidirectionalSizes,
 16613      triggersBounds: diff.any
 16614    }, {
 16615      name: 'control-point-weights',
 16616      type: t.numbers,
 16617      triggersBounds: diff.any
 16618    }, {
 16619      name: 'segment-distances',
 16620      type: t.bidirectionalSizes,
 16621      triggersBounds: diff.any
 16622    }, {
 16623      name: 'segment-weights',
 16624      type: t.numbers,
 16625      triggersBounds: diff.any
 16626    }, {
 16627      name: 'taxi-turn',
 16628      type: t.sizeMaybePercent,
 16629      triggersBounds: diff.any
 16630    }, {
 16631      name: 'taxi-turn-min-distance',
 16632      type: t.size,
 16633      triggersBounds: diff.any
 16634    }, {
 16635      name: 'taxi-direction',
 16636      type: t.axisDirection,
 16637      triggersBounds: diff.any
 16638    }, {
 16639      name: 'edge-distances',
 16640      type: t.edgeDistances,
 16641      triggersBounds: diff.any
 16642    }, {
 16643      name: 'arrow-scale',
 16644      type: t.positiveNumber,
 16645      triggersBounds: diff.any
 16646    }, {
 16647      name: 'loop-direction',
 16648      type: t.angle,
 16649      triggersBounds: diff.any
 16650    }, {
 16651      name: 'loop-sweep',
 16652      type: t.angle,
 16653      triggersBounds: diff.any
 16654    }, {
 16655      name: 'source-distance-from-node',
 16656      type: t.size,
 16657      triggersBounds: diff.any
 16658    }, {
 16659      name: 'target-distance-from-node',
 16660      type: t.size,
 16661      triggersBounds: diff.any
 16662    }];
 16663    var ghost = [{
 16664      name: 'ghost',
 16665      type: t.bool,
 16666      triggersBounds: diff.any
 16667    }, {
 16668      name: 'ghost-offset-x',
 16669      type: t.bidirectionalSize,
 16670      triggersBounds: diff.any
 16671    }, {
 16672      name: 'ghost-offset-y',
 16673      type: t.bidirectionalSize,
 16674      triggersBounds: diff.any
 16675    }, {
 16676      name: 'ghost-opacity',
 16677      type: t.zeroOneNumber
 16678    }];
 16679    var core = [{
 16680      name: 'selection-box-color',
 16681      type: t.color
 16682    }, {
 16683      name: 'selection-box-opacity',
 16684      type: t.zeroOneNumber
 16685    }, {
 16686      name: 'selection-box-border-color',
 16687      type: t.color
 16688    }, {
 16689      name: 'selection-box-border-width',
 16690      type: t.size
 16691    }, {
 16692      name: 'active-bg-color',
 16693      type: t.color
 16694    }, {
 16695      name: 'active-bg-opacity',
 16696      type: t.zeroOneNumber
 16697    }, {
 16698      name: 'active-bg-size',
 16699      type: t.size
 16700    }, {
 16701      name: 'outside-texture-bg-color',
 16702      type: t.color
 16703    }, {
 16704      name: 'outside-texture-bg-opacity',
 16705      type: t.zeroOneNumber
 16706    }]; // pie backgrounds for nodes
 16707  
 16708    var pie = [];
 16709    styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
 16710  
 16711    pie.push({
 16712      name: 'pie-size',
 16713      type: t.sizeMaybePercent
 16714    });
 16715  
 16716    for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
 16717      pie.push({
 16718        name: 'pie-' + i + '-background-color',
 16719        type: t.color
 16720      });
 16721      pie.push({
 16722        name: 'pie-' + i + '-background-size',
 16723        type: t.percent
 16724      });
 16725      pie.push({
 16726        name: 'pie-' + i + '-background-opacity',
 16727        type: t.zeroOneNumber
 16728      });
 16729    } // edge arrows
 16730  
 16731  
 16732    var edgeArrow = [];
 16733    var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
 16734    [{
 16735      name: 'arrow-shape',
 16736      type: t.arrowShape,
 16737      triggersBounds: diff.any
 16738    }, {
 16739      name: 'arrow-color',
 16740      type: t.color
 16741    }, {
 16742      name: 'arrow-fill',
 16743      type: t.arrowFill
 16744    }].forEach(function (prop) {
 16745      arrowPrefixes.forEach(function (prefix) {
 16746        var name = prefix + '-' + prop.name;
 16747        var type = prop.type,
 16748            triggersBounds = prop.triggersBounds;
 16749        edgeArrow.push({
 16750          name: name,
 16751          type: type,
 16752          triggersBounds: triggersBounds
 16753        });
 16754      });
 16755    }, {});
 16756    var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
 16757    var propGroups = styfn$6.propertyGroups = {
 16758      // common to all eles
 16759      behavior: behavior,
 16760      transition: transition,
 16761      visibility: visibility,
 16762      overlay: overlay,
 16763      ghost: ghost,
 16764      // labels
 16765      commonLabel: commonLabel,
 16766      labelDimensions: labelDimensions,
 16767      mainLabel: mainLabel,
 16768      sourceLabel: sourceLabel,
 16769      targetLabel: targetLabel,
 16770      // node props
 16771      nodeBody: nodeBody,
 16772      nodeBorder: nodeBorder,
 16773      backgroundImage: backgroundImage,
 16774      pie: pie,
 16775      compound: compound,
 16776      // edge props
 16777      edgeLine: edgeLine,
 16778      edgeArrow: edgeArrow,
 16779      core: core
 16780    };
 16781    var propGroupNames = styfn$6.propertyGroupNames = {};
 16782    var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
 16783    propGroupKeys.forEach(function (key) {
 16784      propGroupNames[key] = propGroups[key].map(function (prop) {
 16785        return prop.name;
 16786      });
 16787      propGroups[key].forEach(function (prop) {
 16788        return prop.groupKey = key;
 16789      });
 16790    }); // define aliases
 16791  
 16792    var aliases = styfn$6.aliases = [{
 16793      name: 'content',
 16794      pointsTo: 'label'
 16795    }, {
 16796      name: 'control-point-distance',
 16797      pointsTo: 'control-point-distances'
 16798    }, {
 16799      name: 'control-point-weight',
 16800      pointsTo: 'control-point-weights'
 16801    }, {
 16802      name: 'edge-text-rotation',
 16803      pointsTo: 'text-rotation'
 16804    }, {
 16805      name: 'padding-left',
 16806      pointsTo: 'padding'
 16807    }, {
 16808      name: 'padding-right',
 16809      pointsTo: 'padding'
 16810    }, {
 16811      name: 'padding-top',
 16812      pointsTo: 'padding'
 16813    }, {
 16814      name: 'padding-bottom',
 16815      pointsTo: 'padding'
 16816    }]; // list of property names
 16817  
 16818    styfn$6.propertyNames = props.map(function (p) {
 16819      return p.name;
 16820    }); // allow access of properties by name ( e.g. style.properties.height )
 16821  
 16822    for (var _i = 0; _i < props.length; _i++) {
 16823      var prop = props[_i];
 16824      props[prop.name] = prop; // allow lookup by name
 16825    } // map aliases
 16826  
 16827  
 16828    for (var _i2 = 0; _i2 < aliases.length; _i2++) {
 16829      var alias = aliases[_i2];
 16830      var pointsToProp = props[alias.pointsTo];
 16831      var aliasProp = {
 16832        name: alias.name,
 16833        alias: true,
 16834        pointsTo: pointsToProp
 16835      }; // add alias prop for parsing
 16836  
 16837      props.push(aliasProp);
 16838      props[alias.name] = aliasProp; // allow lookup by name
 16839    }
 16840  })();
 16841  
 16842  styfn$6.getDefaultProperty = function (name) {
 16843    return this.getDefaultProperties()[name];
 16844  };
 16845  
 16846  styfn$6.getDefaultProperties = function () {
 16847    var _p = this._private;
 16848  
 16849    if (_p.defaultProperties != null) {
 16850      return _p.defaultProperties;
 16851    }
 16852  
 16853    var rawProps = extend({
 16854      // core props
 16855      'selection-box-color': '#ddd',
 16856      'selection-box-opacity': 0.65,
 16857      'selection-box-border-color': '#aaa',
 16858      'selection-box-border-width': 1,
 16859      'active-bg-color': 'black',
 16860      'active-bg-opacity': 0.15,
 16861      'active-bg-size': 30,
 16862      'outside-texture-bg-color': '#000',
 16863      'outside-texture-bg-opacity': 0.125,
 16864      // common node/edge props
 16865      'events': 'yes',
 16866      'text-events': 'no',
 16867      'text-valign': 'top',
 16868      'text-halign': 'center',
 16869      'text-justification': 'auto',
 16870      'line-height': 1,
 16871      'color': '#000',
 16872      'text-outline-color': '#000',
 16873      'text-outline-width': 0,
 16874      'text-outline-opacity': 1,
 16875      'text-opacity': 1,
 16876      'text-decoration': 'none',
 16877      'text-transform': 'none',
 16878      'text-wrap': 'none',
 16879      'text-overflow-wrap': 'whitespace',
 16880      'text-max-width': 9999,
 16881      'text-background-color': '#000',
 16882      'text-background-opacity': 0,
 16883      'text-background-shape': 'rectangle',
 16884      'text-background-padding': 0,
 16885      'text-border-opacity': 0,
 16886      'text-border-width': 0,
 16887      'text-border-style': 'solid',
 16888      'text-border-color': '#000',
 16889      'font-family': 'Helvetica Neue, Helvetica, sans-serif',
 16890      'font-style': 'normal',
 16891      'font-weight': 'normal',
 16892      'font-size': 16,
 16893      'min-zoomed-font-size': 0,
 16894      'text-rotation': 'none',
 16895      'source-text-rotation': 'none',
 16896      'target-text-rotation': 'none',
 16897      'visibility': 'visible',
 16898      'display': 'element',
 16899      'opacity': 1,
 16900      'z-compound-depth': 'auto',
 16901      'z-index-compare': 'auto',
 16902      'z-index': 0,
 16903      'label': '',
 16904      'text-margin-x': 0,
 16905      'text-margin-y': 0,
 16906      'source-label': '',
 16907      'source-text-offset': 0,
 16908      'source-text-margin-x': 0,
 16909      'source-text-margin-y': 0,
 16910      'target-label': '',
 16911      'target-text-offset': 0,
 16912      'target-text-margin-x': 0,
 16913      'target-text-margin-y': 0,
 16914      'overlay-opacity': 0,
 16915      'overlay-color': '#000',
 16916      'overlay-padding': 10,
 16917      'transition-property': 'none',
 16918      'transition-duration': 0,
 16919      'transition-delay': 0,
 16920      'transition-timing-function': 'linear',
 16921      // node props
 16922      'background-blacken': 0,
 16923      'background-color': '#999',
 16924      'background-fill': 'solid',
 16925      'background-opacity': 1,
 16926      'background-image': 'none',
 16927      'background-image-crossorigin': 'anonymous',
 16928      'background-image-opacity': 1,
 16929      'background-position-x': '50%',
 16930      'background-position-y': '50%',
 16931      'background-offset-x': 0,
 16932      'background-offset-y': 0,
 16933      'background-width-relative-to': 'include-padding',
 16934      'background-height-relative-to': 'include-padding',
 16935      'background-repeat': 'no-repeat',
 16936      'background-fit': 'none',
 16937      'background-clip': 'node',
 16938      'background-width': 'auto',
 16939      'background-height': 'auto',
 16940      'border-color': '#000',
 16941      'border-opacity': 1,
 16942      'border-width': 0,
 16943      'border-style': 'solid',
 16944      'height': 30,
 16945      'width': 30,
 16946      'shape': 'ellipse',
 16947      'shape-polygon-points': '-1, -1,   1, -1,   1, 1,   -1, 1',
 16948      'bounds-expansion': 0,
 16949      // node gradient
 16950      'background-gradient-direction': 'to-bottom',
 16951      'background-gradient-stop-colors': '#999',
 16952      'background-gradient-stop-positions': '0%',
 16953      // ghost props
 16954      'ghost': 'no',
 16955      'ghost-offset-y': 0,
 16956      'ghost-offset-x': 0,
 16957      'ghost-opacity': 0,
 16958      // compound props
 16959      'padding': 0,
 16960      'padding-relative-to': 'width',
 16961      'position': 'origin',
 16962      'compound-sizing-wrt-labels': 'include',
 16963      'min-width': 0,
 16964      'min-width-bias-left': 0,
 16965      'min-width-bias-right': 0,
 16966      'min-height': 0,
 16967      'min-height-bias-top': 0,
 16968      'min-height-bias-bottom': 0
 16969    }, {
 16970      // node pie bg
 16971      'pie-size': '100%'
 16972    }, [{
 16973      name: 'pie-{{i}}-background-color',
 16974      value: 'black'
 16975    }, {
 16976      name: 'pie-{{i}}-background-size',
 16977      value: '0%'
 16978    }, {
 16979      name: 'pie-{{i}}-background-opacity',
 16980      value: 1
 16981    }].reduce(function (css, prop) {
 16982      for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
 16983        var name = prop.name.replace('{{i}}', i);
 16984        var val = prop.value;
 16985        css[name] = val;
 16986      }
 16987  
 16988      return css;
 16989    }, {}), {
 16990      // edge props
 16991      'line-style': 'solid',
 16992      'line-color': '#999',
 16993      'line-fill': 'solid',
 16994      'line-cap': 'butt',
 16995      'line-gradient-stop-colors': '#999',
 16996      'line-gradient-stop-positions': '0%',
 16997      'control-point-step-size': 40,
 16998      'control-point-weights': 0.5,
 16999      'segment-weights': 0.5,
 17000      'segment-distances': 20,
 17001      'taxi-turn': '50%',
 17002      'taxi-turn-min-distance': 10,
 17003      'taxi-direction': 'auto',
 17004      'edge-distances': 'intersection',
 17005      'curve-style': 'haystack',
 17006      'haystack-radius': 0,
 17007      'arrow-scale': 1,
 17008      'loop-direction': '-45deg',
 17009      'loop-sweep': '-90deg',
 17010      'source-distance-from-node': 0,
 17011      'target-distance-from-node': 0,
 17012      'source-endpoint': 'outside-to-node',
 17013      'target-endpoint': 'outside-to-node',
 17014      'line-dash-pattern': [6, 3],
 17015      'line-dash-offset': 0
 17016    }, [{
 17017      name: 'arrow-shape',
 17018      value: 'none'
 17019    }, {
 17020      name: 'arrow-color',
 17021      value: '#999'
 17022    }, {
 17023      name: 'arrow-fill',
 17024      value: 'filled'
 17025    }].reduce(function (css, prop) {
 17026      styfn$6.arrowPrefixes.forEach(function (prefix) {
 17027        var name = prefix + '-' + prop.name;
 17028        var val = prop.value;
 17029        css[name] = val;
 17030      });
 17031      return css;
 17032    }, {}));
 17033    var parsedProps = {};
 17034  
 17035    for (var i = 0; i < this.properties.length; i++) {
 17036      var prop = this.properties[i];
 17037  
 17038      if (prop.pointsTo) {
 17039        continue;
 17040      }
 17041  
 17042      var name = prop.name;
 17043      var val = rawProps[name];
 17044      var parsedProp = this.parse(name, val);
 17045      parsedProps[name] = parsedProp;
 17046    }
 17047  
 17048    _p.defaultProperties = parsedProps;
 17049    return _p.defaultProperties;
 17050  };
 17051  
 17052  styfn$6.addDefaultStylesheet = function () {
 17053    this.selector(':parent').css({
 17054      'shape': 'rectangle',
 17055      'padding': 10,
 17056      'background-color': '#eee',
 17057      'border-color': '#ccc',
 17058      'border-width': 1
 17059    }).selector('edge').css({
 17060      'width': 3
 17061    }).selector(':loop').css({
 17062      'curve-style': 'bezier'
 17063    }).selector('edge:compound').css({
 17064      'curve-style': 'bezier',
 17065      'source-endpoint': 'outside-to-line',
 17066      'target-endpoint': 'outside-to-line'
 17067    }).selector(':selected').css({
 17068      'background-color': '#0169D9',
 17069      'line-color': '#0169D9',
 17070      'source-arrow-color': '#0169D9',
 17071      'target-arrow-color': '#0169D9',
 17072      'mid-source-arrow-color': '#0169D9',
 17073      'mid-target-arrow-color': '#0169D9'
 17074    }).selector(':parent:selected').css({
 17075      'background-color': '#CCE1F9',
 17076      'border-color': '#aec8e5'
 17077    }).selector(':active').css({
 17078      'overlay-color': 'black',
 17079      'overlay-padding': 10,
 17080      'overlay-opacity': 0.25
 17081    });
 17082    this.defaultLength = this.length;
 17083  };
 17084  
 17085  var styfn$7 = {}; // a caching layer for property parsing
 17086  
 17087  styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
 17088    var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
 17089  
 17090    if (fn(value)) {
 17091      return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
 17092    }
 17093  
 17094    var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
 17095    var bypassKey = propIsBypass ? 't' : 'f';
 17096    var valueKey = '' + value;
 17097    var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
 17098    var propCache = self.propCache = self.propCache || [];
 17099    var ret;
 17100  
 17101    if (!(ret = propCache[argHash])) {
 17102      ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
 17103    } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
 17104    // - mappings can't be shared b/c mappings are per-element
 17105  
 17106  
 17107    if (propIsBypass || propIsFlat === 'mapping') {
 17108      // need a copy since props are mutated later in their lifecycles
 17109      ret = copy(ret);
 17110  
 17111      if (ret) {
 17112        ret.value = copy(ret.value); // because it could be an array, e.g. colour
 17113      }
 17114    }
 17115  
 17116    return ret;
 17117  };
 17118  
 17119  styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
 17120    var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
 17121  
 17122    if (!prop && value != null) {
 17123      warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
 17124    }
 17125  
 17126    return prop;
 17127  }; // parse a property; return null on invalid; return parsed property otherwise
 17128  // fields :
 17129  // - name : the name of the property
 17130  // - value : the parsed, native-typed value of the property
 17131  // - strValue : a string value that represents the property value in valid css
 17132  // - bypass : true iff the property is a bypass property
 17133  
 17134  
 17135  styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
 17136    var self = this;
 17137    name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
 17138  
 17139    var property = self.properties[name];
 17140    var passedValue = value;
 17141    var types = self.types;
 17142  
 17143    if (!property) {
 17144      return null;
 17145    } // return null on property of unknown name
 17146  
 17147  
 17148    if (value === undefined) {
 17149      return null;
 17150    } // can't assign undefined
 17151    // the property may be an alias
 17152  
 17153  
 17154    if (property.alias) {
 17155      property = property.pointsTo;
 17156      name = property.name;
 17157    }
 17158  
 17159    var valueIsString = string(value);
 17160  
 17161    if (valueIsString) {
 17162      // trim the value to make parsing easier
 17163      value = value.trim();
 17164    }
 17165  
 17166    var type = property.type;
 17167  
 17168    if (!type) {
 17169      return null;
 17170    } // no type, no luck
 17171    // check if bypass is null or empty string (i.e. indication to delete bypass property)
 17172  
 17173  
 17174    if (propIsBypass && (value === '' || value === null)) {
 17175      return {
 17176        name: name,
 17177        value: value,
 17178        bypass: true,
 17179        deleteBypass: true
 17180      };
 17181    } // check if value is a function used as a mapper
 17182  
 17183  
 17184    if (fn(value)) {
 17185      return {
 17186        name: name,
 17187        value: value,
 17188        strValue: 'fn',
 17189        mapped: types.fn,
 17190        bypass: propIsBypass
 17191      };
 17192    } // check if value is mapped
 17193  
 17194  
 17195    var data, mapData;
 17196  
 17197    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))) {
 17198      if (propIsBypass) {
 17199        return false;
 17200      } // mappers not allowed in bypass
 17201  
 17202  
 17203      var mapped = types.data;
 17204      return {
 17205        name: name,
 17206        value: data,
 17207        strValue: '' + value,
 17208        mapped: mapped,
 17209        field: data[1],
 17210        bypass: propIsBypass
 17211      };
 17212    } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
 17213      if (propIsBypass) {
 17214        return false;
 17215      } // mappers not allowed in bypass
 17216  
 17217  
 17218      if (type.multiple) {
 17219        return false;
 17220      } // impossible to map to num
 17221  
 17222  
 17223      var _mapped = types.mapData; // we can map only if the type is a colour or a number
 17224  
 17225      if (!(type.color || type.number)) {
 17226        return false;
 17227      }
 17228  
 17229      var valueMin = this.parse(name, mapData[4]); // parse to validate
 17230  
 17231      if (!valueMin || valueMin.mapped) {
 17232        return false;
 17233      } // can't be invalid or mapped
 17234  
 17235  
 17236      var valueMax = this.parse(name, mapData[5]); // parse to validate
 17237  
 17238      if (!valueMax || valueMax.mapped) {
 17239        return false;
 17240      } // can't be invalid or mapped
 17241      // check if valueMin and valueMax are the same
 17242  
 17243  
 17244      if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
 17245        warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
 17246        return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
 17247      } else if (type.color) {
 17248        var c1 = valueMin.value;
 17249        var c2 = valueMax.value;
 17250        var same = c1[0] === c2[0] // red
 17251        && c1[1] === c2[1] // green
 17252        && c1[2] === c2[2] // blue
 17253        && ( // optional alpha
 17254        c1[3] === c2[3] // same alpha outright
 17255        || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
 17256        c2[3] == null || c2[3] === 1) // full opacity for colour 2?
 17257        );
 17258  
 17259        if (same) {
 17260          return false;
 17261        } // can't make a mapper without a range
 17262  
 17263      }
 17264  
 17265      return {
 17266        name: name,
 17267        value: mapData,
 17268        strValue: '' + value,
 17269        mapped: _mapped,
 17270        field: mapData[1],
 17271        fieldMin: parseFloat(mapData[2]),
 17272        // min & max are numeric
 17273        fieldMax: parseFloat(mapData[3]),
 17274        valueMin: valueMin.value,
 17275        valueMax: valueMax.value,
 17276        bypass: propIsBypass
 17277      };
 17278    }
 17279  
 17280    if (type.multiple && propIsFlat !== 'multiple') {
 17281      var vals;
 17282  
 17283      if (valueIsString) {
 17284        vals = value.split(/\s+/);
 17285      } else if (array(value)) {
 17286        vals = value;
 17287      } else {
 17288        vals = [value];
 17289      }
 17290  
 17291      if (type.evenMultiple && vals.length % 2 !== 0) {
 17292        return null;
 17293      }
 17294  
 17295      var valArr = [];
 17296      var unitsArr = [];
 17297      var pfValArr = [];
 17298      var strVal = '';
 17299      var hasEnum = false;
 17300  
 17301      for (var i = 0; i < vals.length; i++) {
 17302        var p = self.parse(name, vals[i], propIsBypass, 'multiple');
 17303        hasEnum = hasEnum || string(p.value);
 17304        valArr.push(p.value);
 17305        pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
 17306        unitsArr.push(p.units);
 17307        strVal += (i > 0 ? ' ' : '') + p.strValue;
 17308      }
 17309  
 17310      if (type.validate && !type.validate(valArr, unitsArr)) {
 17311        return null;
 17312      }
 17313  
 17314      if (type.singleEnum && hasEnum) {
 17315        if (valArr.length === 1 && string(valArr[0])) {
 17316          return {
 17317            name: name,
 17318            value: valArr[0],
 17319            strValue: valArr[0],
 17320            bypass: propIsBypass
 17321          };
 17322        } else {
 17323          return null;
 17324        }
 17325      }
 17326  
 17327      return {
 17328        name: name,
 17329        value: valArr,
 17330        pfValue: pfValArr,
 17331        strValue: strVal,
 17332        bypass: propIsBypass,
 17333        units: unitsArr
 17334      };
 17335    } // several types also allow enums
 17336  
 17337  
 17338    var checkEnums = function checkEnums() {
 17339      for (var _i = 0; _i < type.enums.length; _i++) {
 17340        var en = type.enums[_i];
 17341  
 17342        if (en === value) {
 17343          return {
 17344            name: name,
 17345            value: value,
 17346            strValue: '' + value,
 17347            bypass: propIsBypass
 17348          };
 17349        }
 17350      }
 17351  
 17352      return null;
 17353    }; // check the type and return the appropriate object
 17354  
 17355  
 17356    if (type.number) {
 17357      var units;
 17358      var implicitUnits = 'px'; // not set => px
 17359  
 17360      if (type.units) {
 17361        // use specified units if set
 17362        units = type.units;
 17363      }
 17364  
 17365      if (type.implicitUnits) {
 17366        implicitUnits = type.implicitUnits;
 17367      }
 17368  
 17369      if (!type.unitless) {
 17370        if (valueIsString) {
 17371          var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
 17372  
 17373          if (units) {
 17374            unitsRegex = units;
 17375          } // only allow explicit units if so set
 17376  
 17377  
 17378          var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
 17379  
 17380          if (match) {
 17381            value = match[1];
 17382            units = match[2] || implicitUnits;
 17383          }
 17384        } else if (!units || type.implicitUnits) {
 17385          units = implicitUnits; // implicitly px if unspecified
 17386        }
 17387      }
 17388  
 17389      value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
 17390  
 17391      if (isNaN(value) && type.enums === undefined) {
 17392        return null;
 17393      } // check if this number type also accepts special keywords in place of numbers
 17394      // (i.e. `left`, `auto`, etc)
 17395  
 17396  
 17397      if (isNaN(value) && type.enums !== undefined) {
 17398        value = passedValue;
 17399        return checkEnums();
 17400      } // check if value must be an integer
 17401  
 17402  
 17403      if (type.integer && !integer(value)) {
 17404        return null;
 17405      } // check value is within range
 17406  
 17407  
 17408      if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
 17409        return null;
 17410      }
 17411  
 17412      var ret = {
 17413        name: name,
 17414        value: value,
 17415        strValue: '' + value + (units ? units : ''),
 17416        units: units,
 17417        bypass: propIsBypass
 17418      }; // normalise value in pixels
 17419  
 17420      if (type.unitless || units !== 'px' && units !== 'em') {
 17421        ret.pfValue = value;
 17422      } else {
 17423        ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
 17424      } // normalise value in ms
 17425  
 17426  
 17427      if (units === 'ms' || units === 's') {
 17428        ret.pfValue = units === 'ms' ? value : 1000 * value;
 17429      } // normalise value in rad
 17430  
 17431  
 17432      if (units === 'deg' || units === 'rad') {
 17433        ret.pfValue = units === 'rad' ? value : deg2rad(value);
 17434      } // normalize value in %
 17435  
 17436  
 17437      if (units === '%') {
 17438        ret.pfValue = value / 100;
 17439      }
 17440  
 17441      return ret;
 17442    } else if (type.propList) {
 17443      var props = [];
 17444      var propsStr = '' + value;
 17445  
 17446      if (propsStr === 'none') ; else {
 17447        // go over each prop
 17448        var propsSplit = propsStr.split(/\s*,\s*|\s+/);
 17449  
 17450        for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
 17451          var propName = propsSplit[_i2].trim();
 17452  
 17453          if (self.properties[propName]) {
 17454            props.push(propName);
 17455          } else {
 17456            warn('`' + propName + '` is not a valid property name');
 17457          }
 17458        }
 17459  
 17460        if (props.length === 0) {
 17461          return null;
 17462        }
 17463      }
 17464  
 17465      return {
 17466        name: name,
 17467        value: props,
 17468        strValue: props.length === 0 ? 'none' : props.join(' '),
 17469        bypass: propIsBypass
 17470      };
 17471    } else if (type.color) {
 17472      var tuple = color2tuple(value);
 17473  
 17474      if (!tuple) {
 17475        return null;
 17476      }
 17477  
 17478      return {
 17479        name: name,
 17480        value: tuple,
 17481        pfValue: tuple,
 17482        strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
 17483        // n.b. no spaces b/c of multiple support
 17484        bypass: propIsBypass
 17485      };
 17486    } else if (type.regex || type.regexes) {
 17487      // first check enums
 17488      if (type.enums) {
 17489        var enumProp = checkEnums();
 17490  
 17491        if (enumProp) {
 17492          return enumProp;
 17493        }
 17494      }
 17495  
 17496      var regexes = type.regexes ? type.regexes : [type.regex];
 17497  
 17498      for (var _i3 = 0; _i3 < regexes.length; _i3++) {
 17499        var regex = new RegExp(regexes[_i3]); // make a regex from the type string
 17500  
 17501        var m = regex.exec(value);
 17502  
 17503        if (m) {
 17504          // regex matches
 17505          return {
 17506            name: name,
 17507            value: type.singleRegexMatchValue ? m[1] : m,
 17508            strValue: '' + value,
 17509            bypass: propIsBypass
 17510          };
 17511        }
 17512      }
 17513  
 17514      return null; // didn't match any
 17515    } else if (type.string) {
 17516      // just return
 17517      return {
 17518        name: name,
 17519        value: '' + value,
 17520        strValue: '' + value,
 17521        bypass: propIsBypass
 17522      };
 17523    } else if (type.enums) {
 17524      // check enums last because it's a combo type in others
 17525      return checkEnums();
 17526    } else {
 17527      return null; // not a type we can handle
 17528    }
 17529  };
 17530  
 17531  var Style = function Style(cy) {
 17532    if (!(this instanceof Style)) {
 17533      return new Style(cy);
 17534    }
 17535  
 17536    if (!core(cy)) {
 17537      error('A style must have a core reference');
 17538      return;
 17539    }
 17540  
 17541    this._private = {
 17542      cy: cy,
 17543      coreStyle: {}
 17544    };
 17545    this.length = 0;
 17546    this.resetToDefault();
 17547  };
 17548  
 17549  var styfn$8 = Style.prototype;
 17550  
 17551  styfn$8.instanceString = function () {
 17552    return 'style';
 17553  }; // remove all contexts
 17554  
 17555  
 17556  styfn$8.clear = function () {
 17557    for (var i = 0; i < this.length; i++) {
 17558      this[i] = undefined;
 17559    }
 17560  
 17561    this.length = 0;
 17562    var _p = this._private;
 17563    _p.newStyle = true;
 17564    return this; // chaining
 17565  };
 17566  
 17567  styfn$8.resetToDefault = function () {
 17568    this.clear();
 17569    this.addDefaultStylesheet();
 17570    return this;
 17571  }; // builds a style object for the 'core' selector
 17572  
 17573  
 17574  styfn$8.core = function (propName) {
 17575    return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
 17576  }; // create a new context from the specified selector string and switch to that context
 17577  
 17578  
 17579  styfn$8.selector = function (selectorStr) {
 17580    // 'core' is a special case and does not need a selector
 17581    var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
 17582    var i = this.length++; // new context means new index
 17583  
 17584    this[i] = {
 17585      selector: selector,
 17586      properties: [],
 17587      mappedProperties: [],
 17588      index: i
 17589    };
 17590    return this; // chaining
 17591  }; // add one or many css rules to the current context
 17592  
 17593  
 17594  styfn$8.css = function () {
 17595    var self = this;
 17596    var args = arguments;
 17597  
 17598    if (args.length === 1) {
 17599      var map = args[0];
 17600  
 17601      for (var i = 0; i < self.properties.length; i++) {
 17602        var prop = self.properties[i];
 17603        var mapVal = map[prop.name];
 17604  
 17605        if (mapVal === undefined) {
 17606          mapVal = map[dash2camel(prop.name)];
 17607        }
 17608  
 17609        if (mapVal !== undefined) {
 17610          this.cssRule(prop.name, mapVal);
 17611        }
 17612      }
 17613    } else if (args.length === 2) {
 17614      this.cssRule(args[0], args[1]);
 17615    } // do nothing if args are invalid
 17616  
 17617  
 17618    return this; // chaining
 17619  };
 17620  
 17621  styfn$8.style = styfn$8.css; // add a single css rule to the current context
 17622  
 17623  styfn$8.cssRule = function (name, value) {
 17624    // name-value pair
 17625    var property = this.parse(name, value); // add property to current context if valid
 17626  
 17627    if (property) {
 17628      var i = this.length - 1;
 17629      this[i].properties.push(property);
 17630      this[i].properties[property.name] = property; // allow access by name as well
 17631  
 17632      if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
 17633        this._private.hasPie = true;
 17634      }
 17635  
 17636      if (property.mapped) {
 17637        this[i].mappedProperties.push(property);
 17638      } // add to core style if necessary
 17639  
 17640  
 17641      var currentSelectorIsCore = !this[i].selector;
 17642  
 17643      if (currentSelectorIsCore) {
 17644        this._private.coreStyle[property.name] = property;
 17645      }
 17646    }
 17647  
 17648    return this; // chaining
 17649  };
 17650  
 17651  styfn$8.append = function (style) {
 17652    if (stylesheet(style)) {
 17653      style.appendToStyle(this);
 17654    } else if (array(style)) {
 17655      this.appendFromJson(style);
 17656    } else if (string(style)) {
 17657      this.appendFromString(style);
 17658    } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
 17659  
 17660  
 17661    return this;
 17662  }; // static function
 17663  
 17664  
 17665  Style.fromJson = function (cy, json) {
 17666    var style = new Style(cy);
 17667    style.fromJson(json);
 17668    return style;
 17669  };
 17670  
 17671  Style.fromString = function (cy, string) {
 17672    return new Style(cy).fromString(string);
 17673  };
 17674  
 17675  [styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
 17676    extend(styfn$8, props);
 17677  });
 17678  Style.types = styfn$8.types;
 17679  Style.properties = styfn$8.properties;
 17680  Style.propertyGroups = styfn$8.propertyGroups;
 17681  Style.propertyGroupNames = styfn$8.propertyGroupNames;
 17682  Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
 17683  
 17684  var corefn$7 = {
 17685    style: function style(newStyle) {
 17686      if (newStyle) {
 17687        var s = this.setStyle(newStyle);
 17688        s.update();
 17689      }
 17690  
 17691      return this._private.style;
 17692    },
 17693    setStyle: function setStyle(style) {
 17694      var _p = this._private;
 17695  
 17696      if (stylesheet(style)) {
 17697        _p.style = style.generateStyle(this);
 17698      } else if (array(style)) {
 17699        _p.style = Style.fromJson(this, style);
 17700      } else if (string(style)) {
 17701        _p.style = Style.fromString(this, style);
 17702      } else {
 17703        _p.style = Style(this);
 17704      }
 17705  
 17706      return _p.style;
 17707    }
 17708  };
 17709  
 17710  var defaultSelectionType = 'single';
 17711  var corefn$8 = {
 17712    autolock: function autolock(bool) {
 17713      if (bool !== undefined) {
 17714        this._private.autolock = bool ? true : false;
 17715      } else {
 17716        return this._private.autolock;
 17717      }
 17718  
 17719      return this; // chaining
 17720    },
 17721    autoungrabify: function autoungrabify(bool) {
 17722      if (bool !== undefined) {
 17723        this._private.autoungrabify = bool ? true : false;
 17724      } else {
 17725        return this._private.autoungrabify;
 17726      }
 17727  
 17728      return this; // chaining
 17729    },
 17730    autounselectify: function autounselectify(bool) {
 17731      if (bool !== undefined) {
 17732        this._private.autounselectify = bool ? true : false;
 17733      } else {
 17734        return this._private.autounselectify;
 17735      }
 17736  
 17737      return this; // chaining
 17738    },
 17739    selectionType: function selectionType(selType) {
 17740      var _p = this._private;
 17741  
 17742      if (_p.selectionType == null) {
 17743        _p.selectionType = defaultSelectionType;
 17744      }
 17745  
 17746      if (selType !== undefined) {
 17747        if (selType === 'additive' || selType === 'single') {
 17748          _p.selectionType = selType;
 17749        }
 17750      } else {
 17751        return _p.selectionType;
 17752      }
 17753  
 17754      return this;
 17755    },
 17756    panningEnabled: function panningEnabled(bool) {
 17757      if (bool !== undefined) {
 17758        this._private.panningEnabled = bool ? true : false;
 17759      } else {
 17760        return this._private.panningEnabled;
 17761      }
 17762  
 17763      return this; // chaining
 17764    },
 17765    userPanningEnabled: function userPanningEnabled(bool) {
 17766      if (bool !== undefined) {
 17767        this._private.userPanningEnabled = bool ? true : false;
 17768      } else {
 17769        return this._private.userPanningEnabled;
 17770      }
 17771  
 17772      return this; // chaining
 17773    },
 17774    zoomingEnabled: function zoomingEnabled(bool) {
 17775      if (bool !== undefined) {
 17776        this._private.zoomingEnabled = bool ? true : false;
 17777      } else {
 17778        return this._private.zoomingEnabled;
 17779      }
 17780  
 17781      return this; // chaining
 17782    },
 17783    userZoomingEnabled: function userZoomingEnabled(bool) {
 17784      if (bool !== undefined) {
 17785        this._private.userZoomingEnabled = bool ? true : false;
 17786      } else {
 17787        return this._private.userZoomingEnabled;
 17788      }
 17789  
 17790      return this; // chaining
 17791    },
 17792    boxSelectionEnabled: function boxSelectionEnabled(bool) {
 17793      if (bool !== undefined) {
 17794        this._private.boxSelectionEnabled = bool ? true : false;
 17795      } else {
 17796        return this._private.boxSelectionEnabled;
 17797      }
 17798  
 17799      return this; // chaining
 17800    },
 17801    pan: function pan() {
 17802      var args = arguments;
 17803      var pan = this._private.pan;
 17804      var dim, val, dims, x, y;
 17805  
 17806      switch (args.length) {
 17807        case 0:
 17808          // .pan()
 17809          return pan;
 17810  
 17811        case 1:
 17812          if (string(args[0])) {
 17813            // .pan('x')
 17814            dim = args[0];
 17815            return pan[dim];
 17816          } else if (plainObject(args[0])) {
 17817            // .pan({ x: 0, y: 100 })
 17818            if (!this._private.panningEnabled) {
 17819              return this;
 17820            }
 17821  
 17822            dims = args[0];
 17823            x = dims.x;
 17824            y = dims.y;
 17825  
 17826            if (number(x)) {
 17827              pan.x = x;
 17828            }
 17829  
 17830            if (number(y)) {
 17831              pan.y = y;
 17832            }
 17833  
 17834            this.emit('pan viewport');
 17835          }
 17836  
 17837          break;
 17838  
 17839        case 2:
 17840          // .pan('x', 100)
 17841          if (!this._private.panningEnabled) {
 17842            return this;
 17843          }
 17844  
 17845          dim = args[0];
 17846          val = args[1];
 17847  
 17848          if ((dim === 'x' || dim === 'y') && number(val)) {
 17849            pan[dim] = val;
 17850          }
 17851  
 17852          this.emit('pan viewport');
 17853          break;
 17854        // invalid
 17855      }
 17856  
 17857      this.notify('viewport');
 17858      return this; // chaining
 17859    },
 17860    panBy: function panBy(arg0, arg1) {
 17861      var args = arguments;
 17862      var pan = this._private.pan;
 17863      var dim, val, dims, x, y;
 17864  
 17865      if (!this._private.panningEnabled) {
 17866        return this;
 17867      }
 17868  
 17869      switch (args.length) {
 17870        case 1:
 17871          if (plainObject(arg0)) {
 17872            // .panBy({ x: 0, y: 100 })
 17873            dims = args[0];
 17874            x = dims.x;
 17875            y = dims.y;
 17876  
 17877            if (number(x)) {
 17878              pan.x += x;
 17879            }
 17880  
 17881            if (number(y)) {
 17882              pan.y += y;
 17883            }
 17884  
 17885            this.emit('pan viewport');
 17886          }
 17887  
 17888          break;
 17889  
 17890        case 2:
 17891          // .panBy('x', 100)
 17892          dim = arg0;
 17893          val = arg1;
 17894  
 17895          if ((dim === 'x' || dim === 'y') && number(val)) {
 17896            pan[dim] += val;
 17897          }
 17898  
 17899          this.emit('pan viewport');
 17900          break;
 17901        // invalid
 17902      }
 17903  
 17904      this.notify('viewport');
 17905      return this; // chaining
 17906    },
 17907    fit: function fit(elements, padding) {
 17908      var viewportState = this.getFitViewport(elements, padding);
 17909  
 17910      if (viewportState) {
 17911        var _p = this._private;
 17912        _p.zoom = viewportState.zoom;
 17913        _p.pan = viewportState.pan;
 17914        this.emit('pan zoom viewport');
 17915        this.notify('viewport');
 17916      }
 17917  
 17918      return this; // chaining
 17919    },
 17920    getFitViewport: function getFitViewport(elements, padding) {
 17921      if (number(elements) && padding === undefined) {
 17922        // elements is optional
 17923        padding = elements;
 17924        elements = undefined;
 17925      }
 17926  
 17927      if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
 17928        return;
 17929      }
 17930  
 17931      var bb;
 17932  
 17933      if (string(elements)) {
 17934        var sel = elements;
 17935        elements = this.$(sel);
 17936      } else if (boundingBox(elements)) {
 17937        // assume bb
 17938        var bbe = elements;
 17939        bb = {
 17940          x1: bbe.x1,
 17941          y1: bbe.y1,
 17942          x2: bbe.x2,
 17943          y2: bbe.y2
 17944        };
 17945        bb.w = bb.x2 - bb.x1;
 17946        bb.h = bb.y2 - bb.y1;
 17947      } else if (!elementOrCollection(elements)) {
 17948        elements = this.mutableElements();
 17949      }
 17950  
 17951      if (elementOrCollection(elements) && elements.empty()) {
 17952        return;
 17953      } // can't fit to nothing
 17954  
 17955  
 17956      bb = bb || elements.boundingBox();
 17957      var w = this.width();
 17958      var h = this.height();
 17959      var zoom;
 17960      padding = number(padding) ? padding : 0;
 17961  
 17962      if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
 17963        zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
 17964  
 17965        zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
 17966        zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
 17967        var pan = {
 17968          // now pan to middle
 17969          x: (w - zoom * (bb.x1 + bb.x2)) / 2,
 17970          y: (h - zoom * (bb.y1 + bb.y2)) / 2
 17971        };
 17972        return {
 17973          zoom: zoom,
 17974          pan: pan
 17975        };
 17976      }
 17977  
 17978      return;
 17979    },
 17980    zoomRange: function zoomRange(min, max) {
 17981      var _p = this._private;
 17982  
 17983      if (max == null) {
 17984        var opts = min;
 17985        min = opts.min;
 17986        max = opts.max;
 17987      }
 17988  
 17989      if (number(min) && number(max) && min <= max) {
 17990        _p.minZoom = min;
 17991        _p.maxZoom = max;
 17992      } else if (number(min) && max === undefined && min <= _p.maxZoom) {
 17993        _p.minZoom = min;
 17994      } else if (number(max) && min === undefined && max >= _p.minZoom) {
 17995        _p.maxZoom = max;
 17996      }
 17997  
 17998      return this;
 17999    },
 18000    minZoom: function minZoom(zoom) {
 18001      if (zoom === undefined) {
 18002        return this._private.minZoom;
 18003      } else {
 18004        return this.zoomRange({
 18005          min: zoom
 18006        });
 18007      }
 18008    },
 18009    maxZoom: function maxZoom(zoom) {
 18010      if (zoom === undefined) {
 18011        return this._private.maxZoom;
 18012      } else {
 18013        return this.zoomRange({
 18014          max: zoom
 18015        });
 18016      }
 18017    },
 18018    getZoomedViewport: function getZoomedViewport(params) {
 18019      var _p = this._private;
 18020      var currentPan = _p.pan;
 18021      var currentZoom = _p.zoom;
 18022      var pos; // in rendered px
 18023  
 18024      var zoom;
 18025      var bail = false;
 18026  
 18027      if (!_p.zoomingEnabled) {
 18028        // zooming disabled
 18029        bail = true;
 18030      }
 18031  
 18032      if (number(params)) {
 18033        // then set the zoom
 18034        zoom = params;
 18035      } else if (plainObject(params)) {
 18036        // then zoom about a point
 18037        zoom = params.level;
 18038  
 18039        if (params.position != null) {
 18040          pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
 18041        } else if (params.renderedPosition != null) {
 18042          pos = params.renderedPosition;
 18043        }
 18044  
 18045        if (pos != null && !_p.panningEnabled) {
 18046          // panning disabled
 18047          bail = true;
 18048        }
 18049      } // crop zoom
 18050  
 18051  
 18052      zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
 18053      zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
 18054  
 18055      if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
 18056        return null;
 18057      }
 18058  
 18059      if (pos != null) {
 18060        // set zoom about position
 18061        var pan1 = currentPan;
 18062        var zoom1 = currentZoom;
 18063        var zoom2 = zoom;
 18064        var pan2 = {
 18065          x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
 18066          y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
 18067        };
 18068        return {
 18069          zoomed: true,
 18070          panned: true,
 18071          zoom: zoom2,
 18072          pan: pan2
 18073        };
 18074      } else {
 18075        // just set the zoom
 18076        return {
 18077          zoomed: true,
 18078          panned: false,
 18079          zoom: zoom,
 18080          pan: currentPan
 18081        };
 18082      }
 18083    },
 18084    zoom: function zoom(params) {
 18085      if (params === undefined) {
 18086        // get
 18087        return this._private.zoom;
 18088      } else {
 18089        // set
 18090        var vp = this.getZoomedViewport(params);
 18091        var _p = this._private;
 18092  
 18093        if (vp == null || !vp.zoomed) {
 18094          return this;
 18095        }
 18096  
 18097        _p.zoom = vp.zoom;
 18098  
 18099        if (vp.panned) {
 18100          _p.pan.x = vp.pan.x;
 18101          _p.pan.y = vp.pan.y;
 18102        }
 18103  
 18104        this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
 18105        this.notify('viewport');
 18106        return this; // chaining
 18107      }
 18108    },
 18109    viewport: function viewport(opts) {
 18110      var _p = this._private;
 18111      var zoomDefd = true;
 18112      var panDefd = true;
 18113      var events = []; // to trigger
 18114  
 18115      var zoomFailed = false;
 18116      var panFailed = false;
 18117  
 18118      if (!opts) {
 18119        return this;
 18120      }
 18121  
 18122      if (!number(opts.zoom)) {
 18123        zoomDefd = false;
 18124      }
 18125  
 18126      if (!plainObject(opts.pan)) {
 18127        panDefd = false;
 18128      }
 18129  
 18130      if (!zoomDefd && !panDefd) {
 18131        return this;
 18132      }
 18133  
 18134      if (zoomDefd) {
 18135        var z = opts.zoom;
 18136  
 18137        if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
 18138          zoomFailed = true;
 18139        } else {
 18140          _p.zoom = z;
 18141          events.push('zoom');
 18142        }
 18143      }
 18144  
 18145      if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
 18146        var p = opts.pan;
 18147  
 18148        if (number(p.x)) {
 18149          _p.pan.x = p.x;
 18150          panFailed = false;
 18151        }
 18152  
 18153        if (number(p.y)) {
 18154          _p.pan.y = p.y;
 18155          panFailed = false;
 18156        }
 18157  
 18158        if (!panFailed) {
 18159          events.push('pan');
 18160        }
 18161      }
 18162  
 18163      if (events.length > 0) {
 18164        events.push('viewport');
 18165        this.emit(events.join(' '));
 18166        this.notify('viewport');
 18167      }
 18168  
 18169      return this; // chaining
 18170    },
 18171    center: function center(elements) {
 18172      var pan = this.getCenterPan(elements);
 18173  
 18174      if (pan) {
 18175        this._private.pan = pan;
 18176        this.emit('pan viewport');
 18177        this.notify('viewport');
 18178      }
 18179  
 18180      return this; // chaining
 18181    },
 18182    getCenterPan: function getCenterPan(elements, zoom) {
 18183      if (!this._private.panningEnabled) {
 18184        return;
 18185      }
 18186  
 18187      if (string(elements)) {
 18188        var selector = elements;
 18189        elements = this.mutableElements().filter(selector);
 18190      } else if (!elementOrCollection(elements)) {
 18191        elements = this.mutableElements();
 18192      }
 18193  
 18194      if (elements.length === 0) {
 18195        return;
 18196      } // can't centre pan to nothing
 18197  
 18198  
 18199      var bb = elements.boundingBox();
 18200      var w = this.width();
 18201      var h = this.height();
 18202      zoom = zoom === undefined ? this._private.zoom : zoom;
 18203      var pan = {
 18204        // middle
 18205        x: (w - zoom * (bb.x1 + bb.x2)) / 2,
 18206        y: (h - zoom * (bb.y1 + bb.y2)) / 2
 18207      };
 18208      return pan;
 18209    },
 18210    reset: function reset() {
 18211      if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
 18212        return this;
 18213      }
 18214  
 18215      this.viewport({
 18216        pan: {
 18217          x: 0,
 18218          y: 0
 18219        },
 18220        zoom: 1
 18221      });
 18222      return this; // chaining
 18223    },
 18224    invalidateSize: function invalidateSize() {
 18225      this._private.sizeCache = null;
 18226    },
 18227    size: function size() {
 18228      var _p = this._private;
 18229      var container = _p.container;
 18230      return _p.sizeCache = _p.sizeCache || (container ? function () {
 18231        var style = window$1.getComputedStyle(container);
 18232  
 18233        var val = function val(name) {
 18234          return parseFloat(style.getPropertyValue(name));
 18235        };
 18236  
 18237        return {
 18238          width: container.clientWidth - val('padding-left') - val('padding-right'),
 18239          height: container.clientHeight - val('padding-top') - val('padding-bottom')
 18240        };
 18241      }() : {
 18242        // fallback if no container (not 0 b/c can be used for dividing etc)
 18243        width: 1,
 18244        height: 1
 18245      });
 18246    },
 18247    width: function width() {
 18248      return this.size().width;
 18249    },
 18250    height: function height() {
 18251      return this.size().height;
 18252    },
 18253    extent: function extent() {
 18254      var pan = this._private.pan;
 18255      var zoom = this._private.zoom;
 18256      var rb = this.renderedExtent();
 18257      var b = {
 18258        x1: (rb.x1 - pan.x) / zoom,
 18259        x2: (rb.x2 - pan.x) / zoom,
 18260        y1: (rb.y1 - pan.y) / zoom,
 18261        y2: (rb.y2 - pan.y) / zoom
 18262      };
 18263      b.w = b.x2 - b.x1;
 18264      b.h = b.y2 - b.y1;
 18265      return b;
 18266    },
 18267    renderedExtent: function renderedExtent() {
 18268      var width = this.width();
 18269      var height = this.height();
 18270      return {
 18271        x1: 0,
 18272        y1: 0,
 18273        x2: width,
 18274        y2: height,
 18275        w: width,
 18276        h: height
 18277      };
 18278    }
 18279  }; // aliases
 18280  
 18281  corefn$8.centre = corefn$8.center; // backwards compatibility
 18282  
 18283  corefn$8.autolockNodes = corefn$8.autolock;
 18284  corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
 18285  
 18286  var fn$6 = {
 18287    data: define$3.data({
 18288      field: 'data',
 18289      bindingEvent: 'data',
 18290      allowBinding: true,
 18291      allowSetting: true,
 18292      settingEvent: 'data',
 18293      settingTriggersEvent: true,
 18294      triggerFnName: 'trigger',
 18295      allowGetting: true
 18296    }),
 18297    removeData: define$3.removeData({
 18298      field: 'data',
 18299      event: 'data',
 18300      triggerFnName: 'trigger',
 18301      triggerEvent: true
 18302    }),
 18303    scratch: define$3.data({
 18304      field: 'scratch',
 18305      bindingEvent: 'scratch',
 18306      allowBinding: true,
 18307      allowSetting: true,
 18308      settingEvent: 'scratch',
 18309      settingTriggersEvent: true,
 18310      triggerFnName: 'trigger',
 18311      allowGetting: true
 18312    }),
 18313    removeScratch: define$3.removeData({
 18314      field: 'scratch',
 18315      event: 'scratch',
 18316      triggerFnName: 'trigger',
 18317      triggerEvent: true
 18318    })
 18319  }; // aliases
 18320  
 18321  fn$6.attr = fn$6.data;
 18322  fn$6.removeAttr = fn$6.removeData;
 18323  
 18324  var Core = function Core(opts) {
 18325    var cy = this;
 18326    opts = extend({}, opts);
 18327    var container = opts.container; // allow for passing a wrapped jquery object
 18328    // e.g. cytoscape({ container: $('#cy') })
 18329  
 18330    if (container && !htmlElement(container) && htmlElement(container[0])) {
 18331      container = container[0];
 18332    }
 18333  
 18334    var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
 18335  
 18336    reg = reg || {};
 18337  
 18338    if (reg && reg.cy) {
 18339      reg.cy.destroy();
 18340      reg = {}; // old instance => replace reg completely
 18341    }
 18342  
 18343    var readies = reg.readies = reg.readies || [];
 18344  
 18345    if (container) {
 18346      container._cyreg = reg;
 18347    } // make sure container assoc'd reg points to this cy
 18348  
 18349  
 18350    reg.cy = cy;
 18351    var head = window$1 !== undefined && container !== undefined && !opts.headless;
 18352    var options = opts;
 18353    options.layout = extend({
 18354      name: head ? 'grid' : 'null'
 18355    }, options.layout);
 18356    options.renderer = extend({
 18357      name: head ? 'canvas' : 'null'
 18358    }, options.renderer);
 18359  
 18360    var defVal = function defVal(def, val, altVal) {
 18361      if (val !== undefined) {
 18362        return val;
 18363      } else if (altVal !== undefined) {
 18364        return altVal;
 18365      } else {
 18366        return def;
 18367      }
 18368    };
 18369  
 18370    var _p = this._private = {
 18371      container: container,
 18372      // html dom ele container
 18373      ready: false,
 18374      // whether ready has been triggered
 18375      options: options,
 18376      // cached options
 18377      elements: new Collection(this),
 18378      // elements in the graph
 18379      listeners: [],
 18380      // list of listeners
 18381      aniEles: new Collection(this),
 18382      // elements being animated
 18383      data: {},
 18384      // data for the core
 18385      scratch: {},
 18386      // scratch object for core
 18387      layout: null,
 18388      renderer: null,
 18389      destroyed: false,
 18390      // whether destroy was called
 18391      notificationsEnabled: true,
 18392      // whether notifications are sent to the renderer
 18393      minZoom: 1e-50,
 18394      maxZoom: 1e50,
 18395      zoomingEnabled: defVal(true, options.zoomingEnabled),
 18396      userZoomingEnabled: defVal(true, options.userZoomingEnabled),
 18397      panningEnabled: defVal(true, options.panningEnabled),
 18398      userPanningEnabled: defVal(true, options.userPanningEnabled),
 18399      boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
 18400      autolock: defVal(false, options.autolock, options.autolockNodes),
 18401      autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
 18402      autounselectify: defVal(false, options.autounselectify),
 18403      styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
 18404      zoom: number(options.zoom) ? options.zoom : 1,
 18405      pan: {
 18406        x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
 18407        y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
 18408      },
 18409      animation: {
 18410        // object for currently-running animations
 18411        current: [],
 18412        queue: []
 18413      },
 18414      hasCompoundNodes: false
 18415    };
 18416  
 18417    this.createEmitter(); // set selection type
 18418  
 18419    this.selectionType(options.selectionType); // init zoom bounds
 18420  
 18421    this.zoomRange({
 18422      min: options.minZoom,
 18423      max: options.maxZoom
 18424    });
 18425  
 18426    var loadExtData = function loadExtData(extData, next) {
 18427      var anyIsPromise = extData.some(promise);
 18428  
 18429      if (anyIsPromise) {
 18430        return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
 18431      } else {
 18432        next(extData); // exec synchronously for convenience
 18433      }
 18434    }; // start with the default stylesheet so we have something before loading an external stylesheet
 18435  
 18436  
 18437    if (_p.styleEnabled) {
 18438      cy.setStyle([]);
 18439    } // create the renderer
 18440  
 18441  
 18442    var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
 18443  
 18444    cy.initRenderer(rendererOptions);
 18445  
 18446    var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
 18447      cy.notifications(false); // remove old elements
 18448  
 18449      var oldEles = cy.mutableElements();
 18450  
 18451      if (oldEles.length > 0) {
 18452        oldEles.remove();
 18453      }
 18454  
 18455      if (elements != null) {
 18456        if (plainObject(elements) || array(elements)) {
 18457          cy.add(elements);
 18458        }
 18459      }
 18460  
 18461      cy.one('layoutready', function (e) {
 18462        cy.notifications(true);
 18463        cy.emit(e); // we missed this event by turning notifications off, so pass it on
 18464  
 18465        cy.one('load', onload);
 18466        cy.emitAndNotify('load');
 18467      }).one('layoutstop', function () {
 18468        cy.one('done', ondone);
 18469        cy.emit('done');
 18470      });
 18471      var layoutOpts = extend({}, cy._private.options.layout);
 18472      layoutOpts.eles = cy.elements();
 18473      cy.layout(layoutOpts).run();
 18474    };
 18475  
 18476    loadExtData([options.style, options.elements], function (thens) {
 18477      var initStyle = thens[0];
 18478      var initEles = thens[1]; // init style
 18479  
 18480      if (_p.styleEnabled) {
 18481        cy.style().append(initStyle);
 18482      } // initial load
 18483  
 18484  
 18485      setElesAndLayout(initEles, function () {
 18486        // onready
 18487        cy.startAnimationLoop();
 18488        _p.ready = true; // if a ready callback is specified as an option, the bind it
 18489  
 18490        if (fn(options.ready)) {
 18491          cy.on('ready', options.ready);
 18492        } // bind all the ready handlers registered before creating this instance
 18493  
 18494  
 18495        for (var i = 0; i < readies.length; i++) {
 18496          var fn$1 = readies[i];
 18497          cy.on('ready', fn$1);
 18498        }
 18499  
 18500        if (reg) {
 18501          reg.readies = [];
 18502        } // 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
 18503  
 18504  
 18505        cy.emit('ready');
 18506      }, options.done);
 18507    });
 18508  };
 18509  
 18510  var corefn$9 = Core.prototype; // short alias
 18511  
 18512  extend(corefn$9, {
 18513    instanceString: function instanceString() {
 18514      return 'core';
 18515    },
 18516    isReady: function isReady() {
 18517      return this._private.ready;
 18518    },
 18519    destroyed: function destroyed() {
 18520      return this._private.destroyed;
 18521    },
 18522    ready: function ready(fn) {
 18523      if (this.isReady()) {
 18524        this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
 18525      } else {
 18526        this.on('ready', fn);
 18527      }
 18528  
 18529      return this;
 18530    },
 18531    destroy: function destroy() {
 18532      var cy = this;
 18533      if (cy.destroyed()) return;
 18534      cy.stopAnimationLoop();
 18535      cy.destroyRenderer();
 18536      this.emit('destroy');
 18537      cy._private.destroyed = true;
 18538      return cy;
 18539    },
 18540    hasElementWithId: function hasElementWithId(id) {
 18541      return this._private.elements.hasElementWithId(id);
 18542    },
 18543    getElementById: function getElementById(id) {
 18544      return this._private.elements.getElementById(id);
 18545    },
 18546    hasCompoundNodes: function hasCompoundNodes() {
 18547      return this._private.hasCompoundNodes;
 18548    },
 18549    headless: function headless() {
 18550      return this._private.renderer.isHeadless();
 18551    },
 18552    styleEnabled: function styleEnabled() {
 18553      return this._private.styleEnabled;
 18554    },
 18555    addToPool: function addToPool(eles) {
 18556      this._private.elements.merge(eles);
 18557  
 18558      return this; // chaining
 18559    },
 18560    removeFromPool: function removeFromPool(eles) {
 18561      this._private.elements.unmerge(eles);
 18562  
 18563      return this;
 18564    },
 18565    container: function container() {
 18566      return this._private.container || null;
 18567    },
 18568    mount: function mount(container) {
 18569      if (container == null) {
 18570        return;
 18571      }
 18572  
 18573      var cy = this;
 18574      var _p = cy._private;
 18575      var options = _p.options;
 18576  
 18577      if (!htmlElement(container) && htmlElement(container[0])) {
 18578        container = container[0];
 18579      }
 18580  
 18581      cy.stopAnimationLoop();
 18582      cy.destroyRenderer();
 18583      _p.container = container;
 18584      _p.styleEnabled = true;
 18585      cy.invalidateSize();
 18586      cy.initRenderer(extend({}, options, options.renderer, {
 18587        // allow custom renderer name to be re-used, otherwise use canvas
 18588        name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
 18589      }));
 18590      cy.startAnimationLoop();
 18591      cy.style(options.style);
 18592      cy.emit('mount');
 18593      return cy;
 18594    },
 18595    unmount: function unmount() {
 18596      var cy = this;
 18597      cy.stopAnimationLoop();
 18598      cy.destroyRenderer();
 18599      cy.initRenderer({
 18600        name: 'null'
 18601      });
 18602      cy.emit('unmount');
 18603      return cy;
 18604    },
 18605    options: function options() {
 18606      return copy(this._private.options);
 18607    },
 18608    json: function json(obj) {
 18609      var cy = this;
 18610      var _p = cy._private;
 18611      var eles = cy.mutableElements();
 18612  
 18613      var getFreshRef = function getFreshRef(ele) {
 18614        return cy.getElementById(ele.id());
 18615      };
 18616  
 18617      if (plainObject(obj)) {
 18618        // set
 18619        cy.startBatch();
 18620  
 18621        if (obj.elements) {
 18622          var idInJson = {};
 18623  
 18624          var updateEles = function updateEles(jsons, gr) {
 18625            var toAdd = [];
 18626            var toMod = [];
 18627  
 18628            for (var i = 0; i < jsons.length; i++) {
 18629              var json = jsons[i];
 18630              var id = '' + json.data.id; // id must be string
 18631  
 18632              var ele = cy.getElementById(id);
 18633              idInJson[id] = true;
 18634  
 18635              if (ele.length !== 0) {
 18636                // existing element should be updated
 18637                toMod.push({
 18638                  ele: ele,
 18639                  json: json
 18640                });
 18641              } else {
 18642                // otherwise should be added
 18643                if (gr) {
 18644                  json.group = gr;
 18645                  toAdd.push(json);
 18646                } else {
 18647                  toAdd.push(json);
 18648                }
 18649              }
 18650            }
 18651  
 18652            cy.add(toAdd);
 18653  
 18654            for (var _i = 0; _i < toMod.length; _i++) {
 18655              var _toMod$_i = toMod[_i],
 18656                  _ele = _toMod$_i.ele,
 18657                  _json = _toMod$_i.json;
 18658  
 18659              _ele.json(_json);
 18660            }
 18661          };
 18662  
 18663          if (array(obj.elements)) {
 18664            // elements: []
 18665            updateEles(obj.elements);
 18666          } else {
 18667            // elements: { nodes: [], edges: [] }
 18668            var grs = ['nodes', 'edges'];
 18669  
 18670            for (var i = 0; i < grs.length; i++) {
 18671              var gr = grs[i];
 18672              var elements = obj.elements[gr];
 18673  
 18674              if (array(elements)) {
 18675                updateEles(elements, gr);
 18676              }
 18677            }
 18678          }
 18679  
 18680          var parentsToRemove = cy.collection();
 18681          eles.filter(function (ele) {
 18682            return !idInJson[ele.id()];
 18683          }).forEach(function (ele) {
 18684            if (ele.isParent()) {
 18685              parentsToRemove.merge(ele);
 18686            } else {
 18687              ele.remove();
 18688            }
 18689          }); // so that children are not removed w/parent
 18690  
 18691          parentsToRemove.forEach(function (ele) {
 18692            return ele.children().move({
 18693              parent: null
 18694            });
 18695          }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
 18696  
 18697          parentsToRemove.forEach(function (ele) {
 18698            return getFreshRef(ele).remove();
 18699          });
 18700        }
 18701  
 18702        if (obj.style) {
 18703          cy.style(obj.style);
 18704        }
 18705  
 18706        if (obj.zoom != null && obj.zoom !== _p.zoom) {
 18707          cy.zoom(obj.zoom);
 18708        }
 18709  
 18710        if (obj.pan) {
 18711          if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
 18712            cy.pan(obj.pan);
 18713          }
 18714        }
 18715  
 18716        if (obj.data) {
 18717          cy.data(obj.data);
 18718        }
 18719  
 18720        var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify'];
 18721  
 18722        for (var _i2 = 0; _i2 < fields.length; _i2++) {
 18723          var f = fields[_i2];
 18724  
 18725          if (obj[f] != null) {
 18726            cy[f](obj[f]);
 18727          }
 18728        }
 18729  
 18730        cy.endBatch();
 18731        return this; // chaining
 18732      } else {
 18733        // get
 18734        var flat = !!obj;
 18735        var json = {};
 18736  
 18737        if (flat) {
 18738          json.elements = this.elements().map(function (ele) {
 18739            return ele.json();
 18740          });
 18741        } else {
 18742          json.elements = {};
 18743          eles.forEach(function (ele) {
 18744            var group = ele.group();
 18745  
 18746            if (!json.elements[group]) {
 18747              json.elements[group] = [];
 18748            }
 18749  
 18750            json.elements[group].push(ele.json());
 18751          });
 18752        }
 18753  
 18754        if (this._private.styleEnabled) {
 18755          json.style = cy.style().json();
 18756        }
 18757  
 18758        json.data = copy(cy.data());
 18759        var options = _p.options;
 18760        json.zoomingEnabled = _p.zoomingEnabled;
 18761        json.userZoomingEnabled = _p.userZoomingEnabled;
 18762        json.zoom = _p.zoom;
 18763        json.minZoom = _p.minZoom;
 18764        json.maxZoom = _p.maxZoom;
 18765        json.panningEnabled = _p.panningEnabled;
 18766        json.userPanningEnabled = _p.userPanningEnabled;
 18767        json.pan = copy(_p.pan);
 18768        json.boxSelectionEnabled = _p.boxSelectionEnabled;
 18769        json.renderer = copy(options.renderer);
 18770        json.hideEdgesOnViewport = options.hideEdgesOnViewport;
 18771        json.textureOnViewport = options.textureOnViewport;
 18772        json.wheelSensitivity = options.wheelSensitivity;
 18773        json.motionBlur = options.motionBlur;
 18774        return json;
 18775      }
 18776    }
 18777  });
 18778  corefn$9.$id = corefn$9.getElementById;
 18779  [corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
 18780    extend(corefn$9, props);
 18781  });
 18782  
 18783  /* eslint-disable no-unused-vars */
 18784  
 18785  var defaults$9 = {
 18786    fit: true,
 18787    // whether to fit the viewport to the graph
 18788    directed: false,
 18789    // whether the tree is directed downwards (or edges can point in any direction if false)
 18790    padding: 30,
 18791    // padding on fit
 18792    circle: false,
 18793    // put depths in concentric circles if true, put depths top down if false
 18794    grid: false,
 18795    // whether to create an even grid into which the DAG is placed (circle:false only)
 18796    spacingFactor: 1.75,
 18797    // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
 18798    boundingBox: undefined,
 18799    // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 18800    avoidOverlap: true,
 18801    // prevents node overlap, may overflow boundingBox if not enough space
 18802    nodeDimensionsIncludeLabels: false,
 18803    // Excludes the label when calculating node bounding boxes for the layout algorithm
 18804    roots: undefined,
 18805    // the roots of the trees
 18806    maximal: false,
 18807    // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
 18808    animate: false,
 18809    // whether to transition the node positions
 18810    animationDuration: 500,
 18811    // duration of animation in ms if enabled
 18812    animationEasing: undefined,
 18813    // easing of animation if enabled,
 18814    animateFilter: function animateFilter(node, i) {
 18815      return true;
 18816    },
 18817    // 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
 18818    ready: undefined,
 18819    // callback on layoutready
 18820    stop: undefined,
 18821    // callback on layoutstop
 18822    transform: function transform(node, position) {
 18823      return position;
 18824    } // transform a given node position. Useful for changing flow direction in discrete layouts
 18825  
 18826  };
 18827  /* eslint-enable */
 18828  
 18829  var getInfo = function getInfo(ele) {
 18830    return ele.scratch('breadthfirst');
 18831  };
 18832  
 18833  var setInfo = function setInfo(ele, obj) {
 18834    return ele.scratch('breadthfirst', obj);
 18835  };
 18836  
 18837  function BreadthFirstLayout(options) {
 18838    this.options = extend({}, defaults$9, options);
 18839  }
 18840  
 18841  BreadthFirstLayout.prototype.run = function () {
 18842    var params = this.options;
 18843    var options = params;
 18844    var cy = params.cy;
 18845    var eles = options.eles;
 18846    var nodes = eles.nodes().filter(function (n) {
 18847      return !n.isParent();
 18848    });
 18849    var graph = eles;
 18850    var directed = options.directed;
 18851    var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
 18852  
 18853    var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
 18854      x1: 0,
 18855      y1: 0,
 18856      w: cy.width(),
 18857      h: cy.height()
 18858    });
 18859    var roots;
 18860  
 18861    if (elementOrCollection(options.roots)) {
 18862      roots = options.roots;
 18863    } else if (array(options.roots)) {
 18864      var rootsArray = [];
 18865  
 18866      for (var i = 0; i < options.roots.length; i++) {
 18867        var id = options.roots[i];
 18868        var ele = cy.getElementById(id);
 18869        rootsArray.push(ele);
 18870      }
 18871  
 18872      roots = cy.collection(rootsArray);
 18873    } else if (string(options.roots)) {
 18874      roots = cy.$(options.roots);
 18875    } else {
 18876      if (directed) {
 18877        roots = nodes.roots();
 18878      } else {
 18879        var components = eles.components();
 18880        roots = cy.collection();
 18881  
 18882        var _loop = function _loop(_i) {
 18883          var comp = components[_i];
 18884          var maxDegree = comp.maxDegree(false);
 18885          var compRoots = comp.filter(function (ele) {
 18886            return ele.degree(false) === maxDegree;
 18887          });
 18888          roots = roots.add(compRoots);
 18889        };
 18890  
 18891        for (var _i = 0; _i < components.length; _i++) {
 18892          _loop(_i);
 18893        }
 18894      }
 18895    }
 18896  
 18897    var depths = [];
 18898    var foundByBfs = {};
 18899  
 18900    var addToDepth = function addToDepth(ele, d) {
 18901      if (depths[d] == null) {
 18902        depths[d] = [];
 18903      }
 18904  
 18905      var i = depths[d].length;
 18906      depths[d].push(ele);
 18907      setInfo(ele, {
 18908        index: i,
 18909        depth: d
 18910      });
 18911    };
 18912  
 18913    var changeDepth = function changeDepth(ele, newDepth) {
 18914      var _getInfo = getInfo(ele),
 18915          depth = _getInfo.depth,
 18916          index = _getInfo.index;
 18917  
 18918      depths[depth][index] = null;
 18919      addToDepth(ele, newDepth);
 18920    }; // find the depths of the nodes
 18921  
 18922  
 18923    graph.bfs({
 18924      roots: roots,
 18925      directed: options.directed,
 18926      visit: function visit(node, edge, pNode, i, depth) {
 18927        var ele = node[0];
 18928        var id = ele.id();
 18929        addToDepth(ele, depth);
 18930        foundByBfs[id] = true;
 18931      }
 18932    }); // check for nodes not found by bfs
 18933  
 18934    var orphanNodes = [];
 18935  
 18936    for (var _i2 = 0; _i2 < nodes.length; _i2++) {
 18937      var _ele = nodes[_i2];
 18938  
 18939      if (foundByBfs[_ele.id()]) {
 18940        continue;
 18941      } else {
 18942        orphanNodes.push(_ele);
 18943      }
 18944    } // assign the nodes a depth and index
 18945  
 18946  
 18947    var assignDepthsAt = function assignDepthsAt(i) {
 18948      var eles = depths[i];
 18949  
 18950      for (var j = 0; j < eles.length; j++) {
 18951        var _ele2 = eles[j];
 18952  
 18953        if (_ele2 == null) {
 18954          eles.splice(j, 1);
 18955          j--;
 18956          continue;
 18957        }
 18958  
 18959        setInfo(_ele2, {
 18960          depth: i,
 18961          index: j
 18962        });
 18963      }
 18964    };
 18965  
 18966    var assignDepths = function assignDepths() {
 18967      for (var _i3 = 0; _i3 < depths.length; _i3++) {
 18968        assignDepthsAt(_i3);
 18969      }
 18970    };
 18971  
 18972    var adjustMaximally = function adjustMaximally(ele, shifted) {
 18973      var eInfo = getInfo(ele);
 18974      var incomers = ele.incomers().filter(function (el) {
 18975        return el.isNode() && eles.has(el);
 18976      });
 18977      var maxDepth = -1;
 18978      var id = ele.id();
 18979  
 18980      for (var k = 0; k < incomers.length; k++) {
 18981        var incmr = incomers[k];
 18982        var iInfo = getInfo(incmr);
 18983        maxDepth = Math.max(maxDepth, iInfo.depth);
 18984      }
 18985  
 18986      if (eInfo.depth <= maxDepth) {
 18987        if (shifted[id]) {
 18988          return null;
 18989        }
 18990  
 18991        changeDepth(ele, maxDepth + 1);
 18992        shifted[id] = true;
 18993        return true;
 18994      }
 18995  
 18996      return false;
 18997    }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
 18998  
 18999  
 19000    if (directed && maximal) {
 19001      var Q = [];
 19002      var shifted = {};
 19003  
 19004      var enqueue = function enqueue(n) {
 19005        return Q.push(n);
 19006      };
 19007  
 19008      var dequeue = function dequeue() {
 19009        return Q.shift();
 19010      };
 19011  
 19012      nodes.forEach(function (n) {
 19013        return Q.push(n);
 19014      });
 19015  
 19016      while (Q.length > 0) {
 19017        var _ele3 = dequeue();
 19018  
 19019        var didShift = adjustMaximally(_ele3, shifted);
 19020  
 19021        if (didShift) {
 19022          _ele3.outgoers().filter(function (el) {
 19023            return el.isNode() && eles.has(el);
 19024          }).forEach(enqueue);
 19025        } else if (didShift === null) {
 19026          warn('Detected double maximal shift for node `' + _ele3.id() + '`.  Bailing maximal adjustment due to cycle.  Use `options.maximal: true` only on DAGs.');
 19027          break; // exit on failure
 19028        }
 19029      }
 19030    }
 19031  
 19032    assignDepths(); // clear holes
 19033    // find min distance we need to leave between nodes
 19034  
 19035    var minDistance = 0;
 19036  
 19037    if (options.avoidOverlap) {
 19038      for (var _i4 = 0; _i4 < nodes.length; _i4++) {
 19039        var n = nodes[_i4];
 19040        var nbb = n.layoutDimensions(options);
 19041        var w = nbb.w;
 19042        var h = nbb.h;
 19043        minDistance = Math.max(minDistance, w, h);
 19044      }
 19045    } // get the weighted percent for an element based on its connectivity to other levels
 19046  
 19047  
 19048    var cachedWeightedPercent = {};
 19049  
 19050    var getWeightedPercent = function getWeightedPercent(ele) {
 19051      if (cachedWeightedPercent[ele.id()]) {
 19052        return cachedWeightedPercent[ele.id()];
 19053      }
 19054  
 19055      var eleDepth = getInfo(ele).depth;
 19056      var neighbors = ele.neighborhood();
 19057      var percent = 0;
 19058      var samples = 0;
 19059  
 19060      for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
 19061        var neighbor = neighbors[_i5];
 19062  
 19063        if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
 19064          continue;
 19065        }
 19066  
 19067        var bf = getInfo(neighbor);
 19068        var index = bf.index;
 19069        var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
 19070  
 19071        if (index == null || depth == null) {
 19072          continue;
 19073        }
 19074  
 19075        var nDepth = depths[depth].length;
 19076  
 19077        if (depth < eleDepth) {
 19078          // only get influenced by elements above
 19079          percent += index / nDepth;
 19080          samples++;
 19081        }
 19082      }
 19083  
 19084      samples = Math.max(1, samples);
 19085      percent = percent / samples;
 19086  
 19087      if (samples === 0) {
 19088        // put lone nodes at the start
 19089        percent = 0;
 19090      }
 19091  
 19092      cachedWeightedPercent[ele.id()] = percent;
 19093      return percent;
 19094    }; // rearrange the indices in each depth level based on connectivity
 19095  
 19096  
 19097    var sortFn = function sortFn(a, b) {
 19098      var apct = getWeightedPercent(a);
 19099      var bpct = getWeightedPercent(b);
 19100      var diff = apct - bpct;
 19101  
 19102      if (diff === 0) {
 19103        return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
 19104      } else {
 19105        return diff;
 19106      }
 19107    }; // sort each level to make connected nodes closer
 19108  
 19109  
 19110    for (var _i6 = 0; _i6 < depths.length; _i6++) {
 19111      depths[_i6].sort(sortFn);
 19112  
 19113      assignDepthsAt(_i6);
 19114    } // assign orphan nodes to a new top-level depth
 19115  
 19116  
 19117    var orphanDepth = [];
 19118  
 19119    for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
 19120      orphanDepth.push(orphanNodes[_i7]);
 19121    }
 19122  
 19123    depths.unshift(orphanDepth);
 19124    assignDepths();
 19125    var biggestDepthSize = 0;
 19126  
 19127    for (var _i8 = 0; _i8 < depths.length; _i8++) {
 19128      biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
 19129    }
 19130  
 19131    var center = {
 19132      x: bb.x1 + bb.w / 2,
 19133      y: bb.x1 + bb.h / 2
 19134    };
 19135    var maxDepthSize = depths.reduce(function (max, eles) {
 19136      return Math.max(max, eles.length);
 19137    }, 0);
 19138  
 19139    var getPosition = function getPosition(ele) {
 19140      var _getInfo2 = getInfo(ele),
 19141          depth = _getInfo2.depth,
 19142          index = _getInfo2.index;
 19143  
 19144      var depthSize = depths[depth].length;
 19145      var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
 19146      var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
 19147      var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
 19148      radiusStepSize = Math.max(radiusStepSize, minDistance);
 19149  
 19150      if (!options.circle) {
 19151        var epos = {
 19152          x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
 19153          y: (depth + 1) * distanceY
 19154        };
 19155        return epos;
 19156      } else {
 19157        var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
 19158        var theta = 2 * Math.PI / depths[depth].length * index;
 19159  
 19160        if (depth === 0 && depths[0].length === 1) {
 19161          radius = 1;
 19162        }
 19163  
 19164        return {
 19165          x: center.x + radius * Math.cos(theta),
 19166          y: center.y + radius * Math.sin(theta)
 19167        };
 19168      }
 19169    };
 19170  
 19171    nodes.layoutPositions(this, options, getPosition);
 19172    return this; // chaining
 19173  };
 19174  
 19175  var defaults$a = {
 19176    fit: true,
 19177    // whether to fit the viewport to the graph
 19178    padding: 30,
 19179    // the padding on fit
 19180    boundingBox: undefined,
 19181    // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 19182    avoidOverlap: true,
 19183    // prevents node overlap, may overflow boundingBox and radius if not enough space
 19184    nodeDimensionsIncludeLabels: false,
 19185    // Excludes the label when calculating node bounding boxes for the layout algorithm
 19186    spacingFactor: undefined,
 19187    // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
 19188    radius: undefined,
 19189    // the radius of the circle
 19190    startAngle: 3 / 2 * Math.PI,
 19191    // where nodes start in radians
 19192    sweep: undefined,
 19193    // how many radians should be between the first and last node (defaults to full circle)
 19194    clockwise: true,
 19195    // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
 19196    sort: undefined,
 19197    // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
 19198    animate: false,
 19199    // whether to transition the node positions
 19200    animationDuration: 500,
 19201    // duration of animation in ms if enabled
 19202    animationEasing: undefined,
 19203    // easing of animation if enabled
 19204    animateFilter: function animateFilter(node, i) {
 19205      return true;
 19206    },
 19207    // 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
 19208    ready: undefined,
 19209    // callback on layoutready
 19210    stop: undefined,
 19211    // callback on layoutstop
 19212    transform: function transform(node, position) {
 19213      return position;
 19214    } // transform a given node position. Useful for changing flow direction in discrete layouts 
 19215  
 19216  };
 19217  
 19218  function CircleLayout(options) {
 19219    this.options = extend({}, defaults$a, options);
 19220  }
 19221  
 19222  CircleLayout.prototype.run = function () {
 19223    var params = this.options;
 19224    var options = params;
 19225    var cy = params.cy;
 19226    var eles = options.eles;
 19227    var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
 19228    var nodes = eles.nodes().not(':parent');
 19229  
 19230    if (options.sort) {
 19231      nodes = nodes.sort(options.sort);
 19232    }
 19233  
 19234    var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
 19235      x1: 0,
 19236      y1: 0,
 19237      w: cy.width(),
 19238      h: cy.height()
 19239    });
 19240    var center = {
 19241      x: bb.x1 + bb.w / 2,
 19242      y: bb.y1 + bb.h / 2
 19243    };
 19244    var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
 19245    var dTheta = sweep / Math.max(1, nodes.length - 1);
 19246    var r;
 19247    var minDistance = 0;
 19248  
 19249    for (var i = 0; i < nodes.length; i++) {
 19250      var n = nodes[i];
 19251      var nbb = n.layoutDimensions(options);
 19252      var w = nbb.w;
 19253      var h = nbb.h;
 19254      minDistance = Math.max(minDistance, w, h);
 19255    }
 19256  
 19257    if (number(options.radius)) {
 19258      r = options.radius;
 19259    } else if (nodes.length <= 1) {
 19260      r = 0;
 19261    } else {
 19262      r = Math.min(bb.h, bb.w) / 2 - minDistance;
 19263    } // calculate the radius
 19264  
 19265  
 19266    if (nodes.length > 1 && options.avoidOverlap) {
 19267      // but only if more than one node (can't overlap)
 19268      minDistance *= 1.75; // just to have some nice spacing
 19269  
 19270      var dcos = Math.cos(dTheta) - Math.cos(0);
 19271      var dsin = Math.sin(dTheta) - Math.sin(0);
 19272      var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
 19273  
 19274      r = Math.max(rMin, r);
 19275    }
 19276  
 19277    var getPos = function getPos(ele, i) {
 19278      var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
 19279      var rx = r * Math.cos(theta);
 19280      var ry = r * Math.sin(theta);
 19281      var pos = {
 19282        x: center.x + rx,
 19283        y: center.y + ry
 19284      };
 19285      return pos;
 19286    };
 19287  
 19288    nodes.layoutPositions(this, options, getPos);
 19289    return this; // chaining
 19290  };
 19291  
 19292  var defaults$b = {
 19293    fit: true,
 19294    // whether to fit the viewport to the graph
 19295    padding: 30,
 19296    // the padding on fit
 19297    startAngle: 3 / 2 * Math.PI,
 19298    // where nodes start in radians
 19299    sweep: undefined,
 19300    // how many radians should be between the first and last node (defaults to full circle)
 19301    clockwise: true,
 19302    // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
 19303    equidistant: false,
 19304    // whether levels have an equal radial distance betwen them, may cause bounding box overflow
 19305    minNodeSpacing: 10,
 19306    // min spacing between outside of nodes (used for radius adjustment)
 19307    boundingBox: undefined,
 19308    // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 19309    avoidOverlap: true,
 19310    // prevents node overlap, may overflow boundingBox if not enough space
 19311    nodeDimensionsIncludeLabels: false,
 19312    // Excludes the label when calculating node bounding boxes for the layout algorithm
 19313    height: undefined,
 19314    // height of layout area (overrides container height)
 19315    width: undefined,
 19316    // width of layout area (overrides container width)
 19317    spacingFactor: undefined,
 19318    // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
 19319    concentric: function concentric(node) {
 19320      // returns numeric value for each node, placing higher nodes in levels towards the centre
 19321      return node.degree();
 19322    },
 19323    levelWidth: function levelWidth(nodes) {
 19324      // the letiation of concentric values in each level
 19325      return nodes.maxDegree() / 4;
 19326    },
 19327    animate: false,
 19328    // whether to transition the node positions
 19329    animationDuration: 500,
 19330    // duration of animation in ms if enabled
 19331    animationEasing: undefined,
 19332    // easing of animation if enabled
 19333    animateFilter: function animateFilter(node, i) {
 19334      return true;
 19335    },
 19336    // 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
 19337    ready: undefined,
 19338    // callback on layoutready
 19339    stop: undefined,
 19340    // callback on layoutstop
 19341    transform: function transform(node, position) {
 19342      return position;
 19343    } // transform a given node position. Useful for changing flow direction in discrete layouts
 19344  
 19345  };
 19346  
 19347  function ConcentricLayout(options) {
 19348    this.options = extend({}, defaults$b, options);
 19349  }
 19350  
 19351  ConcentricLayout.prototype.run = function () {
 19352    var params = this.options;
 19353    var options = params;
 19354    var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
 19355    var cy = params.cy;
 19356    var eles = options.eles;
 19357    var nodes = eles.nodes().not(':parent');
 19358    var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
 19359      x1: 0,
 19360      y1: 0,
 19361      w: cy.width(),
 19362      h: cy.height()
 19363    });
 19364    var center = {
 19365      x: bb.x1 + bb.w / 2,
 19366      y: bb.y1 + bb.h / 2
 19367    };
 19368    var nodeValues = []; // { node, value }
 19369  
 19370    var maxNodeSize = 0;
 19371  
 19372    for (var i = 0; i < nodes.length; i++) {
 19373      var node = nodes[i];
 19374      var value = void 0; // calculate the node value
 19375  
 19376      value = options.concentric(node);
 19377      nodeValues.push({
 19378        value: value,
 19379        node: node
 19380      }); // for style mapping
 19381  
 19382      node._private.scratch.concentric = value;
 19383    } // in case we used the `concentric` in style
 19384  
 19385  
 19386    nodes.updateStyle(); // calculate max size now based on potentially updated mappers
 19387  
 19388    for (var _i = 0; _i < nodes.length; _i++) {
 19389      var _node = nodes[_i];
 19390  
 19391      var nbb = _node.layoutDimensions(options);
 19392  
 19393      maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
 19394    } // sort node values in descreasing order
 19395  
 19396  
 19397    nodeValues.sort(function (a, b) {
 19398      return b.value - a.value;
 19399    });
 19400    var levelWidth = options.levelWidth(nodes); // put the values into levels
 19401  
 19402    var levels = [[]];
 19403    var currentLevel = levels[0];
 19404  
 19405    for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
 19406      var val = nodeValues[_i2];
 19407  
 19408      if (currentLevel.length > 0) {
 19409        var diff = Math.abs(currentLevel[0].value - val.value);
 19410  
 19411        if (diff >= levelWidth) {
 19412          currentLevel = [];
 19413          levels.push(currentLevel);
 19414        }
 19415      }
 19416  
 19417      currentLevel.push(val);
 19418    } // create positions from levels
 19419  
 19420  
 19421    var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
 19422  
 19423    if (!options.avoidOverlap) {
 19424      // then strictly constrain to bb
 19425      var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
 19426      var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
 19427      var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
 19428      minDist = Math.min(minDist, rStep);
 19429    } // find the metrics for each level
 19430  
 19431  
 19432    var r = 0;
 19433  
 19434    for (var _i3 = 0; _i3 < levels.length; _i3++) {
 19435      var level = levels[_i3];
 19436      var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
 19437      var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
 19438  
 19439      if (level.length > 1 && options.avoidOverlap) {
 19440        // but only if more than one node (can't overlap)
 19441        var dcos = Math.cos(dTheta) - Math.cos(0);
 19442        var dsin = Math.sin(dTheta) - Math.sin(0);
 19443        var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
 19444  
 19445        r = Math.max(rMin, r);
 19446      }
 19447  
 19448      level.r = r;
 19449      r += minDist;
 19450    }
 19451  
 19452    if (options.equidistant) {
 19453      var rDeltaMax = 0;
 19454      var _r = 0;
 19455  
 19456      for (var _i4 = 0; _i4 < levels.length; _i4++) {
 19457        var _level = levels[_i4];
 19458        var rDelta = _level.r - _r;
 19459        rDeltaMax = Math.max(rDeltaMax, rDelta);
 19460      }
 19461  
 19462      _r = 0;
 19463  
 19464      for (var _i5 = 0; _i5 < levels.length; _i5++) {
 19465        var _level2 = levels[_i5];
 19466  
 19467        if (_i5 === 0) {
 19468          _r = _level2.r;
 19469        }
 19470  
 19471        _level2.r = _r;
 19472        _r += rDeltaMax;
 19473      }
 19474    } // calculate the node positions
 19475  
 19476  
 19477    var pos = {}; // id => position
 19478  
 19479    for (var _i6 = 0; _i6 < levels.length; _i6++) {
 19480      var _level3 = levels[_i6];
 19481      var _dTheta = _level3.dTheta;
 19482      var _r2 = _level3.r;
 19483  
 19484      for (var j = 0; j < _level3.length; j++) {
 19485        var _val = _level3[j];
 19486        var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
 19487        var p = {
 19488          x: center.x + _r2 * Math.cos(theta),
 19489          y: center.y + _r2 * Math.sin(theta)
 19490        };
 19491        pos[_val.node.id()] = p;
 19492      }
 19493    } // position the nodes
 19494  
 19495  
 19496    nodes.layoutPositions(this, options, function (ele) {
 19497      var id = ele.id();
 19498      return pos[id];
 19499    });
 19500    return this; // chaining
 19501  };
 19502  
 19503  /*
 19504  The CoSE layout was written by Gerardo Huck.
 19505  https://www.linkedin.com/in/gerardohuck/
 19506  
 19507  Based on the following article:
 19508  http://dl.acm.org/citation.cfm?id=1498047
 19509  
 19510  Modifications tracked on Github.
 19511  */
 19512  var DEBUG;
 19513  /**
 19514   * @brief :  default layout options
 19515   */
 19516  
 19517  var defaults$c = {
 19518    // Called on `layoutready`
 19519    ready: function ready() {},
 19520    // Called on `layoutstop`
 19521    stop: function stop() {},
 19522    // Whether to animate while running the layout
 19523    // true : Animate continuously as the layout is running
 19524    // false : Just show the end result
 19525    // 'end' : Animate with the end result, from the initial positions to the end positions
 19526    animate: true,
 19527    // Easing of the animation for animate:'end'
 19528    animationEasing: undefined,
 19529    // The duration of the animation for animate:'end'
 19530    animationDuration: undefined,
 19531    // A function that determines whether the node should be animated
 19532    // All nodes animated by default on animate enabled
 19533    // Non-animated nodes are positioned immediately when the layout starts
 19534    animateFilter: function animateFilter(node, i) {
 19535      return true;
 19536    },
 19537    // The layout animates only after this many milliseconds for animate:true
 19538    // (prevents flashing on fast runs)
 19539    animationThreshold: 250,
 19540    // Number of iterations between consecutive screen positions update
 19541    refresh: 20,
 19542    // Whether to fit the network view after when done
 19543    fit: true,
 19544    // Padding on fit
 19545    padding: 30,
 19546    // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 19547    boundingBox: undefined,
 19548    // Excludes the label when calculating node bounding boxes for the layout algorithm
 19549    nodeDimensionsIncludeLabels: false,
 19550    // Randomize the initial positions of the nodes (true) or use existing positions (false)
 19551    randomize: false,
 19552    // Extra spacing between components in non-compound graphs
 19553    componentSpacing: 40,
 19554    // Node repulsion (non overlapping) multiplier
 19555    nodeRepulsion: function nodeRepulsion(node) {
 19556      return 2048;
 19557    },
 19558    // Node repulsion (overlapping) multiplier
 19559    nodeOverlap: 4,
 19560    // Ideal edge (non nested) length
 19561    idealEdgeLength: function idealEdgeLength(edge) {
 19562      return 32;
 19563    },
 19564    // Divisor to compute edge forces
 19565    edgeElasticity: function edgeElasticity(edge) {
 19566      return 32;
 19567    },
 19568    // Nesting factor (multiplier) to compute ideal edge length for nested edges
 19569    nestingFactor: 1.2,
 19570    // Gravity force (constant)
 19571    gravity: 1,
 19572    // Maximum number of iterations to perform
 19573    numIter: 1000,
 19574    // Initial temperature (maximum node displacement)
 19575    initialTemp: 1000,
 19576    // Cooling factor (how the temperature is reduced between consecutive iterations
 19577    coolingFactor: 0.99,
 19578    // Lower temperature threshold (below this point the layout will end)
 19579    minTemp: 1.0
 19580  };
 19581  /**
 19582   * @brief       : constructor
 19583   * @arg options : object containing layout options
 19584   */
 19585  
 19586  function CoseLayout(options) {
 19587    this.options = extend({}, defaults$c, options);
 19588    this.options.layout = this;
 19589  }
 19590  /**
 19591   * @brief : runs the layout
 19592   */
 19593  
 19594  
 19595  CoseLayout.prototype.run = function () {
 19596    var options = this.options;
 19597    var cy = options.cy;
 19598    var layout = this;
 19599    layout.stopped = false;
 19600  
 19601    if (options.animate === true || options.animate === false) {
 19602      layout.emit({
 19603        type: 'layoutstart',
 19604        layout: layout
 19605      });
 19606    } // Set DEBUG - Global variable
 19607  
 19608  
 19609    if (true === options.debug) {
 19610      DEBUG = true;
 19611    } else {
 19612      DEBUG = false;
 19613    } // Initialize layout info
 19614  
 19615  
 19616    var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
 19617  
 19618    if (DEBUG) {
 19619      printLayoutInfo(layoutInfo);
 19620    } // If required, randomize node positions
 19621  
 19622  
 19623    if (options.randomize) {
 19624      randomizePositions(layoutInfo);
 19625    }
 19626  
 19627    var startTime = performanceNow();
 19628  
 19629    var refresh = function refresh() {
 19630      refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
 19631  
 19632      if (true === options.fit) {
 19633        cy.fit(options.padding);
 19634      }
 19635    };
 19636  
 19637    var mainLoop = function mainLoop(i) {
 19638      if (layout.stopped || i >= options.numIter) {
 19639        // logDebug("Layout manually stopped. Stopping computation in step " + i);
 19640        return false;
 19641      } // Do one step in the phisical simulation
 19642  
 19643  
 19644      step$1(layoutInfo, options); // Update temperature
 19645  
 19646      layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
 19647  
 19648      if (layoutInfo.temperature < options.minTemp) {
 19649        // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
 19650        return false;
 19651      }
 19652  
 19653      return true;
 19654    };
 19655  
 19656    var done = function done() {
 19657      if (options.animate === true || options.animate === false) {
 19658        refresh(); // Layout has finished
 19659  
 19660        layout.one('layoutstop', options.stop);
 19661        layout.emit({
 19662          type: 'layoutstop',
 19663          layout: layout
 19664        });
 19665      } else {
 19666        var nodes = options.eles.nodes();
 19667        var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
 19668        nodes.layoutPositions(layout, options, getScaledPos);
 19669      }
 19670    };
 19671  
 19672    var i = 0;
 19673    var loopRet = true;
 19674  
 19675    if (options.animate === true) {
 19676      var frame = function frame() {
 19677        var f = 0;
 19678  
 19679        while (loopRet && f < options.refresh) {
 19680          loopRet = mainLoop(i);
 19681          i++;
 19682          f++;
 19683        }
 19684  
 19685        if (!loopRet) {
 19686          // it's done
 19687          separateComponents(layoutInfo, options);
 19688          done();
 19689        } else {
 19690          var now = performanceNow();
 19691  
 19692          if (now - startTime >= options.animationThreshold) {
 19693            refresh();
 19694          }
 19695  
 19696          requestAnimationFrame(frame);
 19697        }
 19698      };
 19699  
 19700      frame();
 19701    } else {
 19702      while (loopRet) {
 19703        loopRet = mainLoop(i);
 19704        i++;
 19705      }
 19706  
 19707      separateComponents(layoutInfo, options);
 19708      done();
 19709    }
 19710  
 19711    return this; // chaining
 19712  };
 19713  /**
 19714   * @brief : called on continuous layouts to stop them before they finish
 19715   */
 19716  
 19717  
 19718  CoseLayout.prototype.stop = function () {
 19719    this.stopped = true;
 19720  
 19721    if (this.thread) {
 19722      this.thread.stop();
 19723    }
 19724  
 19725    this.emit('layoutstop');
 19726    return this; // chaining
 19727  };
 19728  
 19729  CoseLayout.prototype.destroy = function () {
 19730    if (this.thread) {
 19731      this.thread.stop();
 19732    }
 19733  
 19734    return this; // chaining
 19735  };
 19736  /**
 19737   * @brief     : Creates an object which is contains all the data
 19738   *              used in the layout process
 19739   * @arg cy    : cytoscape.js object
 19740   * @return    : layoutInfo object initialized
 19741   */
 19742  
 19743  
 19744  var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
 19745    // Shortcut
 19746    var edges = options.eles.edges();
 19747    var nodes = options.eles.nodes();
 19748    var layoutInfo = {
 19749      isCompound: cy.hasCompoundNodes(),
 19750      layoutNodes: [],
 19751      idToIndex: {},
 19752      nodeSize: nodes.size(),
 19753      graphSet: [],
 19754      indexToGraph: [],
 19755      layoutEdges: [],
 19756      edgeSize: edges.size(),
 19757      temperature: options.initialTemp,
 19758      clientWidth: cy.width(),
 19759      clientHeight: cy.width(),
 19760      boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
 19761        x1: 0,
 19762        y1: 0,
 19763        w: cy.width(),
 19764        h: cy.height()
 19765      })
 19766    };
 19767    var components = options.eles.components();
 19768    var id2cmptId = {};
 19769  
 19770    for (var i = 0; i < components.length; i++) {
 19771      var component = components[i];
 19772  
 19773      for (var j = 0; j < component.length; j++) {
 19774        var node = component[j];
 19775        id2cmptId[node.id()] = i;
 19776      }
 19777    } // Iterate over all nodes, creating layout nodes
 19778  
 19779  
 19780    for (var i = 0; i < layoutInfo.nodeSize; i++) {
 19781      var n = nodes[i];
 19782      var nbb = n.layoutDimensions(options);
 19783      var tempNode = {};
 19784      tempNode.isLocked = n.locked();
 19785      tempNode.id = n.data('id');
 19786      tempNode.parentId = n.data('parent');
 19787      tempNode.cmptId = id2cmptId[n.id()];
 19788      tempNode.children = [];
 19789      tempNode.positionX = n.position('x');
 19790      tempNode.positionY = n.position('y');
 19791      tempNode.offsetX = 0;
 19792      tempNode.offsetY = 0;
 19793      tempNode.height = nbb.w;
 19794      tempNode.width = nbb.h;
 19795      tempNode.maxX = tempNode.positionX + tempNode.width / 2;
 19796      tempNode.minX = tempNode.positionX - tempNode.width / 2;
 19797      tempNode.maxY = tempNode.positionY + tempNode.height / 2;
 19798      tempNode.minY = tempNode.positionY - tempNode.height / 2;
 19799      tempNode.padLeft = parseFloat(n.style('padding'));
 19800      tempNode.padRight = parseFloat(n.style('padding'));
 19801      tempNode.padTop = parseFloat(n.style('padding'));
 19802      tempNode.padBottom = parseFloat(n.style('padding')); // forces
 19803  
 19804      tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
 19805  
 19806      layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
 19807  
 19808      layoutInfo.idToIndex[tempNode.id] = i;
 19809    } // Inline implementation of a queue, used for traversing the graph in BFS order
 19810  
 19811  
 19812    var queue = [];
 19813    var start = 0; // Points to the start the queue
 19814  
 19815    var end = -1; // Points to the end of the queue
 19816  
 19817    var tempGraph = []; // Second pass to add child information and
 19818    // initialize queue for hierarchical traversal
 19819  
 19820    for (var i = 0; i < layoutInfo.nodeSize; i++) {
 19821      var n = layoutInfo.layoutNodes[i];
 19822      var p_id = n.parentId; // Check if node n has a parent node
 19823  
 19824      if (null != p_id) {
 19825        // Add node Id to parent's list of children
 19826        layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
 19827      } else {
 19828        // If a node doesn't have a parent, then it's in the root graph
 19829        queue[++end] = n.id;
 19830        tempGraph.push(n.id);
 19831      }
 19832    } // Add root graph to graphSet
 19833  
 19834  
 19835    layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
 19836  
 19837    while (start <= end) {
 19838      // Get the node to visit and remove it from queue
 19839      var node_id = queue[start++];
 19840      var node_ix = layoutInfo.idToIndex[node_id];
 19841      var node = layoutInfo.layoutNodes[node_ix];
 19842      var children = node.children;
 19843  
 19844      if (children.length > 0) {
 19845        // Add children nodes as a new graph to graph set
 19846        layoutInfo.graphSet.push(children); // Add children to que queue to be visited
 19847  
 19848        for (var i = 0; i < children.length; i++) {
 19849          queue[++end] = children[i];
 19850        }
 19851      }
 19852    } // Create indexToGraph map
 19853  
 19854  
 19855    for (var i = 0; i < layoutInfo.graphSet.length; i++) {
 19856      var graph = layoutInfo.graphSet[i];
 19857  
 19858      for (var j = 0; j < graph.length; j++) {
 19859        var index = layoutInfo.idToIndex[graph[j]];
 19860        layoutInfo.indexToGraph[index] = i;
 19861      }
 19862    } // Iterate over all edges, creating Layout Edges
 19863  
 19864  
 19865    for (var i = 0; i < layoutInfo.edgeSize; i++) {
 19866      var e = edges[i];
 19867      var tempEdge = {};
 19868      tempEdge.id = e.data('id');
 19869      tempEdge.sourceId = e.data('source');
 19870      tempEdge.targetId = e.data('target'); // Compute ideal length
 19871  
 19872      var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
 19873      var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
 19874  
 19875      var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
 19876      var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
 19877      var sourceGraph = layoutInfo.indexToGraph[sourceIx];
 19878      var targetGraph = layoutInfo.indexToGraph[targetIx];
 19879  
 19880      if (sourceGraph != targetGraph) {
 19881        // Find lowest common graph ancestor
 19882        var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
 19883  
 19884        var lcaGraph = layoutInfo.graphSet[lca];
 19885        var depth = 0; // Source depth
 19886  
 19887        var tempNode = layoutInfo.layoutNodes[sourceIx];
 19888  
 19889        while (-1 === lcaGraph.indexOf(tempNode.id)) {
 19890          tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
 19891          depth++;
 19892        } // Target depth
 19893  
 19894  
 19895        tempNode = layoutInfo.layoutNodes[targetIx];
 19896  
 19897        while (-1 === lcaGraph.indexOf(tempNode.id)) {
 19898          tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
 19899          depth++;
 19900        } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
 19901        //  ". Index: " + lca + " Contents: " + lcaGraph.toString() +
 19902        //  ". Depth: " + depth);
 19903        // Update idealLength
 19904  
 19905  
 19906        idealLength *= depth * options.nestingFactor;
 19907      }
 19908  
 19909      tempEdge.idealLength = idealLength;
 19910      tempEdge.elasticity = elasticity;
 19911      layoutInfo.layoutEdges.push(tempEdge);
 19912    } // Finally, return layoutInfo object
 19913  
 19914  
 19915    return layoutInfo;
 19916  };
 19917  /**
 19918   * @brief : This function finds the index of the lowest common
 19919   *          graph ancestor between 2 nodes in the subtree
 19920   *          (from the graph hierarchy induced tree) whose
 19921   *          root is graphIx
 19922   *
 19923   * @arg node1: node1's ID
 19924   * @arg node2: node2's ID
 19925   * @arg layoutInfo: layoutInfo object
 19926   *
 19927   */
 19928  
 19929  
 19930  var findLCA = function findLCA(node1, node2, layoutInfo) {
 19931    // Find their common ancester, starting from the root graph
 19932    var res = findLCA_aux(node1, node2, 0, layoutInfo);
 19933  
 19934    if (2 > res.count) {
 19935      // If aux function couldn't find the common ancester,
 19936      // then it is the root graph
 19937      return 0;
 19938    } else {
 19939      return res.graph;
 19940    }
 19941  };
 19942  /**
 19943   * @brief          : Auxiliary function used for LCA computation
 19944   *
 19945   * @arg node1      : node1's ID
 19946   * @arg node2      : node2's ID
 19947   * @arg graphIx    : subgraph index
 19948   * @arg layoutInfo : layoutInfo object
 19949   *
 19950   * @return         : object of the form {count: X, graph: Y}, where:
 19951   *                   X is the number of ancesters (max: 2) found in
 19952   *                   graphIx (and it's subgraphs),
 19953   *                   Y is the graph index of the lowest graph containing
 19954   *                   all X nodes
 19955   */
 19956  
 19957  
 19958  var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
 19959    var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
 19960  
 19961    if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
 19962      return {
 19963        count: 2,
 19964        graph: graphIx
 19965      };
 19966    } // Make recursive calls for all subgraphs
 19967  
 19968  
 19969    var c = 0;
 19970  
 19971    for (var i = 0; i < graph.length; i++) {
 19972      var nodeId = graph[i];
 19973      var nodeIx = layoutInfo.idToIndex[nodeId];
 19974      var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
 19975  
 19976      if (0 === children.length) {
 19977        continue;
 19978      }
 19979  
 19980      var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
 19981      var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
 19982  
 19983      if (0 === result.count) {
 19984        // Neither node1 nor node2 are present in this subgraph
 19985        continue;
 19986      } else if (1 === result.count) {
 19987        // One of (node1, node2) is present in this subgraph
 19988        c++;
 19989  
 19990        if (2 === c) {
 19991          // We've already found both nodes, no need to keep searching
 19992          break;
 19993        }
 19994      } else {
 19995        // Both nodes are present in this subgraph
 19996        return result;
 19997      }
 19998    }
 19999  
 20000    return {
 20001      count: c,
 20002      graph: graphIx
 20003    };
 20004  };
 20005  /**
 20006   * @brief: printsLayoutInfo into js console
 20007   *         Only used for debbuging
 20008   */
 20009  
 20010  
 20011  if (false) {
 20012    var printLayoutInfo;
 20013  }
 20014  /**
 20015   * @brief : Randomizes the position of all nodes
 20016   */
 20017  
 20018  
 20019  var randomizePositions = function randomizePositions(layoutInfo, cy) {
 20020    var width = layoutInfo.clientWidth;
 20021    var height = layoutInfo.clientHeight;
 20022  
 20023    for (var i = 0; i < layoutInfo.nodeSize; i++) {
 20024      var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
 20025  
 20026      if (0 === n.children.length && !n.isLocked) {
 20027        n.positionX = Math.random() * width;
 20028        n.positionY = Math.random() * height;
 20029      }
 20030    }
 20031  };
 20032  
 20033  var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
 20034    var bb = layoutInfo.boundingBox;
 20035    var coseBB = {
 20036      x1: Infinity,
 20037      x2: -Infinity,
 20038      y1: Infinity,
 20039      y2: -Infinity
 20040    };
 20041  
 20042    if (options.boundingBox) {
 20043      nodes.forEach(function (node) {
 20044        var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
 20045        coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
 20046        coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
 20047        coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
 20048        coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
 20049      });
 20050      coseBB.w = coseBB.x2 - coseBB.x1;
 20051      coseBB.h = coseBB.y2 - coseBB.y1;
 20052    }
 20053  
 20054    return function (ele, i) {
 20055      var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
 20056  
 20057      if (options.boundingBox) {
 20058        // then add extra bounding box constraint
 20059        var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
 20060        var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
 20061        return {
 20062          x: bb.x1 + pctX * bb.w,
 20063          y: bb.y1 + pctY * bb.h
 20064        };
 20065      } else {
 20066        return {
 20067          x: lnode.positionX,
 20068          y: lnode.positionY
 20069        };
 20070      }
 20071    };
 20072  };
 20073  /**
 20074   * @brief          : Updates the positions of nodes in the network
 20075   * @arg layoutInfo : LayoutInfo object
 20076   * @arg cy         : Cytoscape object
 20077   * @arg options    : Layout options
 20078   */
 20079  
 20080  
 20081  var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
 20082    // var s = 'Refreshing positions';
 20083    // logDebug(s);
 20084    var layout = options.layout;
 20085    var nodes = options.eles.nodes();
 20086    var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
 20087    nodes.positions(getScaledPos); // Trigger layoutReady only on first call
 20088  
 20089    if (true !== layoutInfo.ready) {
 20090      // s = 'Triggering layoutready';
 20091      // logDebug(s);
 20092      layoutInfo.ready = true;
 20093      layout.one('layoutready', options.ready);
 20094      layout.emit({
 20095        type: 'layoutready',
 20096        layout: this
 20097      });
 20098    }
 20099  };
 20100  /**
 20101   * @brief : Logs a debug message in JS console, if DEBUG is ON
 20102   */
 20103  // var logDebug = function(text) {
 20104  //   if (DEBUG) {
 20105  //     console.debug(text);
 20106  //   }
 20107  // };
 20108  
 20109  /**
 20110   * @brief          : Performs one iteration of the physical simulation
 20111   * @arg layoutInfo : LayoutInfo object already initialized
 20112   * @arg cy         : Cytoscape object
 20113   * @arg options    : Layout options
 20114   */
 20115  
 20116  
 20117  var step$1 = function step(layoutInfo, options, _step) {
 20118    // var s = "\n\n###############################";
 20119    // s += "\nSTEP: " + step;
 20120    // s += "\n###############################\n";
 20121    // logDebug(s);
 20122    // Calculate node repulsions
 20123    calculateNodeForces(layoutInfo, options); // Calculate edge forces
 20124  
 20125    calculateEdgeForces(layoutInfo); // Calculate gravity forces
 20126  
 20127    calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
 20128  
 20129    propagateForces(layoutInfo); // Update positions based on calculated forces
 20130  
 20131    updatePositions(layoutInfo);
 20132  };
 20133  /**
 20134   * @brief : Computes the node repulsion forces
 20135   */
 20136  
 20137  
 20138  var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
 20139    // Go through each of the graphs in graphSet
 20140    // Nodes only repel each other if they belong to the same graph
 20141    // var s = 'calculateNodeForces';
 20142    // logDebug(s);
 20143    for (var i = 0; i < layoutInfo.graphSet.length; i++) {
 20144      var graph = layoutInfo.graphSet[i];
 20145      var numNodes = graph.length; // s = "Set: " + graph.toString();
 20146      // logDebug(s);
 20147      // Now get all the pairs of nodes
 20148      // Only get each pair once, (A, B) = (B, A)
 20149  
 20150      for (var j = 0; j < numNodes; j++) {
 20151        var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
 20152  
 20153        for (var k = j + 1; k < numNodes; k++) {
 20154          var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
 20155          nodeRepulsion(node1, node2, layoutInfo, options);
 20156        }
 20157      }
 20158    }
 20159  };
 20160  
 20161  var randomDistance = function randomDistance(max) {
 20162    return -max + 2 * max * Math.random();
 20163  };
 20164  /**
 20165   * @brief : Compute the node repulsion forces between a pair of nodes
 20166   */
 20167  
 20168  
 20169  var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
 20170    // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
 20171    var cmptId1 = node1.cmptId;
 20172    var cmptId2 = node2.cmptId;
 20173  
 20174    if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
 20175      return;
 20176    } // Get direction of line connecting both node centers
 20177  
 20178  
 20179    var directionX = node2.positionX - node1.positionX;
 20180    var directionY = node2.positionY - node1.positionY;
 20181    var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
 20182    // If both centers are the same, apply a random force
 20183  
 20184    if (0 === directionX && 0 === directionY) {
 20185      directionX = randomDistance(maxRandDist);
 20186      directionY = randomDistance(maxRandDist);
 20187    }
 20188  
 20189    var overlap = nodesOverlap(node1, node2, directionX, directionY);
 20190  
 20191    if (overlap > 0) {
 20192      // s += "\nNodes DO overlap.";
 20193      // s += "\nOverlap: " + overlap;
 20194      // If nodes overlap, repulsion force is proportional
 20195      // to the overlap
 20196      var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
 20197  
 20198      var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
 20199  
 20200      var forceX = force * directionX / distance;
 20201      var forceY = force * directionY / distance;
 20202    } else {
 20203      // s += "\nNodes do NOT overlap.";
 20204      // If there's no overlap, force is inversely proportional
 20205      // to squared distance
 20206      // Get clipping points for both nodes
 20207      var point1 = findClippingPoint(node1, directionX, directionY);
 20208      var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
 20209  
 20210      var distanceX = point2.x - point1.x;
 20211      var distanceY = point2.y - point1.y;
 20212      var distanceSqr = distanceX * distanceX + distanceY * distanceY;
 20213      var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
 20214      // Compute the module and components of the force vector
 20215  
 20216      var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
 20217      var forceX = force * distanceX / distance;
 20218      var forceY = force * distanceY / distance;
 20219    } // Apply force
 20220  
 20221  
 20222    if (!node1.isLocked) {
 20223      node1.offsetX -= forceX;
 20224      node1.offsetY -= forceY;
 20225    }
 20226  
 20227    if (!node2.isLocked) {
 20228      node2.offsetX += forceX;
 20229      node2.offsetY += forceY;
 20230    } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
 20231    // logDebug(s);
 20232  
 20233  
 20234    return;
 20235  };
 20236  /**
 20237   * @brief  : Determines whether two nodes overlap or not
 20238   * @return : Amount of overlapping (0 => no overlap)
 20239   */
 20240  
 20241  
 20242  var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
 20243    if (dX > 0) {
 20244      var overlapX = node1.maxX - node2.minX;
 20245    } else {
 20246      var overlapX = node2.maxX - node1.minX;
 20247    }
 20248  
 20249    if (dY > 0) {
 20250      var overlapY = node1.maxY - node2.minY;
 20251    } else {
 20252      var overlapY = node2.maxY - node1.minY;
 20253    }
 20254  
 20255    if (overlapX >= 0 && overlapY >= 0) {
 20256      return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
 20257    } else {
 20258      return 0;
 20259    }
 20260  };
 20261  /**
 20262   * @brief : Finds the point in which an edge (direction dX, dY) intersects
 20263   *          the rectangular bounding box of it's source/target node
 20264   */
 20265  
 20266  
 20267  var findClippingPoint = function findClippingPoint(node, dX, dY) {
 20268    // Shorcuts
 20269    var X = node.positionX;
 20270    var Y = node.positionY;
 20271    var H = node.height || 1;
 20272    var W = node.width || 1;
 20273    var dirSlope = dY / dX;
 20274    var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
 20275    //   " . Height:  " + H + ", Width: " + W +
 20276    //   "\nDirection " + dX + ", " + dY;
 20277    //
 20278    // Compute intersection
 20279  
 20280    var res = {}; // Case: Vertical direction (up)
 20281  
 20282    if (0 === dX && 0 < dY) {
 20283      res.x = X; // s += "\nUp direction";
 20284  
 20285      res.y = Y + H / 2;
 20286      return res;
 20287    } // Case: Vertical direction (down)
 20288  
 20289  
 20290    if (0 === dX && 0 > dY) {
 20291      res.x = X;
 20292      res.y = Y + H / 2; // s += "\nDown direction";
 20293  
 20294      return res;
 20295    } // Case: Intersects the right border
 20296  
 20297  
 20298    if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
 20299      res.x = X + W / 2;
 20300      res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
 20301  
 20302      return res;
 20303    } // Case: Intersects the left border
 20304  
 20305  
 20306    if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
 20307      res.x = X - W / 2;
 20308      res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
 20309  
 20310      return res;
 20311    } // Case: Intersects the top border
 20312  
 20313  
 20314    if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
 20315      res.x = X + H * dX / 2 / dY;
 20316      res.y = Y + H / 2; // s += "\nTop border";
 20317  
 20318      return res;
 20319    } // Case: Intersects the bottom border
 20320  
 20321  
 20322    if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
 20323      res.x = X - H * dX / 2 / dY;
 20324      res.y = Y - H / 2; // s += "\nBottom border";
 20325  
 20326      return res;
 20327    } // s += "\nClipping point found at " + res.x + ", " + res.y;
 20328    // logDebug(s);
 20329  
 20330  
 20331    return res;
 20332  };
 20333  /**
 20334   * @brief : Calculates all edge forces
 20335   */
 20336  
 20337  
 20338  var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
 20339    // Iterate over all edges
 20340    for (var i = 0; i < layoutInfo.edgeSize; i++) {
 20341      // Get edge, source & target nodes
 20342      var edge = layoutInfo.layoutEdges[i];
 20343      var sourceIx = layoutInfo.idToIndex[edge.sourceId];
 20344      var source = layoutInfo.layoutNodes[sourceIx];
 20345      var targetIx = layoutInfo.idToIndex[edge.targetId];
 20346      var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
 20347  
 20348      var directionX = target.positionX - source.positionX;
 20349      var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
 20350      // A random force has already been applied as node repulsion
 20351  
 20352      if (0 === directionX && 0 === directionY) {
 20353        continue;
 20354      } // Get clipping points for both nodes
 20355  
 20356  
 20357      var point1 = findClippingPoint(source, directionX, directionY);
 20358      var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
 20359      var lx = point2.x - point1.x;
 20360      var ly = point2.y - point1.y;
 20361      var l = Math.sqrt(lx * lx + ly * ly);
 20362      var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
 20363  
 20364      if (0 !== l) {
 20365        var forceX = force * lx / l;
 20366        var forceY = force * ly / l;
 20367      } else {
 20368        var forceX = 0;
 20369        var forceY = 0;
 20370      } // Add this force to target and source nodes
 20371  
 20372  
 20373      if (!source.isLocked) {
 20374        source.offsetX += forceX;
 20375        source.offsetY += forceY;
 20376      }
 20377  
 20378      if (!target.isLocked) {
 20379        target.offsetX -= forceX;
 20380        target.offsetY -= forceY;
 20381      } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
 20382      // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
 20383      // logDebug(s);
 20384  
 20385    }
 20386  };
 20387  /**
 20388   * @brief : Computes gravity forces for all nodes
 20389   */
 20390  
 20391  
 20392  var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
 20393    var distThreshold = 1; // var s = 'calculateGravityForces';
 20394    // logDebug(s);
 20395  
 20396    for (var i = 0; i < layoutInfo.graphSet.length; i++) {
 20397      var graph = layoutInfo.graphSet[i];
 20398      var numNodes = graph.length; // s = "Set: " + graph.toString();
 20399      // logDebug(s);
 20400      // Compute graph center
 20401  
 20402      if (0 === i) {
 20403        var centerX = layoutInfo.clientHeight / 2;
 20404        var centerY = layoutInfo.clientWidth / 2;
 20405      } else {
 20406        // Get Parent node for this graph, and use its position as center
 20407        var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
 20408        var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
 20409        var centerX = parent.positionX;
 20410        var centerY = parent.positionY;
 20411      } // s = "Center found at: " + centerX + ", " + centerY;
 20412      // logDebug(s);
 20413      // Apply force to all nodes in graph
 20414  
 20415  
 20416      for (var j = 0; j < numNodes; j++) {
 20417        var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
 20418  
 20419        if (node.isLocked) {
 20420          continue;
 20421        }
 20422  
 20423        var dx = centerX - node.positionX;
 20424        var dy = centerY - node.positionY;
 20425        var d = Math.sqrt(dx * dx + dy * dy);
 20426  
 20427        if (d > distThreshold) {
 20428          var fx = options.gravity * dx / d;
 20429          var fy = options.gravity * dy / d;
 20430          node.offsetX += fx;
 20431          node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
 20432        } // s += ": skypped since it's too close to center";
 20433          // logDebug(s);
 20434  
 20435      }
 20436    }
 20437  };
 20438  /**
 20439   * @brief          : This function propagates the existing offsets from
 20440   *                   parent nodes to its descendents.
 20441   * @arg layoutInfo : layoutInfo Object
 20442   * @arg cy         : cytoscape Object
 20443   * @arg options    : Layout options
 20444   */
 20445  
 20446  
 20447  var propagateForces = function propagateForces(layoutInfo, options) {
 20448    // Inline implementation of a queue, used for traversing the graph in BFS order
 20449    var queue = [];
 20450    var start = 0; // Points to the start the queue
 20451  
 20452    var end = -1; // Points to the end of the queue
 20453    // logDebug('propagateForces');
 20454    // Start by visiting the nodes in the root graph
 20455  
 20456    queue.push.apply(queue, layoutInfo.graphSet[0]);
 20457    end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
 20458  
 20459    while (start <= end) {
 20460      // Get the node to visit and remove it from queue
 20461      var nodeId = queue[start++];
 20462      var nodeIndex = layoutInfo.idToIndex[nodeId];
 20463      var node = layoutInfo.layoutNodes[nodeIndex];
 20464      var children = node.children; // We only need to process the node if it's compound
 20465  
 20466      if (0 < children.length && !node.isLocked) {
 20467        var offX = node.offsetX;
 20468        var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
 20469        //   ". OffsetX: " + offX + ". OffsetY: " + offY;
 20470        // s += "\n Children: " + children.toString();
 20471        // logDebug(s);
 20472  
 20473        for (var i = 0; i < children.length; i++) {
 20474          var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
 20475  
 20476          childNode.offsetX += offX;
 20477          childNode.offsetY += offY; // Add children to queue to be visited
 20478  
 20479          queue[++end] = children[i];
 20480        } // Reset parent offsets
 20481  
 20482  
 20483        node.offsetX = 0;
 20484        node.offsetY = 0;
 20485      }
 20486    }
 20487  };
 20488  /**
 20489   * @brief : Updates the layout model positions, based on
 20490   *          the accumulated forces
 20491   */
 20492  
 20493  
 20494  var updatePositions = function updatePositions(layoutInfo, options) {
 20495    // var s = 'Updating positions';
 20496    // logDebug(s);
 20497    // Reset boundaries for compound nodes
 20498    for (var i = 0; i < layoutInfo.nodeSize; i++) {
 20499      var n = layoutInfo.layoutNodes[i];
 20500  
 20501      if (0 < n.children.length) {
 20502        // logDebug("Resetting boundaries of compound node: " + n.id);
 20503        n.maxX = undefined;
 20504        n.minX = undefined;
 20505        n.maxY = undefined;
 20506        n.minY = undefined;
 20507      }
 20508    }
 20509  
 20510    for (var i = 0; i < layoutInfo.nodeSize; i++) {
 20511      var n = layoutInfo.layoutNodes[i];
 20512  
 20513      if (0 < n.children.length || n.isLocked) {
 20514        // No need to set compound or locked node position
 20515        // logDebug("Skipping position update of node: " + n.id);
 20516        continue;
 20517      } // s = "Node: " + n.id + " Previous position: (" +
 20518      // n.positionX + ", " + n.positionY + ").";
 20519      // Limit displacement in order to improve stability
 20520  
 20521  
 20522      var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
 20523      n.positionX += tempForce.x;
 20524      n.positionY += tempForce.y;
 20525      n.offsetX = 0;
 20526      n.offsetY = 0;
 20527      n.minX = n.positionX - n.width;
 20528      n.maxX = n.positionX + n.width;
 20529      n.minY = n.positionY - n.height;
 20530      n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
 20531      // logDebug(s);
 20532      // Update ancestry boudaries
 20533  
 20534      updateAncestryBoundaries(n, layoutInfo);
 20535    } // Update size, position of compund nodes
 20536  
 20537  
 20538    for (var i = 0; i < layoutInfo.nodeSize; i++) {
 20539      var n = layoutInfo.layoutNodes[i];
 20540  
 20541      if (0 < n.children.length && !n.isLocked) {
 20542        n.positionX = (n.maxX + n.minX) / 2;
 20543        n.positionY = (n.maxY + n.minY) / 2;
 20544        n.width = n.maxX - n.minX;
 20545        n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
 20546        // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
 20547        // s += "\nWidth: " + n.width + ", Height: " + n.height;
 20548        // logDebug(s);
 20549      }
 20550    }
 20551  };
 20552  /**
 20553   * @brief : Limits a force (forceX, forceY) to be not
 20554   *          greater (in modulo) than max.
 20555   8          Preserves force direction.
 20556    */
 20557  
 20558  
 20559  var limitForce = function limitForce(forceX, forceY, max) {
 20560    // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
 20561    var force = Math.sqrt(forceX * forceX + forceY * forceY);
 20562  
 20563    if (force > max) {
 20564      var res = {
 20565        x: max * forceX / force,
 20566        y: max * forceY / force
 20567      };
 20568    } else {
 20569      var res = {
 20570        x: forceX,
 20571        y: forceY
 20572      };
 20573    } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
 20574    // logDebug(s);
 20575  
 20576  
 20577    return res;
 20578  };
 20579  /**
 20580   * @brief : Function used for keeping track of compound node
 20581   *          sizes, since they should bound all their subnodes.
 20582   */
 20583  
 20584  
 20585  var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
 20586    // var s = "Propagating new position/size of node " + node.id;
 20587    var parentId = node.parentId;
 20588  
 20589    if (null == parentId) {
 20590      // If there's no parent, we are done
 20591      // s += ". No parent node.";
 20592      // logDebug(s);
 20593      return;
 20594    } // Get Parent Node
 20595  
 20596  
 20597    var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
 20598    var flag = false; // MaxX
 20599  
 20600    if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
 20601      p.maxX = node.maxX + p.padRight;
 20602      flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
 20603    } // MinX
 20604  
 20605  
 20606    if (null == p.minX || node.minX - p.padLeft < p.minX) {
 20607      p.minX = node.minX - p.padLeft;
 20608      flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
 20609    } // MaxY
 20610  
 20611  
 20612    if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
 20613      p.maxY = node.maxY + p.padBottom;
 20614      flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
 20615    } // MinY
 20616  
 20617  
 20618    if (null == p.minY || node.minY - p.padTop < p.minY) {
 20619      p.minY = node.minY - p.padTop;
 20620      flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
 20621    } // If updated boundaries, propagate changes upward
 20622  
 20623  
 20624    if (flag) {
 20625      // logDebug(s);
 20626      return updateAncestryBoundaries(p, layoutInfo);
 20627    } // s += ". No changes in boundaries/position of parent node " + p.id;
 20628    // logDebug(s);
 20629  
 20630  
 20631    return;
 20632  };
 20633  
 20634  var separateComponents = function separateComponents(layoutInfo, options) {
 20635    var nodes = layoutInfo.layoutNodes;
 20636    var components = [];
 20637  
 20638    for (var i = 0; i < nodes.length; i++) {
 20639      var node = nodes[i];
 20640      var cid = node.cmptId;
 20641      var component = components[cid] = components[cid] || [];
 20642      component.push(node);
 20643    }
 20644  
 20645    var totalA = 0;
 20646  
 20647    for (var i = 0; i < components.length; i++) {
 20648      var c = components[i];
 20649  
 20650      if (!c) {
 20651        continue;
 20652      }
 20653  
 20654      c.x1 = Infinity;
 20655      c.x2 = -Infinity;
 20656      c.y1 = Infinity;
 20657      c.y2 = -Infinity;
 20658  
 20659      for (var j = 0; j < c.length; j++) {
 20660        var n = c[j];
 20661        c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
 20662        c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
 20663        c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
 20664        c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
 20665      }
 20666  
 20667      c.w = c.x2 - c.x1;
 20668      c.h = c.y2 - c.y1;
 20669      totalA += c.w * c.h;
 20670    }
 20671  
 20672    components.sort(function (c1, c2) {
 20673      return c2.w * c2.h - c1.w * c1.h;
 20674    });
 20675    var x = 0;
 20676    var y = 0;
 20677    var usedW = 0;
 20678    var rowH = 0;
 20679    var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
 20680  
 20681    for (var i = 0; i < components.length; i++) {
 20682      var c = components[i];
 20683  
 20684      if (!c) {
 20685        continue;
 20686      }
 20687  
 20688      for (var j = 0; j < c.length; j++) {
 20689        var n = c[j];
 20690  
 20691        if (!n.isLocked) {
 20692          n.positionX += x - c.x1;
 20693          n.positionY += y - c.y1;
 20694        }
 20695      }
 20696  
 20697      x += c.w + options.componentSpacing;
 20698      usedW += c.w + options.componentSpacing;
 20699      rowH = Math.max(rowH, c.h);
 20700  
 20701      if (usedW > maxRowW) {
 20702        y += rowH + options.componentSpacing;
 20703        x = 0;
 20704        usedW = 0;
 20705        rowH = 0;
 20706      }
 20707    }
 20708  };
 20709  
 20710  var defaults$d = {
 20711    fit: true,
 20712    // whether to fit the viewport to the graph
 20713    padding: 30,
 20714    // padding used on fit
 20715    boundingBox: undefined,
 20716    // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 20717    avoidOverlap: true,
 20718    // prevents node overlap, may overflow boundingBox if not enough space
 20719    avoidOverlapPadding: 10,
 20720    // extra spacing around nodes when avoidOverlap: true
 20721    nodeDimensionsIncludeLabels: false,
 20722    // Excludes the label when calculating node bounding boxes for the layout algorithm
 20723    spacingFactor: undefined,
 20724    // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
 20725    condense: false,
 20726    // uses all available space on false, uses minimal space on true
 20727    rows: undefined,
 20728    // force num of rows in the grid
 20729    cols: undefined,
 20730    // force num of columns in the grid
 20731    position: function position(node) {},
 20732    // returns { row, col } for element
 20733    sort: undefined,
 20734    // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
 20735    animate: false,
 20736    // whether to transition the node positions
 20737    animationDuration: 500,
 20738    // duration of animation in ms if enabled
 20739    animationEasing: undefined,
 20740    // easing of animation if enabled
 20741    animateFilter: function animateFilter(node, i) {
 20742      return true;
 20743    },
 20744    // 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
 20745    ready: undefined,
 20746    // callback on layoutready
 20747    stop: undefined,
 20748    // callback on layoutstop
 20749    transform: function transform(node, position) {
 20750      return position;
 20751    } // transform a given node position. Useful for changing flow direction in discrete layouts 
 20752  
 20753  };
 20754  
 20755  function GridLayout(options) {
 20756    this.options = extend({}, defaults$d, options);
 20757  }
 20758  
 20759  GridLayout.prototype.run = function () {
 20760    var params = this.options;
 20761    var options = params;
 20762    var cy = params.cy;
 20763    var eles = options.eles;
 20764    var nodes = eles.nodes().not(':parent');
 20765  
 20766    if (options.sort) {
 20767      nodes = nodes.sort(options.sort);
 20768    }
 20769  
 20770    var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
 20771      x1: 0,
 20772      y1: 0,
 20773      w: cy.width(),
 20774      h: cy.height()
 20775    });
 20776  
 20777    if (bb.h === 0 || bb.w === 0) {
 20778      nodes.layoutPositions(this, options, function (ele) {
 20779        return {
 20780          x: bb.x1,
 20781          y: bb.y1
 20782        };
 20783      });
 20784    } else {
 20785      // width/height * splits^2 = cells where splits is number of times to split width
 20786      var cells = nodes.size();
 20787      var splits = Math.sqrt(cells * bb.h / bb.w);
 20788      var rows = Math.round(splits);
 20789      var cols = Math.round(bb.w / bb.h * splits);
 20790  
 20791      var small = function small(val) {
 20792        if (val == null) {
 20793          return Math.min(rows, cols);
 20794        } else {
 20795          var min = Math.min(rows, cols);
 20796  
 20797          if (min == rows) {
 20798            rows = val;
 20799          } else {
 20800            cols = val;
 20801          }
 20802        }
 20803      };
 20804  
 20805      var large = function large(val) {
 20806        if (val == null) {
 20807          return Math.max(rows, cols);
 20808        } else {
 20809          var max = Math.max(rows, cols);
 20810  
 20811          if (max == rows) {
 20812            rows = val;
 20813          } else {
 20814            cols = val;
 20815          }
 20816        }
 20817      };
 20818  
 20819      var oRows = options.rows;
 20820      var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
 20821  
 20822      if (oRows != null && oCols != null) {
 20823        rows = oRows;
 20824        cols = oCols;
 20825      } else if (oRows != null && oCols == null) {
 20826        rows = oRows;
 20827        cols = Math.ceil(cells / rows);
 20828      } else if (oRows == null && oCols != null) {
 20829        cols = oCols;
 20830        rows = Math.ceil(cells / cols);
 20831      } // otherwise use the automatic values and adjust accordingly
 20832      // if rounding was up, see if we can reduce rows or columns
 20833      else if (cols * rows > cells) {
 20834          var sm = small();
 20835          var lg = large(); // reducing the small side takes away the most cells, so try it first
 20836  
 20837          if ((sm - 1) * lg >= cells) {
 20838            small(sm - 1);
 20839          } else if ((lg - 1) * sm >= cells) {
 20840            large(lg - 1);
 20841          }
 20842        } else {
 20843          // if rounding was too low, add rows or columns
 20844          while (cols * rows < cells) {
 20845            var _sm = small();
 20846  
 20847            var _lg = large(); // try to add to larger side first (adds less in multiplication)
 20848  
 20849  
 20850            if ((_lg + 1) * _sm >= cells) {
 20851              large(_lg + 1);
 20852            } else {
 20853              small(_sm + 1);
 20854            }
 20855          }
 20856        }
 20857  
 20858      var cellWidth = bb.w / cols;
 20859      var cellHeight = bb.h / rows;
 20860  
 20861      if (options.condense) {
 20862        cellWidth = 0;
 20863        cellHeight = 0;
 20864      }
 20865  
 20866      if (options.avoidOverlap) {
 20867        for (var i = 0; i < nodes.length; i++) {
 20868          var node = nodes[i];
 20869          var pos = node._private.position;
 20870  
 20871          if (pos.x == null || pos.y == null) {
 20872            // for bb
 20873            pos.x = 0;
 20874            pos.y = 0;
 20875          }
 20876  
 20877          var nbb = node.layoutDimensions(options);
 20878          var p = options.avoidOverlapPadding;
 20879          var w = nbb.w + p;
 20880          var h = nbb.h + p;
 20881          cellWidth = Math.max(cellWidth, w);
 20882          cellHeight = Math.max(cellHeight, h);
 20883        }
 20884      }
 20885  
 20886      var cellUsed = {}; // e.g. 'c-0-2' => true
 20887  
 20888      var used = function used(row, col) {
 20889        return cellUsed['c-' + row + '-' + col] ? true : false;
 20890      };
 20891  
 20892      var use = function use(row, col) {
 20893        cellUsed['c-' + row + '-' + col] = true;
 20894      }; // to keep track of current cell position
 20895  
 20896  
 20897      var row = 0;
 20898      var col = 0;
 20899  
 20900      var moveToNextCell = function moveToNextCell() {
 20901        col++;
 20902  
 20903        if (col >= cols) {
 20904          col = 0;
 20905          row++;
 20906        }
 20907      }; // get a cache of all the manual positions
 20908  
 20909  
 20910      var id2manPos = {};
 20911  
 20912      for (var _i = 0; _i < nodes.length; _i++) {
 20913        var _node = nodes[_i];
 20914        var rcPos = options.position(_node);
 20915  
 20916        if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
 20917          // must have at least row or col def'd
 20918          var _pos = {
 20919            row: rcPos.row,
 20920            col: rcPos.col
 20921          };
 20922  
 20923          if (_pos.col === undefined) {
 20924            // find unused col
 20925            _pos.col = 0;
 20926  
 20927            while (used(_pos.row, _pos.col)) {
 20928              _pos.col++;
 20929            }
 20930          } else if (_pos.row === undefined) {
 20931            // find unused row
 20932            _pos.row = 0;
 20933  
 20934            while (used(_pos.row, _pos.col)) {
 20935              _pos.row++;
 20936            }
 20937          }
 20938  
 20939          id2manPos[_node.id()] = _pos;
 20940          use(_pos.row, _pos.col);
 20941        }
 20942      }
 20943  
 20944      var getPos = function getPos(element, i) {
 20945        var x, y;
 20946  
 20947        if (element.locked() || element.isParent()) {
 20948          return false;
 20949        } // see if we have a manual position set
 20950  
 20951  
 20952        var rcPos = id2manPos[element.id()];
 20953  
 20954        if (rcPos) {
 20955          x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
 20956          y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
 20957        } else {
 20958          // otherwise set automatically
 20959          while (used(row, col)) {
 20960            moveToNextCell();
 20961          }
 20962  
 20963          x = col * cellWidth + cellWidth / 2 + bb.x1;
 20964          y = row * cellHeight + cellHeight / 2 + bb.y1;
 20965          use(row, col);
 20966          moveToNextCell();
 20967        }
 20968  
 20969        return {
 20970          x: x,
 20971          y: y
 20972        };
 20973      };
 20974  
 20975      nodes.layoutPositions(this, options, getPos);
 20976    }
 20977  
 20978    return this; // chaining
 20979  };
 20980  
 20981  var defaults$e = {
 20982    ready: function ready() {},
 20983    // on layoutready
 20984    stop: function stop() {} // on layoutstop
 20985  
 20986  }; // constructor
 20987  // options : object containing layout options
 20988  
 20989  function NullLayout(options) {
 20990    this.options = extend({}, defaults$e, options);
 20991  } // runs the layout
 20992  
 20993  
 20994  NullLayout.prototype.run = function () {
 20995    var options = this.options;
 20996    var eles = options.eles; // elements to consider in the layout
 20997  
 20998    var layout = this; // cy is automatically populated for us in the constructor
 20999    // (disable eslint for next line as this serves as example layout code to external developers)
 21000    // eslint-disable-next-line no-unused-vars
 21001  
 21002    var cy = options.cy;
 21003    layout.emit('layoutstart'); // puts all nodes at (0, 0)
 21004    // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
 21005  
 21006    eles.nodes().positions(function () {
 21007      return {
 21008        x: 0,
 21009        y: 0
 21010      };
 21011    }); // trigger layoutready when each node has had its position set at least once
 21012  
 21013    layout.one('layoutready', options.ready);
 21014    layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
 21015  
 21016    layout.one('layoutstop', options.stop);
 21017    layout.emit('layoutstop');
 21018    return this; // chaining
 21019  }; // called on continuous layouts to stop them before they finish
 21020  
 21021  
 21022  NullLayout.prototype.stop = function () {
 21023    return this; // chaining
 21024  };
 21025  
 21026  var defaults$f = {
 21027    positions: undefined,
 21028    // map of (node id) => (position obj); or function(node){ return somPos; }
 21029    zoom: undefined,
 21030    // the zoom level to set (prob want fit = false if set)
 21031    pan: undefined,
 21032    // the pan level to set (prob want fit = false if set)
 21033    fit: true,
 21034    // whether to fit to viewport
 21035    padding: 30,
 21036    // padding on fit
 21037    animate: false,
 21038    // whether to transition the node positions
 21039    animationDuration: 500,
 21040    // duration of animation in ms if enabled
 21041    animationEasing: undefined,
 21042    // easing of animation if enabled
 21043    animateFilter: function animateFilter(node, i) {
 21044      return true;
 21045    },
 21046    // 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
 21047    ready: undefined,
 21048    // callback on layoutready
 21049    stop: undefined,
 21050    // callback on layoutstop
 21051    transform: function transform(node, position) {
 21052      return position;
 21053    } // transform a given node position. Useful for changing flow direction in discrete layouts
 21054  
 21055  };
 21056  
 21057  function PresetLayout(options) {
 21058    this.options = extend({}, defaults$f, options);
 21059  }
 21060  
 21061  PresetLayout.prototype.run = function () {
 21062    var options = this.options;
 21063    var eles = options.eles;
 21064    var nodes = eles.nodes();
 21065    var posIsFn = fn(options.positions);
 21066  
 21067    function getPosition(node) {
 21068      if (options.positions == null) {
 21069        return copyPosition(node.position());
 21070      }
 21071  
 21072      if (posIsFn) {
 21073        return options.positions(node);
 21074      }
 21075  
 21076      var pos = options.positions[node._private.data.id];
 21077  
 21078      if (pos == null) {
 21079        return null;
 21080      }
 21081  
 21082      return pos;
 21083    }
 21084  
 21085    nodes.layoutPositions(this, options, function (node, i) {
 21086      var position = getPosition(node);
 21087  
 21088      if (node.locked() || position == null) {
 21089        return false;
 21090      }
 21091  
 21092      return position;
 21093    });
 21094    return this; // chaining
 21095  };
 21096  
 21097  var defaults$g = {
 21098    fit: true,
 21099    // whether to fit to viewport
 21100    padding: 30,
 21101    // fit padding
 21102    boundingBox: undefined,
 21103    // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 21104    animate: false,
 21105    // whether to transition the node positions
 21106    animationDuration: 500,
 21107    // duration of animation in ms if enabled
 21108    animationEasing: undefined,
 21109    // easing of animation if enabled
 21110    animateFilter: function animateFilter(node, i) {
 21111      return true;
 21112    },
 21113    // 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
 21114    ready: undefined,
 21115    // callback on layoutready
 21116    stop: undefined,
 21117    // callback on layoutstop
 21118    transform: function transform(node, position) {
 21119      return position;
 21120    } // transform a given node position. Useful for changing flow direction in discrete layouts 
 21121  
 21122  };
 21123  
 21124  function RandomLayout(options) {
 21125    this.options = extend({}, defaults$g, options);
 21126  }
 21127  
 21128  RandomLayout.prototype.run = function () {
 21129    var options = this.options;
 21130    var cy = options.cy;
 21131    var eles = options.eles;
 21132    var nodes = eles.nodes().not(':parent');
 21133    var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
 21134      x1: 0,
 21135      y1: 0,
 21136      w: cy.width(),
 21137      h: cy.height()
 21138    });
 21139  
 21140    var getPos = function getPos(node, i) {
 21141      return {
 21142        x: bb.x1 + Math.round(Math.random() * bb.w),
 21143        y: bb.y1 + Math.round(Math.random() * bb.h)
 21144      };
 21145    };
 21146  
 21147    nodes.layoutPositions(this, options, getPos);
 21148    return this; // chaining
 21149  };
 21150  
 21151  var layout = [{
 21152    name: 'breadthfirst',
 21153    impl: BreadthFirstLayout
 21154  }, {
 21155    name: 'circle',
 21156    impl: CircleLayout
 21157  }, {
 21158    name: 'concentric',
 21159    impl: ConcentricLayout
 21160  }, {
 21161    name: 'cose',
 21162    impl: CoseLayout
 21163  }, {
 21164    name: 'grid',
 21165    impl: GridLayout
 21166  }, {
 21167    name: 'null',
 21168    impl: NullLayout
 21169  }, {
 21170    name: 'preset',
 21171    impl: PresetLayout
 21172  }, {
 21173    name: 'random',
 21174    impl: RandomLayout
 21175  }];
 21176  
 21177  function NullRenderer(options) {
 21178    this.options = options;
 21179    this.notifications = 0; // for testing
 21180  }
 21181  
 21182  var noop$1 = function noop() {};
 21183  
 21184  var throwImgErr = function throwImgErr() {
 21185    throw new Error('A headless instance can not render images');
 21186  };
 21187  
 21188  NullRenderer.prototype = {
 21189    recalculateRenderedStyle: noop$1,
 21190    notify: function notify() {
 21191      this.notifications++;
 21192    },
 21193    init: noop$1,
 21194    isHeadless: function isHeadless() {
 21195      return true;
 21196    },
 21197    png: throwImgErr,
 21198    jpg: throwImgErr
 21199  };
 21200  
 21201  var BRp = {};
 21202  BRp.arrowShapeWidth = 0.3;
 21203  
 21204  BRp.registerArrowShapes = function () {
 21205    var arrowShapes = this.arrowShapes = {};
 21206    var renderer = this; // Contract for arrow shapes:
 21207    // 0, 0 is arrow tip
 21208    // (0, 1) is direction towards node
 21209    // (1, 0) is right
 21210    //
 21211    // functional api:
 21212    // collide: check x, y in shape
 21213    // roughCollide: called before collide, no false negatives
 21214    // draw: draw
 21215    // spacing: dist(arrowTip, nodeBoundary)
 21216    // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
 21217  
 21218    var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
 21219      var x1 = translation.x - size / 2 - padding;
 21220      var x2 = translation.x + size / 2 + padding;
 21221      var y1 = translation.y - size / 2 - padding;
 21222      var y2 = translation.y + size / 2 + padding;
 21223      var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
 21224      return inside;
 21225    };
 21226  
 21227    var transform = function transform(x, y, size, angle, translation) {
 21228      var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
 21229      var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
 21230      var xScaled = xRotated * size;
 21231      var yScaled = yRotated * size;
 21232      var xTranslated = xScaled + translation.x;
 21233      var yTranslated = yScaled + translation.y;
 21234      return {
 21235        x: xTranslated,
 21236        y: yTranslated
 21237      };
 21238    };
 21239  
 21240    var transformPoints = function transformPoints(pts, size, angle, translation) {
 21241      var retPts = [];
 21242  
 21243      for (var i = 0; i < pts.length; i += 2) {
 21244        var x = pts[i];
 21245        var y = pts[i + 1];
 21246        retPts.push(transform(x, y, size, angle, translation));
 21247      }
 21248  
 21249      return retPts;
 21250    };
 21251  
 21252    var pointsToArr = function pointsToArr(pts) {
 21253      var ret = [];
 21254  
 21255      for (var i = 0; i < pts.length; i++) {
 21256        var p = pts[i];
 21257        ret.push(p.x, p.y);
 21258      }
 21259  
 21260      return ret;
 21261    };
 21262  
 21263    var standardGap = function standardGap(edge) {
 21264      return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
 21265    };
 21266  
 21267    var defineArrowShape = function defineArrowShape(name, defn) {
 21268      if (string(defn)) {
 21269        defn = arrowShapes[defn];
 21270      }
 21271  
 21272      arrowShapes[name] = extend({
 21273        name: name,
 21274        points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
 21275        collide: function collide(x, y, size, angle, translation, padding) {
 21276          var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
 21277          var inside = pointInsidePolygonPoints(x, y, points);
 21278          return inside;
 21279        },
 21280        roughCollide: bbCollide,
 21281        draw: function draw(context, size, angle, translation) {
 21282          var points = transformPoints(this.points, size, angle, translation);
 21283          renderer.arrowShapeImpl('polygon')(context, points);
 21284        },
 21285        spacing: function spacing(edge) {
 21286          return 0;
 21287        },
 21288        gap: standardGap
 21289      }, defn);
 21290    };
 21291  
 21292    defineArrowShape('none', {
 21293      collide: falsify,
 21294      roughCollide: falsify,
 21295      draw: noop,
 21296      spacing: zeroify,
 21297      gap: zeroify
 21298    });
 21299    defineArrowShape('triangle', {
 21300      points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
 21301    });
 21302    defineArrowShape('arrow', 'triangle');
 21303    defineArrowShape('triangle-backcurve', {
 21304      points: arrowShapes['triangle'].points,
 21305      controlPoint: [0, -0.15],
 21306      roughCollide: bbCollide,
 21307      draw: function draw(context, size, angle, translation, edgeWidth) {
 21308        var ptsTrans = transformPoints(this.points, size, angle, translation);
 21309        var ctrlPt = this.controlPoint;
 21310        var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
 21311        renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
 21312      },
 21313      gap: function gap(edge) {
 21314        return standardGap(edge) * 0.8;
 21315      }
 21316    });
 21317    defineArrowShape('triangle-tee', {
 21318      points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
 21319      pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
 21320      collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
 21321        var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
 21322        var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
 21323        var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
 21324        return inside;
 21325      },
 21326      draw: function draw(context, size, angle, translation, edgeWidth) {
 21327        var triPts = transformPoints(this.points, size, angle, translation);
 21328        var teePts = transformPoints(this.pointsTee, size, angle, translation);
 21329        renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
 21330      }
 21331    });
 21332    defineArrowShape('triangle-cross', {
 21333      points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
 21334      baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
 21335      -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
 21336      0.15, -0.4],
 21337      crossLinePts: function crossLinePts(size, edgeWidth) {
 21338        // shift points so that the distance between the cross points matches edge width
 21339        var p = this.baseCrossLinePts.slice();
 21340        var shiftFactor = edgeWidth / size;
 21341        var y0 = 3;
 21342        var y1 = 5;
 21343        p[y0] = p[y0] - shiftFactor;
 21344        p[y1] = p[y1] - shiftFactor;
 21345        return p;
 21346      },
 21347      collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
 21348        var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
 21349        var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
 21350        var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
 21351        return inside;
 21352      },
 21353      draw: function draw(context, size, angle, translation, edgeWidth) {
 21354        var triPts = transformPoints(this.points, size, angle, translation);
 21355        var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
 21356        renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
 21357      }
 21358    });
 21359    defineArrowShape('vee', {
 21360      points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
 21361      gap: function gap(edge) {
 21362        return standardGap(edge) * 0.525;
 21363      }
 21364    });
 21365    defineArrowShape('circle', {
 21366      radius: 0.15,
 21367      collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
 21368        var t = translation;
 21369        var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
 21370        return inside;
 21371      },
 21372      draw: function draw(context, size, angle, translation, edgeWidth) {
 21373        renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
 21374      },
 21375      spacing: function spacing(edge) {
 21376        return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
 21377      }
 21378    });
 21379    defineArrowShape('tee', {
 21380      points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
 21381      spacing: function spacing(edge) {
 21382        return 1;
 21383      },
 21384      gap: function gap(edge) {
 21385        return 1;
 21386      }
 21387    });
 21388    defineArrowShape('square', {
 21389      points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
 21390    });
 21391    defineArrowShape('diamond', {
 21392      points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
 21393      gap: function gap(edge) {
 21394        return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
 21395      }
 21396    });
 21397    defineArrowShape('chevron', {
 21398      points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
 21399      gap: function gap(edge) {
 21400        return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
 21401      }
 21402    });
 21403  };
 21404  
 21405  var BRp$1 = {}; // Project mouse
 21406  
 21407  BRp$1.projectIntoViewport = function (clientX, clientY) {
 21408    var cy = this.cy;
 21409    var offsets = this.findContainerClientCoords();
 21410    var offsetLeft = offsets[0];
 21411    var offsetTop = offsets[1];
 21412    var scale = offsets[4];
 21413    var pan = cy.pan();
 21414    var zoom = cy.zoom();
 21415    var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
 21416    var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
 21417    return [x, y];
 21418  };
 21419  
 21420  BRp$1.findContainerClientCoords = function () {
 21421    if (this.containerBB) {
 21422      return this.containerBB;
 21423    }
 21424  
 21425    var container = this.container;
 21426    var rect = container.getBoundingClientRect();
 21427    var style = window$1.getComputedStyle(container);
 21428  
 21429    var styleValue = function styleValue(name) {
 21430      return parseFloat(style.getPropertyValue(name));
 21431    };
 21432  
 21433    var padding = {
 21434      left: styleValue('padding-left'),
 21435      right: styleValue('padding-right'),
 21436      top: styleValue('padding-top'),
 21437      bottom: styleValue('padding-bottom')
 21438    };
 21439    var border = {
 21440      left: styleValue('border-left-width'),
 21441      right: styleValue('border-right-width'),
 21442      top: styleValue('border-top-width'),
 21443      bottom: styleValue('border-bottom-width')
 21444    };
 21445    var clientWidth = container.clientWidth;
 21446    var clientHeight = container.clientHeight;
 21447    var paddingHor = padding.left + padding.right;
 21448    var paddingVer = padding.top + padding.bottom;
 21449    var borderHor = border.left + border.right;
 21450    var scale = rect.width / (clientWidth + borderHor);
 21451    var unscaledW = clientWidth - paddingHor;
 21452    var unscaledH = clientHeight - paddingVer;
 21453    var left = rect.left + padding.left + border.left;
 21454    var top = rect.top + padding.top + border.top;
 21455    return this.containerBB = [left, top, unscaledW, unscaledH, scale];
 21456  };
 21457  
 21458  BRp$1.invalidateContainerClientCoordsCache = function () {
 21459    this.containerBB = null;
 21460  };
 21461  
 21462  BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
 21463    return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
 21464  };
 21465  
 21466  BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
 21467    var self = this;
 21468    var r = this;
 21469    var eles = r.getCachedZSortedEles();
 21470    var near = []; // 1 node max, 1 edge max
 21471  
 21472    var zoom = r.cy.zoom();
 21473    var hasCompounds = r.cy.hasCompoundNodes();
 21474    var edgeThreshold = (isTouch ? 24 : 8) / zoom;
 21475    var nodeThreshold = (isTouch ? 8 : 2) / zoom;
 21476    var labelThreshold = (isTouch ? 8 : 2) / zoom;
 21477    var minSqDist = Infinity;
 21478    var nearEdge;
 21479    var nearNode;
 21480  
 21481    if (interactiveElementsOnly) {
 21482      eles = eles.interactive;
 21483    }
 21484  
 21485    function addEle(ele, sqDist) {
 21486      if (ele.isNode()) {
 21487        if (nearNode) {
 21488          return; // can't replace node
 21489        } else {
 21490          nearNode = ele;
 21491          near.push(ele);
 21492        }
 21493      }
 21494  
 21495      if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
 21496        if (nearEdge) {
 21497          // then replace existing edge
 21498          // can replace only if same z-index
 21499          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) {
 21500            for (var i = 0; i < near.length; i++) {
 21501              if (near[i].isEdge()) {
 21502                near[i] = ele;
 21503                nearEdge = ele;
 21504                minSqDist = sqDist != null ? sqDist : minSqDist;
 21505                break;
 21506              }
 21507            }
 21508          }
 21509        } else {
 21510          near.push(ele);
 21511          nearEdge = ele;
 21512          minSqDist = sqDist != null ? sqDist : minSqDist;
 21513        }
 21514      }
 21515    }
 21516  
 21517    function checkNode(node) {
 21518      var width = node.outerWidth() + 2 * nodeThreshold;
 21519      var height = node.outerHeight() + 2 * nodeThreshold;
 21520      var hw = width / 2;
 21521      var hh = height / 2;
 21522      var pos = node.position();
 21523  
 21524      if (pos.x - hw <= x && x <= pos.x + hw // bb check x
 21525      && pos.y - hh <= y && y <= pos.y + hh // bb check y
 21526      ) {
 21527          var shape = r.nodeShapes[self.getNodeShape(node)];
 21528  
 21529          if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
 21530            addEle(node, 0);
 21531            return true;
 21532          }
 21533        }
 21534    }
 21535  
 21536    function checkEdge(edge) {
 21537      var _p = edge._private;
 21538      var rs = _p.rscratch;
 21539      var styleWidth = edge.pstyle('width').pfValue;
 21540      var scale = edge.pstyle('arrow-scale').value;
 21541      var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
 21542  
 21543      var widthSq = width * width;
 21544      var width2 = width * 2;
 21545      var src = _p.source;
 21546      var tgt = _p.target;
 21547      var sqDist;
 21548  
 21549      if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
 21550        var pts = rs.allpts;
 21551  
 21552        for (var i = 0; i + 3 < pts.length; i += 2) {
 21553          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]))) {
 21554            addEle(edge, sqDist);
 21555            return true;
 21556          }
 21557        }
 21558      } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
 21559        var pts = rs.allpts;
 21560  
 21561        for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
 21562          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]))) {
 21563            addEle(edge, sqDist);
 21564            return true;
 21565          }
 21566        }
 21567      } // if we're close to the edge but didn't hit it, maybe we hit its arrows
 21568  
 21569  
 21570      var src = src || _p.source;
 21571      var tgt = tgt || _p.target;
 21572      var arSize = self.getArrowWidth(styleWidth, scale);
 21573      var arrows = [{
 21574        name: 'source',
 21575        x: rs.arrowStartX,
 21576        y: rs.arrowStartY,
 21577        angle: rs.srcArrowAngle
 21578      }, {
 21579        name: 'target',
 21580        x: rs.arrowEndX,
 21581        y: rs.arrowEndY,
 21582        angle: rs.tgtArrowAngle
 21583      }, {
 21584        name: 'mid-source',
 21585        x: rs.midX,
 21586        y: rs.midY,
 21587        angle: rs.midsrcArrowAngle
 21588      }, {
 21589        name: 'mid-target',
 21590        x: rs.midX,
 21591        y: rs.midY,
 21592        angle: rs.midtgtArrowAngle
 21593      }];
 21594  
 21595      for (var i = 0; i < arrows.length; i++) {
 21596        var ar = arrows[i];
 21597        var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
 21598        var edgeWidth = edge.pstyle('width').pfValue;
 21599  
 21600        if (shape.roughCollide(x, y, arSize, ar.angle, {
 21601          x: ar.x,
 21602          y: ar.y
 21603        }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
 21604          x: ar.x,
 21605          y: ar.y
 21606        }, edgeWidth, edgeThreshold)) {
 21607          addEle(edge);
 21608          return true;
 21609        }
 21610      } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
 21611  
 21612  
 21613      if (hasCompounds && near.length > 0) {
 21614        checkNode(src);
 21615        checkNode(tgt);
 21616      }
 21617    }
 21618  
 21619    function preprop(obj, name, pre) {
 21620      return getPrefixedProperty(obj, name, pre);
 21621    }
 21622  
 21623    function checkLabel(ele, prefix) {
 21624      var _p = ele._private;
 21625      var th = labelThreshold;
 21626      var prefixDash;
 21627  
 21628      if (prefix) {
 21629        prefixDash = prefix + '-';
 21630      } else {
 21631        prefixDash = '';
 21632      }
 21633  
 21634      ele.boundingBox();
 21635      var bb = _p.labelBounds[prefix || 'main'];
 21636      var text = ele.pstyle(prefixDash + 'label').value;
 21637      var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
 21638  
 21639      if (!eventsEnabled || !text) {
 21640        return;
 21641      }
 21642  
 21643      var rstyle = _p.rstyle;
 21644      var lx = preprop(rstyle, 'labelX', prefix);
 21645      var ly = preprop(rstyle, 'labelY', prefix);
 21646      var theta = preprop(_p.rscratch, 'labelAngle', prefix);
 21647      var lx1 = bb.x1 - th;
 21648      var lx2 = bb.x2 + th;
 21649      var ly1 = bb.y1 - th;
 21650      var ly2 = bb.y2 + th;
 21651  
 21652      if (theta) {
 21653        var cos = Math.cos(theta);
 21654        var sin = Math.sin(theta);
 21655  
 21656        var rotate = function rotate(x, y) {
 21657          x = x - lx;
 21658          y = y - ly;
 21659          return {
 21660            x: x * cos - y * sin + lx,
 21661            y: x * sin + y * cos + ly
 21662          };
 21663        };
 21664  
 21665        var px1y1 = rotate(lx1, ly1);
 21666        var px1y2 = rotate(lx1, ly2);
 21667        var px2y1 = rotate(lx2, ly1);
 21668        var px2y2 = rotate(lx2, ly2);
 21669        var points = [px1y1.x, px1y1.y, px2y1.x, px2y1.y, px2y2.x, px2y2.y, px1y2.x, px1y2.y];
 21670  
 21671        if (pointInsidePolygonPoints(x, y, points)) {
 21672          addEle(ele);
 21673          return true;
 21674        }
 21675      } else {
 21676        // do a cheaper bb check
 21677        if (inBoundingBox(bb, x, y)) {
 21678          addEle(ele);
 21679          return true;
 21680        }
 21681      }
 21682    }
 21683  
 21684    for (var i = eles.length - 1; i >= 0; i--) {
 21685      // reverse order for precedence
 21686      var ele = eles[i];
 21687  
 21688      if (ele.isNode()) {
 21689        checkNode(ele) || checkLabel(ele);
 21690      } else {
 21691        // then edge
 21692        checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
 21693      }
 21694    }
 21695  
 21696    return near;
 21697  }; // 'Give me everything from this box'
 21698  
 21699  
 21700  BRp$1.getAllInBox = function (x1, y1, x2, y2) {
 21701    var eles = this.getCachedZSortedEles().interactive;
 21702    var box = [];
 21703    var x1c = Math.min(x1, x2);
 21704    var x2c = Math.max(x1, x2);
 21705    var y1c = Math.min(y1, y2);
 21706    var y2c = Math.max(y1, y2);
 21707    x1 = x1c;
 21708    x2 = x2c;
 21709    y1 = y1c;
 21710    y2 = y2c;
 21711    var boxBb = makeBoundingBox({
 21712      x1: x1,
 21713      y1: y1,
 21714      x2: x2,
 21715      y2: y2
 21716    });
 21717  
 21718    for (var e = 0; e < eles.length; e++) {
 21719      var ele = eles[e];
 21720  
 21721      if (ele.isNode()) {
 21722        var node = ele;
 21723        var nodeBb = node.boundingBox({
 21724          includeNodes: true,
 21725          includeEdges: false,
 21726          includeLabels: false
 21727        });
 21728  
 21729        if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
 21730          box.push(node);
 21731        }
 21732      } else {
 21733        var edge = ele;
 21734        var _p = edge._private;
 21735        var rs = _p.rscratch;
 21736  
 21737        if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
 21738          continue;
 21739        }
 21740  
 21741        if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
 21742          continue;
 21743        }
 21744  
 21745        if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
 21746          var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
 21747          var allInside = true;
 21748  
 21749          for (var i = 0; i < pts.length; i++) {
 21750            if (!pointInBoundingBox(boxBb, pts[i])) {
 21751              allInside = false;
 21752              break;
 21753            }
 21754          }
 21755  
 21756          if (allInside) {
 21757            box.push(edge);
 21758          }
 21759        } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
 21760          box.push(edge);
 21761        }
 21762      }
 21763    }
 21764  
 21765    return box;
 21766  };
 21767  
 21768  var BRp$2 = {};
 21769  
 21770  BRp$2.calculateArrowAngles = function (edge) {
 21771    var rs = edge._private.rscratch;
 21772    var isHaystack = rs.edgeType === 'haystack';
 21773    var isBezier = rs.edgeType === 'bezier';
 21774    var isMultibezier = rs.edgeType === 'multibezier';
 21775    var isSegments = rs.edgeType === 'segments';
 21776    var isCompound = rs.edgeType === 'compound';
 21777    var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
 21778  
 21779    var dispX, dispY;
 21780    var startX, startY, endX, endY, midX, midY;
 21781  
 21782    if (isHaystack) {
 21783      startX = rs.haystackPts[0];
 21784      startY = rs.haystackPts[1];
 21785      endX = rs.haystackPts[2];
 21786      endY = rs.haystackPts[3];
 21787    } else {
 21788      startX = rs.arrowStartX;
 21789      startY = rs.arrowStartY;
 21790      endX = rs.arrowEndX;
 21791      endY = rs.arrowEndY;
 21792    }
 21793  
 21794    midX = rs.midX;
 21795    midY = rs.midY; // source
 21796    //
 21797  
 21798    if (isSegments) {
 21799      dispX = startX - rs.segpts[0];
 21800      dispY = startY - rs.segpts[1];
 21801    } else if (isMultibezier || isCompound || isSelf || isBezier) {
 21802      var pts = rs.allpts;
 21803      var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
 21804      var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
 21805      dispX = startX - bX;
 21806      dispY = startY - bY;
 21807    } else {
 21808      dispX = startX - midX;
 21809      dispY = startY - midY;
 21810    }
 21811  
 21812    rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
 21813    //
 21814  
 21815    var midX = rs.midX;
 21816    var midY = rs.midY;
 21817  
 21818    if (isHaystack) {
 21819      midX = (startX + endX) / 2;
 21820      midY = (startY + endY) / 2;
 21821    }
 21822  
 21823    dispX = endX - startX;
 21824    dispY = endY - startY;
 21825  
 21826    if (isSegments) {
 21827      var pts = rs.allpts;
 21828  
 21829      if (pts.length / 2 % 2 === 0) {
 21830        var i2 = pts.length / 2;
 21831        var i1 = i2 - 2;
 21832        dispX = pts[i2] - pts[i1];
 21833        dispY = pts[i2 + 1] - pts[i1 + 1];
 21834      } else {
 21835        var i2 = pts.length / 2 - 1;
 21836        var i1 = i2 - 2;
 21837        var i3 = i2 + 2;
 21838        dispX = pts[i2] - pts[i1];
 21839        dispY = pts[i2 + 1] - pts[i1 + 1];
 21840      }
 21841    } else if (isMultibezier || isCompound || isSelf) {
 21842      var pts = rs.allpts;
 21843      var cpts = rs.ctrlpts;
 21844      var bp0x, bp0y;
 21845      var bp1x, bp1y;
 21846  
 21847      if (cpts.length / 2 % 2 === 0) {
 21848        var p0 = pts.length / 2 - 1; // startpt
 21849  
 21850        var ic = p0 + 2;
 21851        var p1 = ic + 2;
 21852        bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
 21853        bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
 21854        bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
 21855        bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
 21856      } else {
 21857        var ic = pts.length / 2 - 1; // ctrpt
 21858  
 21859        var p0 = ic - 2; // startpt
 21860  
 21861        var p1 = ic + 2; // endpt
 21862  
 21863        bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
 21864        bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
 21865        bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
 21866        bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
 21867      }
 21868  
 21869      dispX = bp1x - bp0x;
 21870      dispY = bp1y - bp0y;
 21871    }
 21872  
 21873    rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
 21874    rs.midDispX = dispX;
 21875    rs.midDispY = dispY; // mid source
 21876    //
 21877  
 21878    dispX *= -1;
 21879    dispY *= -1;
 21880  
 21881    if (isSegments) {
 21882      var pts = rs.allpts;
 21883  
 21884      if (pts.length / 2 % 2 === 0) ; else {
 21885        var i2 = pts.length / 2 - 1;
 21886        var i3 = i2 + 2;
 21887        dispX = -(pts[i3] - pts[i2]);
 21888        dispY = -(pts[i3 + 1] - pts[i2 + 1]);
 21889      }
 21890    }
 21891  
 21892    rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
 21893    //
 21894  
 21895    if (isSegments) {
 21896      dispX = endX - rs.segpts[rs.segpts.length - 2];
 21897      dispY = endY - rs.segpts[rs.segpts.length - 1];
 21898    } else if (isMultibezier || isCompound || isSelf || isBezier) {
 21899      var pts = rs.allpts;
 21900      var l = pts.length;
 21901      var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
 21902      var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
 21903      dispX = endX - bX;
 21904      dispY = endY - bY;
 21905    } else {
 21906      dispX = endX - midX;
 21907      dispY = endY - midY;
 21908    }
 21909  
 21910    rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
 21911  };
 21912  
 21913  BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
 21914    var cache = this.arrowWidthCache = this.arrowWidthCache || {};
 21915    var cachedVal = cache[edgeWidth + ', ' + scale];
 21916  
 21917    if (cachedVal) {
 21918      return cachedVal;
 21919    }
 21920  
 21921    cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
 21922    cache[edgeWidth + ', ' + scale] = cachedVal;
 21923    return cachedVal;
 21924  };
 21925  
 21926  var BRp$3 = {};
 21927  
 21928  BRp$3.findHaystackPoints = function (edges) {
 21929    for (var i = 0; i < edges.length; i++) {
 21930      var edge = edges[i];
 21931      var _p = edge._private;
 21932      var rs = _p.rscratch;
 21933  
 21934      if (!rs.haystack) {
 21935        var angle = Math.random() * 2 * Math.PI;
 21936        rs.source = {
 21937          x: Math.cos(angle),
 21938          y: Math.sin(angle)
 21939        };
 21940        angle = Math.random() * 2 * Math.PI;
 21941        rs.target = {
 21942          x: Math.cos(angle),
 21943          y: Math.sin(angle)
 21944        };
 21945      }
 21946  
 21947      var src = _p.source;
 21948      var tgt = _p.target;
 21949      var srcPos = src.position();
 21950      var tgtPos = tgt.position();
 21951      var srcW = src.width();
 21952      var tgtW = tgt.width();
 21953      var srcH = src.height();
 21954      var tgtH = tgt.height();
 21955      var radius = edge.pstyle('haystack-radius').value;
 21956      var halfRadius = radius / 2; // b/c have to half width/height
 21957  
 21958      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];
 21959      rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
 21960      rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
 21961  
 21962      rs.edgeType = 'haystack';
 21963      rs.haystack = true;
 21964      this.storeEdgeProjections(edge);
 21965      this.calculateArrowAngles(edge);
 21966      this.recalculateEdgeLabelProjections(edge);
 21967      this.calculateLabelAngles(edge);
 21968    }
 21969  };
 21970  
 21971  BRp$3.findSegmentsPoints = function (edge, pairInfo) {
 21972    // Segments (multiple straight lines)
 21973    var rs = edge._private.rscratch;
 21974    var posPts = pairInfo.posPts,
 21975        intersectionPts = pairInfo.intersectionPts,
 21976        vectorNormInverse = pairInfo.vectorNormInverse;
 21977    var edgeDistances = edge.pstyle('edge-distances').value;
 21978    var segmentWs = edge.pstyle('segment-weights');
 21979    var segmentDs = edge.pstyle('segment-distances');
 21980    var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
 21981    rs.edgeType = 'segments';
 21982    rs.segpts = [];
 21983  
 21984    for (var s = 0; s < segmentsN; s++) {
 21985      var w = segmentWs.pfValue[s];
 21986      var d = segmentDs.pfValue[s];
 21987      var w1 = 1 - w;
 21988      var w2 = w;
 21989      var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
 21990      var adjustedMidpt = {
 21991        x: midptPts.x1 * w1 + midptPts.x2 * w2,
 21992        y: midptPts.y1 * w1 + midptPts.y2 * w2
 21993      };
 21994      rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
 21995    }
 21996  };
 21997  
 21998  BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
 21999    // Self-edge
 22000    var rs = edge._private.rscratch;
 22001    var dirCounts = pairInfo.dirCounts,
 22002        srcPos = pairInfo.srcPos;
 22003    var ctrlptDists = edge.pstyle('control-point-distances');
 22004    var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
 22005    var loopDir = edge.pstyle('loop-direction').pfValue;
 22006    var loopSwp = edge.pstyle('loop-sweep').pfValue;
 22007    var stepSize = edge.pstyle('control-point-step-size').pfValue;
 22008    rs.edgeType = 'self';
 22009    var j = i;
 22010    var loopDist = stepSize;
 22011  
 22012    if (edgeIsUnbundled) {
 22013      j = 0;
 22014      loopDist = ctrlptDist;
 22015    }
 22016  
 22017    var loopAngle = loopDir - Math.PI / 2;
 22018    var outAngle = loopAngle - loopSwp / 2;
 22019    var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
 22020  
 22021    var dc = String(loopDir + '_' + loopSwp);
 22022    j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
 22023    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)];
 22024  };
 22025  
 22026  BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
 22027    // Compound edge
 22028    var rs = edge._private.rscratch;
 22029    rs.edgeType = 'compound';
 22030    var srcPos = pairInfo.srcPos,
 22031        tgtPos = pairInfo.tgtPos,
 22032        srcW = pairInfo.srcW,
 22033        srcH = pairInfo.srcH,
 22034        tgtW = pairInfo.tgtW,
 22035        tgtH = pairInfo.tgtH;
 22036    var stepSize = edge.pstyle('control-point-step-size').pfValue;
 22037    var ctrlptDists = edge.pstyle('control-point-distances');
 22038    var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
 22039    var j = i;
 22040    var loopDist = stepSize;
 22041  
 22042    if (edgeIsUnbundled) {
 22043      j = 0;
 22044      loopDist = ctrlptDist;
 22045    }
 22046  
 22047    var loopW = 50;
 22048    var loopaPos = {
 22049      x: srcPos.x - srcW / 2,
 22050      y: srcPos.y - srcH / 2
 22051    };
 22052    var loopbPos = {
 22053      x: tgtPos.x - tgtW / 2,
 22054      y: tgtPos.y - tgtH / 2
 22055    };
 22056    var loopPos = {
 22057      x: Math.min(loopaPos.x, loopbPos.x),
 22058      y: Math.min(loopaPos.y, loopbPos.y)
 22059    }; // avoids cases with impossible beziers
 22060  
 22061    var minCompoundStretch = 0.5;
 22062    var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
 22063    var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
 22064    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];
 22065  };
 22066  
 22067  BRp$3.findStraightEdgePoints = function (edge) {
 22068    // Straight edge within bundle
 22069    edge._private.rscratch.edgeType = 'straight';
 22070  };
 22071  
 22072  BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
 22073    var rs = edge._private.rscratch;
 22074    var vectorNormInverse = pairInfo.vectorNormInverse,
 22075        posPts = pairInfo.posPts,
 22076        intersectionPts = pairInfo.intersectionPts;
 22077    var edgeDistances = edge.pstyle('edge-distances').value;
 22078    var stepSize = edge.pstyle('control-point-step-size').pfValue;
 22079    var ctrlptDists = edge.pstyle('control-point-distances');
 22080    var ctrlptWs = edge.pstyle('control-point-weights');
 22081    var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
 22082    var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
 22083    var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
 22084  
 22085    var multi = edgeIsUnbundled;
 22086    rs.edgeType = multi ? 'multibezier' : 'bezier';
 22087    rs.ctrlpts = [];
 22088  
 22089    for (var b = 0; b < bezierN; b++) {
 22090      var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
 22091      var manctrlptDist = void 0;
 22092      var sign = signum(normctrlptDist);
 22093  
 22094      if (multi) {
 22095        ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
 22096  
 22097        ctrlptWeight = ctrlptWs.value[b];
 22098      }
 22099  
 22100      if (edgeIsUnbundled) {
 22101        // multi or single unbundled
 22102        manctrlptDist = ctrlptDist;
 22103      } else {
 22104        manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
 22105      }
 22106  
 22107      var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
 22108      var w1 = 1 - ctrlptWeight;
 22109      var w2 = ctrlptWeight;
 22110      var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
 22111      var adjustedMidpt = {
 22112        x: midptPts.x1 * w1 + midptPts.x2 * w2,
 22113        y: midptPts.y1 * w1 + midptPts.y2 * w2
 22114      };
 22115      rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
 22116    }
 22117  };
 22118  
 22119  BRp$3.findTaxiPoints = function (edge, pairInfo) {
 22120    // Taxicab geometry with two turns maximum
 22121    var rs = edge._private.rscratch;
 22122    rs.edgeType = 'segments';
 22123    var VERTICAL = 'vertical';
 22124    var HORIZONTAL = 'horizontal';
 22125    var LEFTWARD = 'leftward';
 22126    var RIGHTWARD = 'rightward';
 22127    var DOWNWARD = 'downward';
 22128    var UPWARD = 'upward';
 22129    var AUTO = 'auto';
 22130    var posPts = pairInfo.posPts,
 22131        srcW = pairInfo.srcW,
 22132        srcH = pairInfo.srcH,
 22133        tgtW = pairInfo.tgtW,
 22134        tgtH = pairInfo.tgtH;
 22135    var edgeDistances = edge.pstyle('edge-distances').value;
 22136    var dIncludesNodeBody = edgeDistances !== 'node-position';
 22137    var taxiDir = edge.pstyle('taxi-direction').value;
 22138    var rawTaxiDir = taxiDir; // unprocessed value
 22139  
 22140    var taxiTurn = edge.pstyle('taxi-turn');
 22141    var taxiTurnPfVal = taxiTurn.pfValue;
 22142    var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
 22143    var turnIsPercent = taxiTurn.units === '%';
 22144    var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
 22145    var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
 22146    var pdx = posPts.x2 - posPts.x1;
 22147    var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
 22148  
 22149    var subDWH = function subDWH(dxy, dwh) {
 22150      if (dxy > 0) {
 22151        return Math.max(dxy - dwh, 0);
 22152      } else {
 22153        return Math.min(dxy + dwh, 0);
 22154      }
 22155    };
 22156  
 22157    var dx = subDWH(pdx, dw);
 22158    var dy = subDWH(pdy, dh);
 22159    var isExplicitDir = false;
 22160  
 22161    if (taxiDir === AUTO) {
 22162      taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
 22163    } else if (taxiDir === UPWARD || taxiDir === DOWNWARD) {
 22164      taxiDir = VERTICAL;
 22165      isExplicitDir = true;
 22166    } else if (taxiDir === LEFTWARD || taxiDir === RIGHTWARD) {
 22167      taxiDir = HORIZONTAL;
 22168      isExplicitDir = true;
 22169    }
 22170  
 22171    var isVert = taxiDir === VERTICAL;
 22172    var l = isVert ? dy : dx;
 22173    var pl = isVert ? pdy : pdx;
 22174    var sgnL = signum(pl);
 22175    var forcedDir = false;
 22176  
 22177    if (!(isExplicitDir && turnIsPercent) // forcing in this case would cause weird growing in the opposite direction
 22178    && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
 22179      sgnL *= -1;
 22180      l = sgnL * Math.abs(l);
 22181      forcedDir = true;
 22182    }
 22183  
 22184    var d = turnIsPercent ? taxiTurnPfVal * l : taxiTurnPfVal * sgnL;
 22185  
 22186    var getIsTooClose = function getIsTooClose(d) {
 22187      return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
 22188    };
 22189  
 22190    var isTooCloseSrc = getIsTooClose(d);
 22191    var isTooCloseTgt = getIsTooClose(l - d);
 22192    var isTooClose = isTooCloseSrc || isTooCloseTgt;
 22193  
 22194    if (isTooClose && !forcedDir) {
 22195      // non-ideal routing
 22196      if (isVert) {
 22197        // vertical fallbacks
 22198        var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
 22199        var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
 22200  
 22201        if (lShapeInsideSrc) {
 22202          // horizontal Z-shape (direction not respected)
 22203          var x = (posPts.x1 + posPts.x2) / 2;
 22204          var y1 = posPts.y1,
 22205              y2 = posPts.y2;
 22206          rs.segpts = [x, y1, x, y2];
 22207        } else if (lShapeInsideTgt) {
 22208          // vertical Z-shape (distance not respected)
 22209          var y = (posPts.y1 + posPts.y2) / 2;
 22210          var x1 = posPts.x1,
 22211              x2 = posPts.x2;
 22212          rs.segpts = [x1, y, x2, y];
 22213        } else {
 22214          // L-shape fallback (turn distance not respected, but works well with tree siblings)
 22215          rs.segpts = [posPts.x1, posPts.y2];
 22216        }
 22217      } else {
 22218        // horizontal fallbacks
 22219        var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
 22220  
 22221        var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
 22222  
 22223        if (_lShapeInsideSrc) {
 22224          // vertical Z-shape (direction not respected)
 22225          var _y = (posPts.y1 + posPts.y2) / 2;
 22226  
 22227          var _x = posPts.x1,
 22228              _x2 = posPts.x2;
 22229          rs.segpts = [_x, _y, _x2, _y];
 22230        } else if (_lShapeInsideTgt) {
 22231          // horizontal Z-shape (turn distance not respected)
 22232          var _x3 = (posPts.x1 + posPts.x2) / 2;
 22233  
 22234          var _y2 = posPts.y1,
 22235              _y3 = posPts.y2;
 22236          rs.segpts = [_x3, _y2, _x3, _y3];
 22237        } else {
 22238          // L-shape (turn distance not respected, but works well for tree siblings)
 22239          rs.segpts = [posPts.x2, posPts.y1];
 22240        }
 22241      }
 22242    } else {
 22243      // ideal routing
 22244      if (isVert) {
 22245        var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
 22246  
 22247        var _x4 = posPts.x1,
 22248            _x5 = posPts.x2;
 22249        rs.segpts = [_x4, _y4, _x5, _y4];
 22250      } else {
 22251        // horizontal
 22252        var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
 22253  
 22254        var _y5 = posPts.y1,
 22255            _y6 = posPts.y2;
 22256        rs.segpts = [_x6, _y5, _x6, _y6];
 22257      }
 22258    }
 22259  };
 22260  
 22261  BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
 22262    var rs = edge._private.rscratch; // can only correct beziers for now...
 22263  
 22264    if (rs.edgeType === 'bezier') {
 22265      var srcPos = pairInfo.srcPos,
 22266          tgtPos = pairInfo.tgtPos,
 22267          srcW = pairInfo.srcW,
 22268          srcH = pairInfo.srcH,
 22269          tgtW = pairInfo.tgtW,
 22270          tgtH = pairInfo.tgtH,
 22271          srcShape = pairInfo.srcShape,
 22272          tgtShape = pairInfo.tgtShape;
 22273      var badStart = !number(rs.startX) || !number(rs.startY);
 22274      var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
 22275      var badEnd = !number(rs.endX) || !number(rs.endY);
 22276      var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
 22277      var minCpADistFactor = 3;
 22278      var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
 22279      var minCpADist = minCpADistFactor * arrowW;
 22280      var startACpDist = dist({
 22281        x: rs.ctrlpts[0],
 22282        y: rs.ctrlpts[1]
 22283      }, {
 22284        x: rs.startX,
 22285        y: rs.startY
 22286      });
 22287      var closeStartACp = startACpDist < minCpADist;
 22288      var endACpDist = dist({
 22289        x: rs.ctrlpts[0],
 22290        y: rs.ctrlpts[1]
 22291      }, {
 22292        x: rs.endX,
 22293        y: rs.endY
 22294      });
 22295      var closeEndACp = endACpDist < minCpADist;
 22296      var overlapping = false;
 22297  
 22298      if (badStart || badAStart || closeStartACp) {
 22299        overlapping = true; // project control point along line from src centre to outside the src shape
 22300        // (otherwise intersection will yield nothing)
 22301  
 22302        var cpD = {
 22303          // delta
 22304          x: rs.ctrlpts[0] - srcPos.x,
 22305          y: rs.ctrlpts[1] - srcPos.y
 22306        };
 22307        var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
 22308  
 22309        var cpM = {
 22310          // normalised delta
 22311          x: cpD.x / cpL,
 22312          y: cpD.y / cpL
 22313        };
 22314        var radius = Math.max(srcW, srcH);
 22315        var cpProj = {
 22316          // *2 radius guarantees outside shape
 22317          x: rs.ctrlpts[0] + cpM.x * 2 * radius,
 22318          y: rs.ctrlpts[1] + cpM.y * 2 * radius
 22319        };
 22320        var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
 22321  
 22322        if (closeStartACp) {
 22323          rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
 22324          rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
 22325        } else {
 22326          rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
 22327          rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
 22328        }
 22329      }
 22330  
 22331      if (badEnd || badAEnd || closeEndACp) {
 22332        overlapping = true; // project control point along line from tgt centre to outside the tgt shape
 22333        // (otherwise intersection will yield nothing)
 22334  
 22335        var _cpD = {
 22336          // delta
 22337          x: rs.ctrlpts[0] - tgtPos.x,
 22338          y: rs.ctrlpts[1] - tgtPos.y
 22339        };
 22340  
 22341        var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
 22342  
 22343  
 22344        var _cpM = {
 22345          // normalised delta
 22346          x: _cpD.x / _cpL,
 22347          y: _cpD.y / _cpL
 22348        };
 22349  
 22350        var _radius = Math.max(srcW, srcH);
 22351  
 22352        var _cpProj = {
 22353          // *2 radius guarantees outside shape
 22354          x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
 22355          y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
 22356        };
 22357        var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
 22358  
 22359        if (closeEndACp) {
 22360          rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
 22361          rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
 22362        } else {
 22363          rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
 22364          rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
 22365        }
 22366      }
 22367  
 22368      if (overlapping) {
 22369        // recalc endpts
 22370        this.findEndpoints(edge);
 22371      }
 22372    }
 22373  };
 22374  
 22375  BRp$3.storeAllpts = function (edge) {
 22376    var rs = edge._private.rscratch;
 22377  
 22378    if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
 22379      rs.allpts = [];
 22380      rs.allpts.push(rs.startX, rs.startY);
 22381  
 22382      for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
 22383        // ctrl pt itself
 22384        rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
 22385  
 22386        if (b + 3 < rs.ctrlpts.length) {
 22387          rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
 22388        }
 22389      }
 22390  
 22391      rs.allpts.push(rs.endX, rs.endY);
 22392      var m, mt;
 22393  
 22394      if (rs.ctrlpts.length / 2 % 2 === 0) {
 22395        m = rs.allpts.length / 2 - 1;
 22396        rs.midX = rs.allpts[m];
 22397        rs.midY = rs.allpts[m + 1];
 22398      } else {
 22399        m = rs.allpts.length / 2 - 3;
 22400        mt = 0.5;
 22401        rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
 22402        rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
 22403      }
 22404    } else if (rs.edgeType === 'straight') {
 22405      // need to calc these after endpts
 22406      rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
 22407  
 22408      rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
 22409      rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
 22410    } else if (rs.edgeType === 'segments') {
 22411      rs.allpts = [];
 22412      rs.allpts.push(rs.startX, rs.startY);
 22413      rs.allpts.push.apply(rs.allpts, rs.segpts);
 22414      rs.allpts.push(rs.endX, rs.endY);
 22415  
 22416      if (rs.segpts.length % 4 === 0) {
 22417        var i2 = rs.segpts.length / 2;
 22418        var i1 = i2 - 2;
 22419        rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
 22420        rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
 22421      } else {
 22422        var _i = rs.segpts.length / 2 - 1;
 22423  
 22424        rs.midX = rs.segpts[_i];
 22425        rs.midY = rs.segpts[_i + 1];
 22426      }
 22427    }
 22428  };
 22429  
 22430  BRp$3.checkForInvalidEdgeWarning = function (edge) {
 22431    var rs = edge[0]._private.rscratch;
 22432  
 22433    if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
 22434      rs.loggedErr = false;
 22435    } else {
 22436      if (!rs.loggedErr) {
 22437        rs.loggedErr = true;
 22438        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.');
 22439      }
 22440    }
 22441  };
 22442  
 22443  BRp$3.findEdgeControlPoints = function (edges) {
 22444    var _this = this;
 22445  
 22446    if (!edges || edges.length === 0) {
 22447      return;
 22448    }
 22449  
 22450    var r = this;
 22451    var cy = r.cy;
 22452    var hasCompounds = cy.hasCompoundNodes();
 22453    var hashTable = {
 22454      map: new Map$1(),
 22455      get: function get(pairId) {
 22456        var map2 = this.map.get(pairId[0]);
 22457  
 22458        if (map2 != null) {
 22459          return map2.get(pairId[1]);
 22460        } else {
 22461          return null;
 22462        }
 22463      },
 22464      set: function set(pairId, val) {
 22465        var map2 = this.map.get(pairId[0]);
 22466  
 22467        if (map2 == null) {
 22468          map2 = new Map$1();
 22469          this.map.set(pairId[0], map2);
 22470        }
 22471  
 22472        map2.set(pairId[1], val);
 22473      }
 22474    };
 22475    var pairIds = [];
 22476    var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
 22477  
 22478    for (var i = 0; i < edges.length; i++) {
 22479      var edge = edges[i];
 22480      var _p = edge._private;
 22481      var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
 22482      // they shouldn't take up space
 22483  
 22484      if (edge.removed() || !edge.takesUpSpace()) {
 22485        continue;
 22486      }
 22487  
 22488      if (curveStyle === 'haystack') {
 22489        haystackEdges.push(edge);
 22490        continue;
 22491      }
 22492  
 22493      var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'taxi';
 22494      var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
 22495      var src = _p.source;
 22496      var tgt = _p.target;
 22497      var srcIndex = src.poolIndex();
 22498      var tgtIndex = tgt.poolIndex();
 22499      var pairId = [srcIndex, tgtIndex].sort();
 22500      var tableEntry = hashTable.get(pairId);
 22501  
 22502      if (tableEntry == null) {
 22503        tableEntry = {
 22504          eles: []
 22505        };
 22506        hashTable.set(pairId, tableEntry);
 22507        pairIds.push(pairId);
 22508      }
 22509  
 22510      tableEntry.eles.push(edge);
 22511  
 22512      if (edgeIsUnbundled) {
 22513        tableEntry.hasUnbundled = true;
 22514      }
 22515  
 22516      if (edgeIsBezier) {
 22517        tableEntry.hasBezier = true;
 22518      }
 22519    } // for each pair (src, tgt), create the ctrl pts
 22520    // Nested for loop is OK; total number of iterations for both loops = edgeCount
 22521  
 22522  
 22523    var _loop = function _loop(p) {
 22524      var pairId = pairIds[p];
 22525      var pairInfo = hashTable.get(pairId);
 22526      var swappedpairInfo = void 0;
 22527  
 22528      if (!pairInfo.hasUnbundled) {
 22529        var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
 22530          return e.isBundledBezier();
 22531        });
 22532        clearArray(pairInfo.eles);
 22533        pllEdges.forEach(function (edge) {
 22534          return pairInfo.eles.push(edge);
 22535        }); // for each pair id, the edges should be sorted by index
 22536  
 22537        pairInfo.eles.sort(function (edge1, edge2) {
 22538          return edge1.poolIndex() - edge2.poolIndex();
 22539        });
 22540      }
 22541  
 22542      var firstEdge = pairInfo.eles[0];
 22543      var src = firstEdge.source();
 22544      var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
 22545  
 22546      if (src.poolIndex() > tgt.poolIndex()) {
 22547        var temp = src;
 22548        src = tgt;
 22549        tgt = temp;
 22550      }
 22551  
 22552      var srcPos = pairInfo.srcPos = src.position();
 22553      var tgtPos = pairInfo.tgtPos = tgt.position();
 22554      var srcW = pairInfo.srcW = src.outerWidth();
 22555      var srcH = pairInfo.srcH = src.outerHeight();
 22556      var tgtW = pairInfo.tgtW = tgt.outerWidth();
 22557      var tgtH = pairInfo.tgtH = tgt.outerHeight();
 22558  
 22559      var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
 22560  
 22561      var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
 22562  
 22563      pairInfo.dirCounts = {
 22564        'north': 0,
 22565        'west': 0,
 22566        'south': 0,
 22567        'east': 0,
 22568        'northwest': 0,
 22569        'southwest': 0,
 22570        'northeast': 0,
 22571        'southeast': 0
 22572      };
 22573  
 22574      for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
 22575        var _edge = pairInfo.eles[_i2];
 22576        var rs = _edge[0]._private.rscratch;
 22577  
 22578        var _curveStyle = _edge.pstyle('curve-style').value;
 22579  
 22580        var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
 22581  
 22582  
 22583        var edgeIsSwapped = !src.same(_edge.source());
 22584  
 22585        if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
 22586          pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
 22587  
 22588          var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
 22589          var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
 22590  
 22591          var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
 22592          var tgtIntn = pairInfo.tgtIntn = tgtOutside;
 22593          var intersectionPts = pairInfo.intersectionPts = {
 22594            x1: srcOutside[0],
 22595            x2: tgtOutside[0],
 22596            y1: srcOutside[1],
 22597            y2: tgtOutside[1]
 22598          };
 22599          var posPts = pairInfo.posPts = {
 22600            x1: srcPos.x,
 22601            x2: tgtPos.x,
 22602            y1: srcPos.y,
 22603            y2: tgtPos.y
 22604          };
 22605          var dy = tgtOutside[1] - srcOutside[1];
 22606          var dx = tgtOutside[0] - srcOutside[0];
 22607          var l = Math.sqrt(dx * dx + dy * dy);
 22608          var vector = pairInfo.vector = {
 22609            x: dx,
 22610            y: dy
 22611          };
 22612          var vectorNorm = pairInfo.vectorNorm = {
 22613            x: vector.x / l,
 22614            y: vector.y / l
 22615          };
 22616          var vectorNormInverse = {
 22617            x: -vectorNorm.y,
 22618            y: vectorNorm.x
 22619          }; // if node shapes overlap, then no ctrl pts to draw
 22620  
 22621          pairInfo.nodesOverlap = !number(l) || tgtShape.checkPoint(srcOutside[0], srcOutside[1], 0, tgtW, tgtH, tgtPos.x, tgtPos.y) || srcShape.checkPoint(tgtOutside[0], tgtOutside[1], 0, srcW, srcH, srcPos.x, srcPos.y);
 22622          pairInfo.vectorNormInverse = vectorNormInverse;
 22623          swappedpairInfo = {
 22624            nodesOverlap: pairInfo.nodesOverlap,
 22625            dirCounts: pairInfo.dirCounts,
 22626            calculatedIntersection: true,
 22627            hasBezier: pairInfo.hasBezier,
 22628            hasUnbundled: pairInfo.hasUnbundled,
 22629            eles: pairInfo.eles,
 22630            srcPos: tgtPos,
 22631            tgtPos: srcPos,
 22632            srcW: tgtW,
 22633            srcH: tgtH,
 22634            tgtW: srcW,
 22635            tgtH: srcH,
 22636            srcIntn: tgtIntn,
 22637            tgtIntn: srcIntn,
 22638            srcShape: tgtShape,
 22639            tgtShape: srcShape,
 22640            posPts: {
 22641              x1: posPts.x2,
 22642              y1: posPts.y2,
 22643              x2: posPts.x1,
 22644              y2: posPts.y1
 22645            },
 22646            intersectionPts: {
 22647              x1: intersectionPts.x2,
 22648              y1: intersectionPts.y2,
 22649              x2: intersectionPts.x1,
 22650              y2: intersectionPts.y1
 22651            },
 22652            vector: {
 22653              x: -vector.x,
 22654              y: -vector.y
 22655            },
 22656            vectorNorm: {
 22657              x: -vectorNorm.x,
 22658              y: -vectorNorm.y
 22659            },
 22660            vectorNormInverse: {
 22661              x: -vectorNormInverse.x,
 22662              y: -vectorNormInverse.y
 22663            }
 22664          };
 22665        }
 22666  
 22667        var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
 22668        rs.nodesOverlap = passedPairInfo.nodesOverlap;
 22669        rs.srcIntn = passedPairInfo.srcIntn;
 22670        rs.tgtIntn = passedPairInfo.tgtIntn;
 22671  
 22672        if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
 22673          _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
 22674        } else if (src === tgt) {
 22675          _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
 22676        } else if (_curveStyle === 'segments') {
 22677          _this.findSegmentsPoints(_edge, passedPairInfo);
 22678        } else if (_curveStyle === 'taxi') {
 22679          _this.findTaxiPoints(_edge, passedPairInfo);
 22680        } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
 22681          _this.findStraightEdgePoints(_edge);
 22682        } else {
 22683          _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
 22684        }
 22685  
 22686        _this.findEndpoints(_edge);
 22687  
 22688        _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
 22689  
 22690        _this.checkForInvalidEdgeWarning(_edge);
 22691  
 22692        _this.storeAllpts(_edge);
 22693  
 22694        _this.storeEdgeProjections(_edge);
 22695  
 22696        _this.calculateArrowAngles(_edge);
 22697  
 22698        _this.recalculateEdgeLabelProjections(_edge);
 22699  
 22700        _this.calculateLabelAngles(_edge);
 22701      } // for pair edges
 22702  
 22703    };
 22704  
 22705    for (var p = 0; p < pairIds.length; p++) {
 22706      _loop(p);
 22707    } // for pair ids
 22708    // haystacks avoid the expense of pairInfo stuff (intersections etc.)
 22709  
 22710  
 22711    this.findHaystackPoints(haystackEdges);
 22712  };
 22713  
 22714  function getPts(pts) {
 22715    var retPts = [];
 22716  
 22717    if (pts == null) {
 22718      return;
 22719    }
 22720  
 22721    for (var i = 0; i < pts.length; i += 2) {
 22722      var x = pts[i];
 22723      var y = pts[i + 1];
 22724      retPts.push({
 22725        x: x,
 22726        y: y
 22727      });
 22728    }
 22729  
 22730    return retPts;
 22731  }
 22732  
 22733  BRp$3.getSegmentPoints = function (edge) {
 22734    var rs = edge[0]._private.rscratch;
 22735    var type = rs.edgeType;
 22736  
 22737    if (type === 'segments') {
 22738      this.recalculateRenderedStyle(edge);
 22739      return getPts(rs.segpts);
 22740    }
 22741  };
 22742  
 22743  BRp$3.getControlPoints = function (edge) {
 22744    var rs = edge[0]._private.rscratch;
 22745    var type = rs.edgeType;
 22746  
 22747    if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
 22748      this.recalculateRenderedStyle(edge);
 22749      return getPts(rs.ctrlpts);
 22750    }
 22751  };
 22752  
 22753  BRp$3.getEdgeMidpoint = function (edge) {
 22754    var rs = edge[0]._private.rscratch;
 22755    this.recalculateRenderedStyle(edge);
 22756    return {
 22757      x: rs.midX,
 22758      y: rs.midY
 22759    };
 22760  };
 22761  
 22762  var BRp$4 = {};
 22763  
 22764  BRp$4.manualEndptToPx = function (node, prop) {
 22765    var r = this;
 22766    var npos = node.position();
 22767    var w = node.outerWidth();
 22768    var h = node.outerHeight();
 22769  
 22770    if (prop.value.length === 2) {
 22771      var p = [prop.pfValue[0], prop.pfValue[1]];
 22772  
 22773      if (prop.units[0] === '%') {
 22774        p[0] = p[0] * w;
 22775      }
 22776  
 22777      if (prop.units[1] === '%') {
 22778        p[1] = p[1] * h;
 22779      }
 22780  
 22781      p[0] += npos.x;
 22782      p[1] += npos.y;
 22783      return p;
 22784    } else {
 22785      var angle = prop.pfValue[0];
 22786      angle = -Math.PI / 2 + angle; // start at 12 o'clock
 22787  
 22788      var l = 2 * Math.max(w, h);
 22789      var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
 22790      return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
 22791    }
 22792  };
 22793  
 22794  BRp$4.findEndpoints = function (edge) {
 22795    var r = this;
 22796    var intersect;
 22797    var source = edge.source()[0];
 22798    var target = edge.target()[0];
 22799    var srcPos = source.position();
 22800    var tgtPos = target.position();
 22801    var tgtArShape = edge.pstyle('target-arrow-shape').value;
 22802    var srcArShape = edge.pstyle('source-arrow-shape').value;
 22803    var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
 22804    var srcDist = edge.pstyle('source-distance-from-node').pfValue;
 22805    var curveStyle = edge.pstyle('curve-style').value;
 22806    var rs = edge._private.rscratch;
 22807    var et = rs.edgeType;
 22808    var taxi = curveStyle === 'taxi';
 22809    var self = et === 'self' || et === 'compound';
 22810    var bezier = et === 'bezier' || et === 'multibezier' || self;
 22811    var multi = et !== 'bezier';
 22812    var lines = et === 'straight' || et === 'segments';
 22813    var segments = et === 'segments';
 22814    var hasEndpts = bezier || multi || lines;
 22815    var overrideEndpts = self || taxi;
 22816    var srcManEndpt = edge.pstyle('source-endpoint');
 22817    var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
 22818    var tgtManEndpt = edge.pstyle('target-endpoint');
 22819    var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
 22820    rs.srcManEndpt = srcManEndpt;
 22821    rs.tgtManEndpt = tgtManEndpt;
 22822    var p1; // last known point of edge on target side
 22823  
 22824    var p2; // last known point of edge on source side
 22825  
 22826    var p1_i; // point to intersect with target shape
 22827  
 22828    var p2_i; // point to intersect with source shape
 22829  
 22830    if (bezier) {
 22831      var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
 22832      var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
 22833      p1 = cpEnd;
 22834      p2 = cpStart;
 22835    } else if (lines) {
 22836      var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
 22837      var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
 22838      p1 = tgtArrowFromPt;
 22839      p2 = srcArrowFromPt;
 22840    }
 22841  
 22842    if (tgtManEndptVal === 'inside-to-node') {
 22843      intersect = [tgtPos.x, tgtPos.y];
 22844    } else if (tgtManEndpt.units) {
 22845      intersect = this.manualEndptToPx(target, tgtManEndpt);
 22846    } else if (tgtManEndptVal === 'outside-to-line') {
 22847      intersect = rs.tgtIntn; // use cached value from ctrlpt calc
 22848    } else {
 22849      if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
 22850        p1_i = p1;
 22851      } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
 22852        p1_i = [srcPos.x, srcPos.y];
 22853      }
 22854  
 22855      intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
 22856  
 22857      if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
 22858        var trs = target._private.rscratch;
 22859        var lw = trs.labelWidth;
 22860        var lh = trs.labelHeight;
 22861        var lx = trs.labelX;
 22862        var ly = trs.labelY;
 22863        var lw2 = lw / 2;
 22864        var lh2 = lh / 2;
 22865        var va = target.pstyle('text-valign').value;
 22866  
 22867        if (va === 'top') {
 22868          ly -= lh2;
 22869        } else if (va === 'bottom') {
 22870          ly += lh2;
 22871        }
 22872  
 22873        var ha = target.pstyle('text-halign').value;
 22874  
 22875        if (ha === 'left') {
 22876          lx -= lw2;
 22877        } else if (ha === 'right') {
 22878          lx += lw2;
 22879        }
 22880  
 22881        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);
 22882  
 22883        if (labelIntersect.length > 0) {
 22884          var refPt = srcPos;
 22885          var intSqdist = sqdist(refPt, array2point(intersect));
 22886          var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
 22887          var minSqDist = intSqdist;
 22888  
 22889          if (labIntSqdist < intSqdist) {
 22890            intersect = labelIntersect;
 22891            minSqDist = labIntSqdist;
 22892          }
 22893  
 22894          if (labelIntersect.length > 2) {
 22895            var labInt2SqDist = sqdist(refPt, {
 22896              x: labelIntersect[2],
 22897              y: labelIntersect[3]
 22898            });
 22899  
 22900            if (labInt2SqDist < minSqDist) {
 22901              intersect = [labelIntersect[2], labelIntersect[3]];
 22902            }
 22903          }
 22904        }
 22905      }
 22906    }
 22907  
 22908    var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
 22909    var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
 22910    rs.endX = edgeEnd[0];
 22911    rs.endY = edgeEnd[1];
 22912    rs.arrowEndX = arrowEnd[0];
 22913    rs.arrowEndY = arrowEnd[1];
 22914  
 22915    if (srcManEndptVal === 'inside-to-node') {
 22916      intersect = [srcPos.x, srcPos.y];
 22917    } else if (srcManEndpt.units) {
 22918      intersect = this.manualEndptToPx(source, srcManEndpt);
 22919    } else if (srcManEndptVal === 'outside-to-line') {
 22920      intersect = rs.srcIntn; // use cached value from ctrlpt calc
 22921    } else {
 22922      if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
 22923        p2_i = p2;
 22924      } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
 22925        p2_i = [tgtPos.x, tgtPos.y];
 22926      }
 22927  
 22928      intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
 22929  
 22930      if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
 22931        var srs = source._private.rscratch;
 22932        var _lw = srs.labelWidth;
 22933        var _lh = srs.labelHeight;
 22934        var _lx = srs.labelX;
 22935        var _ly = srs.labelY;
 22936  
 22937        var _lw2 = _lw / 2;
 22938  
 22939        var _lh2 = _lh / 2;
 22940  
 22941        var _va = source.pstyle('text-valign').value;
 22942  
 22943        if (_va === 'top') {
 22944          _ly -= _lh2;
 22945        } else if (_va === 'bottom') {
 22946          _ly += _lh2;
 22947        }
 22948  
 22949        var _ha = source.pstyle('text-halign').value;
 22950  
 22951        if (_ha === 'left') {
 22952          _lx -= _lw2;
 22953        } else if (_ha === 'right') {
 22954          _lx += _lw2;
 22955        }
 22956  
 22957        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);
 22958  
 22959        if (_labelIntersect.length > 0) {
 22960          var _refPt = tgtPos;
 22961  
 22962          var _intSqdist = sqdist(_refPt, array2point(intersect));
 22963  
 22964          var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
 22965  
 22966          var _minSqDist = _intSqdist;
 22967  
 22968          if (_labIntSqdist < _intSqdist) {
 22969            intersect = [_labelIntersect[0], _labelIntersect[1]];
 22970            _minSqDist = _labIntSqdist;
 22971          }
 22972  
 22973          if (_labelIntersect.length > 2) {
 22974            var _labInt2SqDist = sqdist(_refPt, {
 22975              x: _labelIntersect[2],
 22976              y: _labelIntersect[3]
 22977            });
 22978  
 22979            if (_labInt2SqDist < _minSqDist) {
 22980              intersect = [_labelIntersect[2], _labelIntersect[3]];
 22981            }
 22982          }
 22983        }
 22984      }
 22985    }
 22986  
 22987    var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
 22988    var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
 22989    rs.startX = edgeStart[0];
 22990    rs.startY = edgeStart[1];
 22991    rs.arrowStartX = arrowStart[0];
 22992    rs.arrowStartY = arrowStart[1];
 22993  
 22994    if (hasEndpts) {
 22995      if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
 22996        rs.badLine = true;
 22997      } else {
 22998        rs.badLine = false;
 22999      }
 23000    }
 23001  };
 23002  
 23003  BRp$4.getSourceEndpoint = function (edge) {
 23004    var rs = edge[0]._private.rscratch;
 23005    this.recalculateRenderedStyle(edge);
 23006  
 23007    switch (rs.edgeType) {
 23008      case 'haystack':
 23009        return {
 23010          x: rs.haystackPts[0],
 23011          y: rs.haystackPts[1]
 23012        };
 23013  
 23014      default:
 23015        return {
 23016          x: rs.arrowStartX,
 23017          y: rs.arrowStartY
 23018        };
 23019    }
 23020  };
 23021  
 23022  BRp$4.getTargetEndpoint = function (edge) {
 23023    var rs = edge[0]._private.rscratch;
 23024    this.recalculateRenderedStyle(edge);
 23025  
 23026    switch (rs.edgeType) {
 23027      case 'haystack':
 23028        return {
 23029          x: rs.haystackPts[2],
 23030          y: rs.haystackPts[3]
 23031        };
 23032  
 23033      default:
 23034        return {
 23035          x: rs.arrowEndX,
 23036          y: rs.arrowEndY
 23037        };
 23038    }
 23039  };
 23040  
 23041  var BRp$5 = {};
 23042  
 23043  function pushBezierPts(r, edge, pts) {
 23044    var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
 23045      return qbezierAt(p1, p2, p3, t);
 23046    };
 23047  
 23048    var _p = edge._private;
 23049    var bpts = _p.rstyle.bezierPts;
 23050  
 23051    for (var i = 0; i < r.bezierProjPcts.length; i++) {
 23052      var p = r.bezierProjPcts[i];
 23053      bpts.push({
 23054        x: qbezierAt$1(pts[0], pts[2], pts[4], p),
 23055        y: qbezierAt$1(pts[1], pts[3], pts[5], p)
 23056      });
 23057    }
 23058  }
 23059  
 23060  BRp$5.storeEdgeProjections = function (edge) {
 23061    var _p = edge._private;
 23062    var rs = _p.rscratch;
 23063    var et = rs.edgeType; // clear the cached points state
 23064  
 23065    _p.rstyle.bezierPts = null;
 23066    _p.rstyle.linePts = null;
 23067    _p.rstyle.haystackPts = null;
 23068  
 23069    if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
 23070      _p.rstyle.bezierPts = [];
 23071  
 23072      for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
 23073        pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
 23074      }
 23075    } else if (et === 'segments') {
 23076      var lpts = _p.rstyle.linePts = [];
 23077  
 23078      for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
 23079        lpts.push({
 23080          x: rs.allpts[i],
 23081          y: rs.allpts[i + 1]
 23082        });
 23083      }
 23084    } else if (et === 'haystack') {
 23085      var hpts = rs.haystackPts;
 23086      _p.rstyle.haystackPts = [{
 23087        x: hpts[0],
 23088        y: hpts[1]
 23089      }, {
 23090        x: hpts[2],
 23091        y: hpts[3]
 23092      }];
 23093    }
 23094  
 23095    _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
 23096  };
 23097  
 23098  BRp$5.recalculateEdgeProjections = function (edges) {
 23099    this.findEdgeControlPoints(edges);
 23100  };
 23101  
 23102  var BRp$6 = {};
 23103  
 23104  BRp$6.recalculateNodeLabelProjection = function (node) {
 23105    var content = node.pstyle('label').strValue;
 23106  
 23107    if (emptyString(content)) {
 23108      return;
 23109    }
 23110  
 23111    var textX, textY;
 23112    var _p = node._private;
 23113    var nodeWidth = node.width();
 23114    var nodeHeight = node.height();
 23115    var padding = node.padding();
 23116    var nodePos = node.position();
 23117    var textHalign = node.pstyle('text-halign').strValue;
 23118    var textValign = node.pstyle('text-valign').strValue;
 23119    var rs = _p.rscratch;
 23120    var rstyle = _p.rstyle;
 23121  
 23122    switch (textHalign) {
 23123      case 'left':
 23124        textX = nodePos.x - nodeWidth / 2 - padding;
 23125        break;
 23126  
 23127      case 'right':
 23128        textX = nodePos.x + nodeWidth / 2 + padding;
 23129        break;
 23130  
 23131      default:
 23132        // e.g. center
 23133        textX = nodePos.x;
 23134    }
 23135  
 23136    switch (textValign) {
 23137      case 'top':
 23138        textY = nodePos.y - nodeHeight / 2 - padding;
 23139        break;
 23140  
 23141      case 'bottom':
 23142        textY = nodePos.y + nodeHeight / 2 + padding;
 23143        break;
 23144  
 23145      default:
 23146        // e.g. middle
 23147        textY = nodePos.y;
 23148    }
 23149  
 23150    rs.labelX = textX;
 23151    rs.labelY = textY;
 23152    rstyle.labelX = textX;
 23153    rstyle.labelY = textY;
 23154    this.applyLabelDimensions(node);
 23155  };
 23156  
 23157  var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
 23158    var angle = Math.atan(dy / dx);
 23159  
 23160    if (dx === 0 && angle < 0) {
 23161      angle = angle * -1;
 23162    }
 23163  
 23164    return angle;
 23165  };
 23166  
 23167  var lineAngle = function lineAngle(p0, p1) {
 23168    var dx = p1.x - p0.x;
 23169    var dy = p1.y - p0.y;
 23170    return lineAngleFromDelta(dx, dy);
 23171  };
 23172  
 23173  var bezierAngle = function bezierAngle(p0, p1, p2, t) {
 23174    var t0 = bound(0, t - 0.001, 1);
 23175    var t1 = bound(0, t + 0.001, 1);
 23176    var lp0 = qbezierPtAt(p0, p1, p2, t0);
 23177    var lp1 = qbezierPtAt(p0, p1, p2, t1);
 23178    return lineAngle(lp0, lp1);
 23179  };
 23180  
 23181  BRp$6.recalculateEdgeLabelProjections = function (edge) {
 23182    var p;
 23183    var _p = edge._private;
 23184    var rs = _p.rscratch;
 23185    var r = this;
 23186    var content = {
 23187      mid: edge.pstyle('label').strValue,
 23188      source: edge.pstyle('source-label').strValue,
 23189      target: edge.pstyle('target-label').strValue
 23190    };
 23191  
 23192    if (content.mid || content.source || content.target) ; else {
 23193        return; // no labels => no calcs
 23194      } // add center point to style so bounding box calculations can use it
 23195    //
 23196  
 23197  
 23198    p = {
 23199      x: rs.midX,
 23200      y: rs.midY
 23201    };
 23202  
 23203    var setRs = function setRs(propName, prefix, value) {
 23204      setPrefixedProperty(_p.rscratch, propName, prefix, value);
 23205      setPrefixedProperty(_p.rstyle, propName, prefix, value);
 23206    };
 23207  
 23208    setRs('labelX', null, p.x);
 23209    setRs('labelY', null, p.y);
 23210    var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
 23211    setRs('labelAutoAngle', null, midAngle);
 23212  
 23213    var createControlPointInfo = function createControlPointInfo() {
 23214      if (createControlPointInfo.cache) {
 23215        return createControlPointInfo.cache;
 23216      } // use cache so only 1x per edge
 23217  
 23218  
 23219      var ctrlpts = []; // store each ctrlpt info init
 23220  
 23221      for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
 23222        var p0 = {
 23223          x: rs.allpts[i],
 23224          y: rs.allpts[i + 1]
 23225        };
 23226        var p1 = {
 23227          x: rs.allpts[i + 2],
 23228          y: rs.allpts[i + 3]
 23229        }; // ctrlpt
 23230  
 23231        var p2 = {
 23232          x: rs.allpts[i + 4],
 23233          y: rs.allpts[i + 5]
 23234        };
 23235        ctrlpts.push({
 23236          p0: p0,
 23237          p1: p1,
 23238          p2: p2,
 23239          startDist: 0,
 23240          length: 0,
 23241          segments: []
 23242        });
 23243      }
 23244  
 23245      var bpts = _p.rstyle.bezierPts;
 23246      var nProjs = r.bezierProjPcts.length;
 23247  
 23248      function addSegment(cp, p0, p1, t0, t1) {
 23249        var length = dist(p0, p1);
 23250        var prevSegment = cp.segments[cp.segments.length - 1];
 23251        var segment = {
 23252          p0: p0,
 23253          p1: p1,
 23254          t0: t0,
 23255          t1: t1,
 23256          startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
 23257          length: length
 23258        };
 23259        cp.segments.push(segment);
 23260        cp.length += length;
 23261      } // update each ctrlpt with segment info
 23262  
 23263  
 23264      for (var _i = 0; _i < ctrlpts.length; _i++) {
 23265        var cp = ctrlpts[_i];
 23266        var prevCp = ctrlpts[_i - 1];
 23267  
 23268        if (prevCp) {
 23269          cp.startDist = prevCp.startDist + prevCp.length;
 23270        }
 23271  
 23272        addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
 23273  
 23274        for (var j = 0; j < nProjs - 1; j++) {
 23275          addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
 23276        }
 23277  
 23278        addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
 23279      }
 23280  
 23281      return createControlPointInfo.cache = ctrlpts;
 23282    };
 23283  
 23284    var calculateEndProjection = function calculateEndProjection(prefix) {
 23285      var angle;
 23286      var isSrc = prefix === 'source';
 23287  
 23288      if (!content[prefix]) {
 23289        return;
 23290      }
 23291  
 23292      var offset = edge.pstyle(prefix + '-text-offset').pfValue;
 23293  
 23294      switch (rs.edgeType) {
 23295        case 'self':
 23296        case 'compound':
 23297        case 'bezier':
 23298        case 'multibezier':
 23299          {
 23300            var cps = createControlPointInfo();
 23301            var selected;
 23302            var startDist = 0;
 23303            var totalDist = 0; // find the segment we're on
 23304  
 23305            for (var i = 0; i < cps.length; i++) {
 23306              var _cp = cps[isSrc ? i : cps.length - 1 - i];
 23307  
 23308              for (var j = 0; j < _cp.segments.length; j++) {
 23309                var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
 23310                var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
 23311                startDist = totalDist;
 23312                totalDist += _seg.length;
 23313  
 23314                if (totalDist >= offset || lastSeg) {
 23315                  selected = {
 23316                    cp: _cp,
 23317                    segment: _seg
 23318                  };
 23319                  break;
 23320                }
 23321              }
 23322  
 23323              if (selected) {
 23324                break;
 23325              }
 23326            }
 23327  
 23328            var cp = selected.cp;
 23329            var seg = selected.segment;
 23330            var tSegment = (offset - startDist) / seg.length;
 23331            var segDt = seg.t1 - seg.t0;
 23332            var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
 23333            t = bound(0, t, 1);
 23334            p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
 23335            angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
 23336            break;
 23337          }
 23338  
 23339        case 'straight':
 23340        case 'segments':
 23341        case 'haystack':
 23342          {
 23343            var d = 0,
 23344                di,
 23345                d0;
 23346            var p0, p1;
 23347            var l = rs.allpts.length;
 23348  
 23349            for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
 23350              if (isSrc) {
 23351                p0 = {
 23352                  x: rs.allpts[_i2],
 23353                  y: rs.allpts[_i2 + 1]
 23354                };
 23355                p1 = {
 23356                  x: rs.allpts[_i2 + 2],
 23357                  y: rs.allpts[_i2 + 3]
 23358                };
 23359              } else {
 23360                p0 = {
 23361                  x: rs.allpts[l - 2 - _i2],
 23362                  y: rs.allpts[l - 1 - _i2]
 23363                };
 23364                p1 = {
 23365                  x: rs.allpts[l - 4 - _i2],
 23366                  y: rs.allpts[l - 3 - _i2]
 23367                };
 23368              }
 23369  
 23370              di = dist(p0, p1);
 23371              d0 = d;
 23372              d += di;
 23373  
 23374              if (d >= offset) {
 23375                break;
 23376              }
 23377            }
 23378  
 23379            var pD = offset - d0;
 23380  
 23381            var _t = pD / di;
 23382  
 23383            _t = bound(0, _t, 1);
 23384            p = lineAt(p0, p1, _t);
 23385            angle = lineAngle(p0, p1);
 23386            break;
 23387          }
 23388      }
 23389  
 23390      setRs('labelX', prefix, p.x);
 23391      setRs('labelY', prefix, p.y);
 23392      setRs('labelAutoAngle', prefix, angle);
 23393    };
 23394  
 23395    calculateEndProjection('source');
 23396    calculateEndProjection('target');
 23397    this.applyLabelDimensions(edge);
 23398  };
 23399  
 23400  BRp$6.applyLabelDimensions = function (ele) {
 23401    this.applyPrefixedLabelDimensions(ele);
 23402  
 23403    if (ele.isEdge()) {
 23404      this.applyPrefixedLabelDimensions(ele, 'source');
 23405      this.applyPrefixedLabelDimensions(ele, 'target');
 23406    }
 23407  };
 23408  
 23409  BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
 23410    var _p = ele._private;
 23411    var text = this.getLabelText(ele, prefix);
 23412    var labelDims = this.calculateLabelDimensions(ele, text);
 23413    var lineHeight = ele.pstyle('line-height').pfValue;
 23414    var textWrap = ele.pstyle('text-wrap').strValue;
 23415    var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
 23416    var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
 23417    var normPerLineHeight = labelDims.height / numLines;
 23418    var labelLineHeight = normPerLineHeight * lineHeight;
 23419    var width = labelDims.width;
 23420    var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
 23421    setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
 23422    setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
 23423    setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
 23424    setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
 23425    setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
 23426  };
 23427  
 23428  BRp$6.getLabelText = function (ele, prefix) {
 23429    var _p = ele._private;
 23430    var pfd = prefix ? prefix + '-' : '';
 23431    var text = ele.pstyle(pfd + 'label').strValue;
 23432    var textTransform = ele.pstyle('text-transform').value;
 23433  
 23434    var rscratch = function rscratch(propName, value) {
 23435      if (value) {
 23436        setPrefixedProperty(_p.rscratch, propName, prefix, value);
 23437        return value;
 23438      } else {
 23439        return getPrefixedProperty(_p.rscratch, propName, prefix);
 23440      }
 23441    }; // for empty text, skip all processing
 23442  
 23443  
 23444    if (!text) {
 23445      return '';
 23446    }
 23447  
 23448    if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
 23449      text = text.toUpperCase();
 23450    } else if (textTransform == 'lowercase') {
 23451      text = text.toLowerCase();
 23452    }
 23453  
 23454    var wrapStyle = ele.pstyle('text-wrap').value;
 23455  
 23456    if (wrapStyle === 'wrap') {
 23457      var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
 23458  
 23459      if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
 23460        return rscratch('labelWrapCachedText');
 23461      }
 23462  
 23463      var zwsp = "\u200B";
 23464      var lines = text.split('\n');
 23465      var maxW = ele.pstyle('text-max-width').pfValue;
 23466      var overflow = ele.pstyle('text-overflow-wrap').value;
 23467      var overflowAny = overflow === 'anywhere';
 23468      var wrappedLines = [];
 23469      var wordsRegex = /[\s\u200b]+/;
 23470      var wordSeparator = overflowAny ? '' : ' ';
 23471  
 23472      for (var l = 0; l < lines.length; l++) {
 23473        var line = lines[l];
 23474        var lineDims = this.calculateLabelDimensions(ele, line);
 23475        var lineW = lineDims.width;
 23476  
 23477        if (overflowAny) {
 23478          var processedLine = line.split('').join(zwsp);
 23479          line = processedLine;
 23480        }
 23481  
 23482        if (lineW > maxW) {
 23483          // line is too long
 23484          var words = line.split(wordsRegex);
 23485          var subline = '';
 23486  
 23487          for (var w = 0; w < words.length; w++) {
 23488            var word = words[w];
 23489            var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
 23490            var testDims = this.calculateLabelDimensions(ele, testLine);
 23491            var testW = testDims.width;
 23492  
 23493            if (testW <= maxW) {
 23494              // word fits on current line
 23495              subline += word + wordSeparator;
 23496            } else {
 23497              // word starts new line
 23498              if (subline) {
 23499                wrappedLines.push(subline);
 23500              }
 23501  
 23502              subline = word + wordSeparator;
 23503            }
 23504          } // if there's remaining text, put it in a wrapped line
 23505  
 23506  
 23507          if (!subline.match(/^[\s\u200b]+$/)) {
 23508            wrappedLines.push(subline);
 23509          }
 23510        } else {
 23511          // line is already short enough
 23512          wrappedLines.push(line);
 23513        }
 23514      } // for
 23515  
 23516  
 23517      rscratch('labelWrapCachedLines', wrappedLines);
 23518      text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
 23519      rscratch('labelWrapKey', labelKey);
 23520    } else if (wrapStyle === 'ellipsis') {
 23521      var _maxW = ele.pstyle('text-max-width').pfValue;
 23522      var ellipsized = '';
 23523      var ellipsis = "\u2026";
 23524      var incLastCh = false;
 23525  
 23526      for (var i = 0; i < text.length; i++) {
 23527        var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
 23528  
 23529        if (widthWithNextCh > _maxW) {
 23530          break;
 23531        }
 23532  
 23533        ellipsized += text[i];
 23534  
 23535        if (i === text.length - 1) {
 23536          incLastCh = true;
 23537        }
 23538      }
 23539  
 23540      if (!incLastCh) {
 23541        ellipsized += ellipsis;
 23542      }
 23543  
 23544      return ellipsized;
 23545    } // if ellipsize
 23546  
 23547  
 23548    return text;
 23549  };
 23550  
 23551  BRp$6.getLabelJustification = function (ele) {
 23552    var justification = ele.pstyle('text-justification').strValue;
 23553    var textHalign = ele.pstyle('text-halign').strValue;
 23554  
 23555    if (justification === 'auto') {
 23556      if (ele.isNode()) {
 23557        switch (textHalign) {
 23558          case 'left':
 23559            return 'right';
 23560  
 23561          case 'right':
 23562            return 'left';
 23563  
 23564          default:
 23565            return 'center';
 23566        }
 23567      } else {
 23568        return 'center';
 23569      }
 23570    } else {
 23571      return justification;
 23572    }
 23573  };
 23574  
 23575  BRp$6.calculateLabelDimensions = function (ele, text) {
 23576    var r = this;
 23577    var cacheKey = hashString(text, ele._private.labelDimsKey);
 23578    var cache = r.labelDimCache || (r.labelDimCache = []);
 23579    var existingVal = cache[cacheKey];
 23580  
 23581    if (existingVal != null) {
 23582      return existingVal;
 23583    }
 23584  
 23585    var sizeMult = 1; // increase the scale to increase accuracy w.r.t. zoomed text
 23586  
 23587    var fStyle = ele.pstyle('font-style').strValue;
 23588    var size = sizeMult * ele.pstyle('font-size').pfValue + 'px';
 23589    var family = ele.pstyle('font-family').strValue;
 23590    var weight = ele.pstyle('font-weight').strValue;
 23591    var div = this.labelCalcDiv;
 23592  
 23593    if (!div) {
 23594      div = this.labelCalcDiv = document.createElement('div'); // eslint-disable-line no-undef
 23595  
 23596      document.body.appendChild(div); // eslint-disable-line no-undef
 23597    }
 23598  
 23599    var ds = div.style; // from ele style
 23600  
 23601    ds.fontFamily = family;
 23602    ds.fontStyle = fStyle;
 23603    ds.fontSize = size;
 23604    ds.fontWeight = weight; // forced style
 23605  
 23606    ds.position = 'absolute';
 23607    ds.left = '-9999px';
 23608    ds.top = '-9999px';
 23609    ds.zIndex = '-1';
 23610    ds.visibility = 'hidden';
 23611    ds.pointerEvents = 'none';
 23612    ds.padding = '0';
 23613    ds.lineHeight = '1'; // - newlines must be taken into account for text-wrap:wrap
 23614    // - since spaces are not collapsed, each space must be taken into account
 23615  
 23616    ds.whiteSpace = 'pre'; // put label content in div
 23617  
 23618    div.textContent = text;
 23619    return cache[cacheKey] = {
 23620      width: Math.ceil(div.clientWidth / sizeMult),
 23621      height: Math.ceil(div.clientHeight / sizeMult)
 23622    };
 23623  };
 23624  
 23625  BRp$6.calculateLabelAngle = function (ele, prefix) {
 23626    var _p = ele._private;
 23627    var rs = _p.rscratch;
 23628    var isEdge = ele.isEdge();
 23629    var prefixDash = prefix ? prefix + '-' : '';
 23630    var rot = ele.pstyle(prefixDash + 'text-rotation');
 23631    var rotStr = rot.strValue;
 23632  
 23633    if (rotStr === 'none') {
 23634      return 0;
 23635    } else if (isEdge && rotStr === 'autorotate') {
 23636      return rs.labelAutoAngle;
 23637    } else if (rotStr === 'autorotate') {
 23638      return 0;
 23639    } else {
 23640      return rot.pfValue;
 23641    }
 23642  };
 23643  
 23644  BRp$6.calculateLabelAngles = function (ele) {
 23645    var r = this;
 23646    var isEdge = ele.isEdge();
 23647    var _p = ele._private;
 23648    var rs = _p.rscratch;
 23649    rs.labelAngle = r.calculateLabelAngle(ele);
 23650  
 23651    if (isEdge) {
 23652      rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
 23653      rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
 23654    }
 23655  };
 23656  
 23657  var BRp$7 = {};
 23658  var TOO_SMALL_CUT_RECT = 28;
 23659  var warnedCutRect = false;
 23660  
 23661  BRp$7.getNodeShape = function (node) {
 23662    var r = this;
 23663    var shape = node.pstyle('shape').value;
 23664  
 23665    if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
 23666      if (!warnedCutRect) {
 23667        warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
 23668        warnedCutRect = true;
 23669      }
 23670  
 23671      return 'rectangle';
 23672    }
 23673  
 23674    if (node.isParent()) {
 23675      if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'cutrectangle' || shape === 'barrel') {
 23676        return shape;
 23677      } else {
 23678        return 'rectangle';
 23679      }
 23680    }
 23681  
 23682    if (shape === 'polygon') {
 23683      var points = node.pstyle('shape-polygon-points').value;
 23684      return r.nodeShapes.makePolygon(points).name;
 23685    }
 23686  
 23687    return shape;
 23688  };
 23689  
 23690  var BRp$8 = {};
 23691  
 23692  BRp$8.registerCalculationListeners = function () {
 23693    var cy = this.cy;
 23694    var elesToUpdate = cy.collection();
 23695    var r = this;
 23696  
 23697    var enqueue = function enqueue(eles) {
 23698      var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
 23699      elesToUpdate.merge(eles);
 23700  
 23701      if (dirtyStyleCaches) {
 23702        for (var i = 0; i < eles.length; i++) {
 23703          var ele = eles[i];
 23704          var _p = ele._private;
 23705          var rstyle = _p.rstyle;
 23706          rstyle.clean = false;
 23707          rstyle.cleanConnected = false;
 23708        }
 23709      }
 23710    };
 23711  
 23712    r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
 23713      var ele = e.target;
 23714      enqueue(ele);
 23715    }).on('style.* background.*', function onDirtyStyle(e) {
 23716      var ele = e.target;
 23717      enqueue(ele, false);
 23718    });
 23719  
 23720    var updateEleCalcs = function updateEleCalcs(willDraw) {
 23721      if (willDraw) {
 23722        var fns = r.onUpdateEleCalcsFns;
 23723  
 23724        for (var i = 0; i < elesToUpdate.length; i++) {
 23725          var ele = elesToUpdate[i];
 23726          var rstyle = ele._private.rstyle;
 23727  
 23728          if (ele.isNode() && !rstyle.cleanConnected) {
 23729            enqueue(ele.connectedEdges());
 23730            rstyle.cleanConnected = true;
 23731          }
 23732        }
 23733  
 23734        if (fns) {
 23735          for (var i = 0; i < fns.length; i++) {
 23736            var fn = fns[i];
 23737            fn(willDraw, elesToUpdate);
 23738          }
 23739        }
 23740  
 23741        r.recalculateRenderedStyle(elesToUpdate);
 23742        elesToUpdate = cy.collection();
 23743      }
 23744    };
 23745  
 23746    r.flushRenderedStyleQueue = function () {
 23747      updateEleCalcs(true);
 23748    };
 23749  
 23750    r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
 23751  };
 23752  
 23753  BRp$8.onUpdateEleCalcs = function (fn) {
 23754    var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
 23755    fns.push(fn);
 23756  };
 23757  
 23758  BRp$8.recalculateRenderedStyle = function (eles, useCache) {
 23759    var isCleanConnected = function isCleanConnected(ele) {
 23760      return ele._private.rstyle.cleanConnected;
 23761    };
 23762  
 23763    var edges = [];
 23764    var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
 23765  
 23766    if (this.destroyed) {
 23767      return;
 23768    } // use cache by default for perf
 23769  
 23770  
 23771    if (useCache === undefined) {
 23772      useCache = true;
 23773    }
 23774  
 23775    for (var i = 0; i < eles.length; i++) {
 23776      var ele = eles[i];
 23777      var _p = ele._private;
 23778      var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
 23779      // (and a request for recalc may come in between frames)
 23780  
 23781      if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
 23782        rstyle.clean = false;
 23783      } // only update if dirty and in graph
 23784  
 23785  
 23786      if (useCache && rstyle.clean || ele.removed()) {
 23787        continue;
 23788      } // only update if not display: none
 23789  
 23790  
 23791      if (ele.pstyle('display').value === 'none') {
 23792        continue;
 23793      }
 23794  
 23795      if (_p.group === 'nodes') {
 23796        nodes.push(ele);
 23797      } else {
 23798        // edges
 23799        edges.push(ele);
 23800      }
 23801  
 23802      rstyle.clean = true;
 23803    } // update node data from projections
 23804  
 23805  
 23806    for (var i = 0; i < nodes.length; i++) {
 23807      var ele = nodes[i];
 23808      var _p = ele._private;
 23809      var rstyle = _p.rstyle;
 23810      var pos = ele.position();
 23811      this.recalculateNodeLabelProjection(ele);
 23812      rstyle.nodeX = pos.x;
 23813      rstyle.nodeY = pos.y;
 23814      rstyle.nodeW = ele.pstyle('width').pfValue;
 23815      rstyle.nodeH = ele.pstyle('height').pfValue;
 23816    }
 23817  
 23818    this.recalculateEdgeProjections(edges); // update edge data from projections
 23819  
 23820    for (var i = 0; i < edges.length; i++) {
 23821      var ele = edges[i];
 23822      var _p = ele._private;
 23823      var rstyle = _p.rstyle;
 23824      var rs = _p.rscratch; // update rstyle positions
 23825  
 23826      rstyle.srcX = rs.arrowStartX;
 23827      rstyle.srcY = rs.arrowStartY;
 23828      rstyle.tgtX = rs.arrowEndX;
 23829      rstyle.tgtY = rs.arrowEndY;
 23830      rstyle.midX = rs.midX;
 23831      rstyle.midY = rs.midY;
 23832      rstyle.labelAngle = rs.labelAngle;
 23833      rstyle.sourceLabelAngle = rs.sourceLabelAngle;
 23834      rstyle.targetLabelAngle = rs.targetLabelAngle;
 23835    }
 23836  };
 23837  
 23838  var BRp$9 = {};
 23839  
 23840  BRp$9.updateCachedGrabbedEles = function () {
 23841    var eles = this.cachedZSortedEles;
 23842  
 23843    if (!eles) {
 23844      // just let this be recalculated on the next z sort tick
 23845      return;
 23846    }
 23847  
 23848    eles.drag = [];
 23849    eles.nondrag = [];
 23850    var grabTargets = [];
 23851  
 23852    for (var i = 0; i < eles.length; i++) {
 23853      var ele = eles[i];
 23854      var rs = ele._private.rscratch;
 23855  
 23856      if (ele.grabbed() && !ele.isParent()) {
 23857        grabTargets.push(ele);
 23858      } else if (rs.inDragLayer) {
 23859        eles.drag.push(ele);
 23860      } else {
 23861        eles.nondrag.push(ele);
 23862      }
 23863    } // put the grab target nodes last so it's on top of its neighbourhood
 23864  
 23865  
 23866    for (var i = 0; i < grabTargets.length; i++) {
 23867      var ele = grabTargets[i];
 23868      eles.drag.push(ele);
 23869    }
 23870  };
 23871  
 23872  BRp$9.invalidateCachedZSortedEles = function () {
 23873    this.cachedZSortedEles = null;
 23874  };
 23875  
 23876  BRp$9.getCachedZSortedEles = function (forceRecalc) {
 23877    if (forceRecalc || !this.cachedZSortedEles) {
 23878      var eles = this.cy.mutableElements().toArray();
 23879      eles.sort(zIndexSort);
 23880      eles.interactive = eles.filter(function (ele) {
 23881        return ele.interactive();
 23882      });
 23883      this.cachedZSortedEles = eles;
 23884      this.updateCachedGrabbedEles();
 23885    } else {
 23886      eles = this.cachedZSortedEles;
 23887    }
 23888  
 23889    return eles;
 23890  };
 23891  
 23892  var BRp$a = {};
 23893  [BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
 23894    extend(BRp$a, props);
 23895  });
 23896  
 23897  var BRp$b = {};
 23898  
 23899  BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
 23900    var r = this;
 23901    var imageCache = r.imageCache = r.imageCache || {};
 23902    var cache = imageCache[url];
 23903  
 23904    if (cache) {
 23905      if (!cache.image.complete) {
 23906        cache.image.addEventListener('load', onLoad);
 23907      }
 23908  
 23909      return cache.image;
 23910    } else {
 23911      cache = imageCache[url] = imageCache[url] || {};
 23912      var image = cache.image = new Image(); // eslint-disable-line no-undef
 23913  
 23914      image.addEventListener('load', onLoad);
 23915      image.addEventListener('error', function () {
 23916        image.error = true;
 23917      }); // #1582 safari doesn't load data uris with crossOrigin properly
 23918      // https://bugs.webkit.org/show_bug.cgi?id=123978
 23919  
 23920      var dataUriPrefix = 'data:';
 23921      var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
 23922  
 23923      if (!isDataUri) {
 23924        image.crossOrigin = crossOrigin; // prevent tainted canvas
 23925      }
 23926  
 23927      image.src = url;
 23928      return image;
 23929    }
 23930  };
 23931  
 23932  var BRp$c = {};
 23933  /* global document, window, ResizeObserver, MutationObserver */
 23934  
 23935  BRp$c.registerBinding = function (target, event, handler, useCapture) {
 23936    // eslint-disable-line no-unused-vars
 23937    var args = Array.prototype.slice.apply(arguments, [1]); // copy
 23938  
 23939    var b = this.binder(target);
 23940    return b.on.apply(b, args);
 23941  };
 23942  
 23943  BRp$c.binder = function (tgt) {
 23944    var r = this;
 23945    var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
 23946  
 23947    if (r.supportsPassiveEvents == null) {
 23948      // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
 23949      var supportsPassive = false;
 23950  
 23951      try {
 23952        var opts = Object.defineProperty({}, 'passive', {
 23953          get: function get() {
 23954            supportsPassive = true;
 23955            return true;
 23956          }
 23957        });
 23958        window.addEventListener('test', null, opts);
 23959      } catch (err) {// not supported
 23960      }
 23961  
 23962      r.supportsPassiveEvents = supportsPassive;
 23963    }
 23964  
 23965    var on = function on(event, handler, useCapture) {
 23966      var args = Array.prototype.slice.call(arguments);
 23967  
 23968      if (tgtIsDom && r.supportsPassiveEvents) {
 23969        // replace useCapture w/ opts obj
 23970        args[2] = {
 23971          capture: useCapture != null ? useCapture : false,
 23972          passive: false,
 23973          once: false
 23974        };
 23975      }
 23976  
 23977      r.bindings.push({
 23978        target: tgt,
 23979        args: args
 23980      });
 23981      (tgt.addEventListener || tgt.on).apply(tgt, args);
 23982      return this;
 23983    };
 23984  
 23985    return {
 23986      on: on,
 23987      addEventListener: on,
 23988      addListener: on,
 23989      bind: on
 23990    };
 23991  };
 23992  
 23993  BRp$c.nodeIsDraggable = function (node) {
 23994    return node && node.isNode() && !node.locked() && node.grabbable();
 23995  };
 23996  
 23997  BRp$c.nodeIsGrabbable = function (node) {
 23998    return this.nodeIsDraggable(node) && node.interactive();
 23999  };
 24000  
 24001  BRp$c.load = function () {
 24002    var r = this;
 24003  
 24004    var isSelected = function isSelected(ele) {
 24005      return ele.selected();
 24006    };
 24007  
 24008    var triggerEvents = function triggerEvents(target, names, e, position) {
 24009      if (target == null) {
 24010        target = r.cy;
 24011      }
 24012  
 24013      for (var i = 0; i < names.length; i++) {
 24014        var name = names[i];
 24015        target.emit({
 24016          originalEvent: e,
 24017          type: name,
 24018          position: position
 24019        });
 24020      }
 24021    };
 24022  
 24023    var isMultSelKeyDown = function isMultSelKeyDown(e) {
 24024      return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
 24025    };
 24026  
 24027    var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
 24028      var allowPassthrough = true;
 24029  
 24030      if (r.cy.hasCompoundNodes() && down && down.pannable()) {
 24031        // a grabbable compound node below the ele => no passthrough panning
 24032        for (var i = 0; downs && i < downs.length; i++) {
 24033          var down = downs[i];
 24034  
 24035          if (down.isNode() && down.isParent()) {
 24036            allowPassthrough = false;
 24037            break;
 24038          }
 24039        }
 24040      } else {
 24041        allowPassthrough = true;
 24042      }
 24043  
 24044      return allowPassthrough;
 24045    };
 24046  
 24047    var setGrabbed = function setGrabbed(ele) {
 24048      ele[0]._private.grabbed = true;
 24049    };
 24050  
 24051    var setFreed = function setFreed(ele) {
 24052      ele[0]._private.grabbed = false;
 24053    };
 24054  
 24055    var setInDragLayer = function setInDragLayer(ele) {
 24056      ele[0]._private.rscratch.inDragLayer = true;
 24057    };
 24058  
 24059    var setOutDragLayer = function setOutDragLayer(ele) {
 24060      ele[0]._private.rscratch.inDragLayer = false;
 24061    };
 24062  
 24063    var setGrabTarget = function setGrabTarget(ele) {
 24064      ele[0]._private.rscratch.isGrabTarget = true;
 24065    };
 24066  
 24067    var removeGrabTarget = function removeGrabTarget(ele) {
 24068      ele[0]._private.rscratch.isGrabTarget = false;
 24069    };
 24070  
 24071    var addToDragList = function addToDragList(ele, opts) {
 24072      var list = opts.addToList;
 24073      var listHasEle = list.has(ele);
 24074  
 24075      if (!listHasEle) {
 24076        list.merge(ele);
 24077        setGrabbed(ele);
 24078      }
 24079    }; // helper function to determine which child nodes and inner edges
 24080    // of a compound node to be dragged as well as the grabbed and selected nodes
 24081  
 24082  
 24083    var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
 24084      if (!node.cy().hasCompoundNodes()) {
 24085        return;
 24086      }
 24087  
 24088      if (opts.inDragLayer == null && opts.addToList == null) {
 24089        return;
 24090      } // nothing to do
 24091  
 24092  
 24093      var innerNodes = node.descendants();
 24094  
 24095      if (opts.inDragLayer) {
 24096        innerNodes.forEach(setInDragLayer);
 24097        innerNodes.connectedEdges().forEach(setInDragLayer);
 24098      }
 24099  
 24100      if (opts.addToList) {
 24101        opts.addToList.unmerge(innerNodes);
 24102      }
 24103    }; // adds the given nodes and its neighbourhood to the drag layer
 24104  
 24105  
 24106    var addNodesToDrag = function addNodesToDrag(nodes, opts) {
 24107      opts = opts || {};
 24108      var hasCompoundNodes = nodes.cy().hasCompoundNodes();
 24109  
 24110      if (opts.inDragLayer) {
 24111        nodes.forEach(setInDragLayer);
 24112        nodes.neighborhood().stdFilter(function (ele) {
 24113          return !hasCompoundNodes || ele.isEdge();
 24114        }).forEach(setInDragLayer);
 24115      }
 24116  
 24117      if (opts.addToList) {
 24118        nodes.forEach(function (ele) {
 24119          addToDragList(ele, opts);
 24120        });
 24121      }
 24122  
 24123      addDescendantsToDrag(nodes, opts); // always add to drag
 24124      // also add nodes and edges related to the topmost ancestor
 24125  
 24126      updateAncestorsInDragLayer(nodes, {
 24127        inDragLayer: opts.inDragLayer
 24128      });
 24129      r.updateCachedGrabbedEles();
 24130    };
 24131  
 24132    var addNodeToDrag = addNodesToDrag;
 24133  
 24134    var freeDraggedElements = function freeDraggedElements(grabbedEles) {
 24135      if (!grabbedEles) {
 24136        return;
 24137      } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
 24138  
 24139  
 24140      r.getCachedZSortedEles().forEach(function (ele) {
 24141        setFreed(ele);
 24142        setOutDragLayer(ele);
 24143        removeGrabTarget(ele);
 24144      });
 24145      r.updateCachedGrabbedEles();
 24146    }; // helper function to determine which ancestor nodes and edges should go
 24147    // to the drag layer (or should be removed from drag layer).
 24148  
 24149  
 24150    var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
 24151      if (opts.inDragLayer == null && opts.addToList == null) {
 24152        return;
 24153      } // nothing to do
 24154  
 24155  
 24156      if (!node.cy().hasCompoundNodes()) {
 24157        return;
 24158      } // find top-level parent
 24159  
 24160  
 24161      var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
 24162  
 24163      if (parent.same(node)) {
 24164        return;
 24165      }
 24166  
 24167      var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
 24168      var edges = nodes.connectedEdges();
 24169  
 24170      if (opts.inDragLayer) {
 24171        edges.forEach(setInDragLayer);
 24172        nodes.forEach(setInDragLayer);
 24173      }
 24174  
 24175      if (opts.addToList) {
 24176        nodes.forEach(function (ele) {
 24177          addToDragList(ele, opts);
 24178        });
 24179      }
 24180    };
 24181  
 24182    var blurActiveDomElement = function blurActiveDomElement() {
 24183      if (document.activeElement != null && document.activeElement.blur != null) {
 24184        document.activeElement.blur();
 24185      }
 24186    };
 24187  
 24188    var haveMutationsApi = typeof MutationObserver !== 'undefined';
 24189    var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
 24190  
 24191    if (haveMutationsApi) {
 24192      r.removeObserver = new MutationObserver(function (mutns) {
 24193        // eslint-disable-line no-undef
 24194        for (var i = 0; i < mutns.length; i++) {
 24195          var mutn = mutns[i];
 24196          var rNodes = mutn.removedNodes;
 24197  
 24198          if (rNodes) {
 24199            for (var j = 0; j < rNodes.length; j++) {
 24200              var rNode = rNodes[j];
 24201  
 24202              if (rNode === r.container) {
 24203                r.destroy();
 24204                break;
 24205              }
 24206            }
 24207          }
 24208        }
 24209      });
 24210  
 24211      if (r.container.parentNode) {
 24212        r.removeObserver.observe(r.container.parentNode, {
 24213          childList: true
 24214        });
 24215      }
 24216    } else {
 24217      r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
 24218        // eslint-disable-line no-unused-vars
 24219        r.destroy();
 24220      });
 24221    }
 24222  
 24223    var onResize = util(function () {
 24224      r.cy.resize();
 24225    }, 100);
 24226  
 24227    if (haveMutationsApi) {
 24228      r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
 24229  
 24230      r.styleObserver.observe(r.container, {
 24231        attributes: true
 24232      });
 24233    } // auto resize
 24234  
 24235  
 24236    r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
 24237  
 24238    if (haveResizeObserverApi) {
 24239      r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
 24240  
 24241      r.resizeObserver.observe(r.container);
 24242    }
 24243  
 24244    var forEachUp = function forEachUp(domEle, fn) {
 24245      while (domEle != null) {
 24246        fn(domEle);
 24247        domEle = domEle.parentNode;
 24248      }
 24249    };
 24250  
 24251    var invalidateCoords = function invalidateCoords() {
 24252      r.invalidateContainerClientCoordsCache();
 24253    };
 24254  
 24255    forEachUp(r.container, function (domEle) {
 24256      r.registerBinding(domEle, 'transitionend', invalidateCoords);
 24257      r.registerBinding(domEle, 'animationend', invalidateCoords);
 24258      r.registerBinding(domEle, 'scroll', invalidateCoords);
 24259    }); // stop right click menu from appearing on cy
 24260  
 24261    r.registerBinding(r.container, 'contextmenu', function (e) {
 24262      e.preventDefault();
 24263    });
 24264  
 24265    var inBoxSelection = function inBoxSelection() {
 24266      return r.selection[4] !== 0;
 24267    };
 24268  
 24269    var eventInContainer = function eventInContainer(e) {
 24270      // save cycles if mouse events aren't to be captured
 24271      var containerPageCoords = r.findContainerClientCoords();
 24272      var x = containerPageCoords[0];
 24273      var y = containerPageCoords[1];
 24274      var width = containerPageCoords[2];
 24275      var height = containerPageCoords[3];
 24276      var positions = e.touches ? e.touches : [e];
 24277      var atLeastOnePosInside = false;
 24278  
 24279      for (var i = 0; i < positions.length; i++) {
 24280        var p = positions[i];
 24281  
 24282        if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
 24283          atLeastOnePosInside = true;
 24284          break;
 24285        }
 24286      }
 24287  
 24288      if (!atLeastOnePosInside) {
 24289        return false;
 24290      }
 24291  
 24292      var container = r.container;
 24293      var target = e.target;
 24294      var tParent = target.parentNode;
 24295      var containerIsTarget = false;
 24296  
 24297      while (tParent) {
 24298        if (tParent === container) {
 24299          containerIsTarget = true;
 24300          break;
 24301        }
 24302  
 24303        tParent = tParent.parentNode;
 24304      }
 24305  
 24306      if (!containerIsTarget) {
 24307        return false;
 24308      } // if target is outisde cy container, then this event is not for us
 24309  
 24310  
 24311      return true;
 24312    }; // Primary key
 24313  
 24314  
 24315    r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
 24316      if (!eventInContainer(e)) {
 24317        return;
 24318      }
 24319  
 24320      e.preventDefault();
 24321      blurActiveDomElement();
 24322      r.hoverData.capture = true;
 24323      r.hoverData.which = e.which;
 24324      var cy = r.cy;
 24325      var gpos = [e.clientX, e.clientY];
 24326      var pos = r.projectIntoViewport(gpos[0], gpos[1]);
 24327      var select = r.selection;
 24328      var nears = r.findNearestElements(pos[0], pos[1], true, false);
 24329      var near = nears[0];
 24330      var draggedElements = r.dragData.possibleDragElements;
 24331      r.hoverData.mdownPos = pos;
 24332      r.hoverData.mdownGPos = gpos;
 24333  
 24334      var checkForTaphold = function checkForTaphold() {
 24335        r.hoverData.tapholdCancelled = false;
 24336        clearTimeout(r.hoverData.tapholdTimeout);
 24337        r.hoverData.tapholdTimeout = setTimeout(function () {
 24338          if (r.hoverData.tapholdCancelled) {
 24339            return;
 24340          } else {
 24341            var ele = r.hoverData.down;
 24342  
 24343            if (ele) {
 24344              ele.emit({
 24345                originalEvent: e,
 24346                type: 'taphold',
 24347                position: {
 24348                  x: pos[0],
 24349                  y: pos[1]
 24350                }
 24351              });
 24352            } else {
 24353              cy.emit({
 24354                originalEvent: e,
 24355                type: 'taphold',
 24356                position: {
 24357                  x: pos[0],
 24358                  y: pos[1]
 24359                }
 24360              });
 24361            }
 24362          }
 24363        }, r.tapholdDuration);
 24364      }; // Right click button
 24365  
 24366  
 24367      if (e.which == 3) {
 24368        r.hoverData.cxtStarted = true;
 24369        var cxtEvt = {
 24370          originalEvent: e,
 24371          type: 'cxttapstart',
 24372          position: {
 24373            x: pos[0],
 24374            y: pos[1]
 24375          }
 24376        };
 24377  
 24378        if (near) {
 24379          near.activate();
 24380          near.emit(cxtEvt);
 24381          r.hoverData.down = near;
 24382        } else {
 24383          cy.emit(cxtEvt);
 24384        }
 24385  
 24386        r.hoverData.downTime = new Date().getTime();
 24387        r.hoverData.cxtDragged = false; // Primary button
 24388      } else if (e.which == 1) {
 24389        if (near) {
 24390          near.activate();
 24391        } // Element dragging
 24392  
 24393  
 24394        {
 24395          // If something is under the cursor and it is draggable, prepare to grab it
 24396          if (near != null) {
 24397            if (r.nodeIsGrabbable(near)) {
 24398              var makeEvent = function makeEvent(type) {
 24399                return {
 24400                  originalEvent: e,
 24401                  type: type,
 24402                  position: {
 24403                    x: pos[0],
 24404                    y: pos[1]
 24405                  }
 24406                };
 24407              };
 24408  
 24409              var triggerGrab = function triggerGrab(ele) {
 24410                ele.emit(makeEvent('grab'));
 24411              };
 24412  
 24413              setGrabTarget(near);
 24414  
 24415              if (!near.selected()) {
 24416                draggedElements = r.dragData.possibleDragElements = cy.collection();
 24417                addNodeToDrag(near, {
 24418                  addToList: draggedElements
 24419                });
 24420                near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
 24421              } else {
 24422                draggedElements = r.dragData.possibleDragElements = cy.collection();
 24423                var selectedNodes = cy.$(function (ele) {
 24424                  return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
 24425                });
 24426                addNodesToDrag(selectedNodes, {
 24427                  addToList: draggedElements
 24428                });
 24429                near.emit(makeEvent('grabon'));
 24430                selectedNodes.forEach(triggerGrab);
 24431              }
 24432  
 24433              r.redrawHint('eles', true);
 24434              r.redrawHint('drag', true);
 24435            }
 24436          }
 24437  
 24438          r.hoverData.down = near;
 24439          r.hoverData.downs = nears;
 24440          r.hoverData.downTime = new Date().getTime();
 24441        }
 24442        triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
 24443          x: pos[0],
 24444          y: pos[1]
 24445        });
 24446  
 24447        if (near == null) {
 24448          select[4] = 1;
 24449          r.data.bgActivePosistion = {
 24450            x: pos[0],
 24451            y: pos[1]
 24452          };
 24453          r.redrawHint('select', true);
 24454          r.redraw();
 24455        } else if (near.pannable()) {
 24456          select[4] = 1; // for future pan
 24457        }
 24458  
 24459        checkForTaphold();
 24460      } // Initialize selection box coordinates
 24461  
 24462  
 24463      select[0] = select[2] = pos[0];
 24464      select[1] = select[3] = pos[1];
 24465    }, false);
 24466    r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
 24467      // eslint-disable-line no-undef
 24468      var capture = r.hoverData.capture;
 24469  
 24470      if (!capture && !eventInContainer(e)) {
 24471        return;
 24472      }
 24473  
 24474      var preventDefault = false;
 24475      var cy = r.cy;
 24476      var zoom = cy.zoom();
 24477      var gpos = [e.clientX, e.clientY];
 24478      var pos = r.projectIntoViewport(gpos[0], gpos[1]);
 24479      var mdownPos = r.hoverData.mdownPos;
 24480      var mdownGPos = r.hoverData.mdownGPos;
 24481      var select = r.selection;
 24482      var near = null;
 24483  
 24484      if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
 24485        near = r.findNearestElement(pos[0], pos[1], true, false);
 24486      }
 24487  
 24488      var last = r.hoverData.last;
 24489      var down = r.hoverData.down;
 24490      var disp = [pos[0] - select[2], pos[1] - select[3]];
 24491      var draggedElements = r.dragData.possibleDragElements;
 24492      var isOverThresholdDrag;
 24493  
 24494      if (mdownGPos) {
 24495        var dx = gpos[0] - mdownGPos[0];
 24496        var dx2 = dx * dx;
 24497        var dy = gpos[1] - mdownGPos[1];
 24498        var dy2 = dy * dy;
 24499        var dist2 = dx2 + dy2;
 24500        r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
 24501      }
 24502  
 24503      var multSelKeyDown = isMultSelKeyDown(e);
 24504  
 24505      if (isOverThresholdDrag) {
 24506        r.hoverData.tapholdCancelled = true;
 24507      }
 24508  
 24509      var updateDragDelta = function updateDragDelta() {
 24510        var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
 24511  
 24512        if (dragDelta.length === 0) {
 24513          dragDelta.push(disp[0]);
 24514          dragDelta.push(disp[1]);
 24515        } else {
 24516          dragDelta[0] += disp[0];
 24517          dragDelta[1] += disp[1];
 24518        }
 24519      };
 24520  
 24521      preventDefault = true;
 24522      triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
 24523        x: pos[0],
 24524        y: pos[1]
 24525      });
 24526  
 24527      var goIntoBoxMode = function goIntoBoxMode() {
 24528        r.data.bgActivePosistion = undefined;
 24529  
 24530        if (!r.hoverData.selecting) {
 24531          cy.emit({
 24532            originalEvent: e,
 24533            type: 'boxstart',
 24534            position: {
 24535              x: pos[0],
 24536              y: pos[1]
 24537            }
 24538          });
 24539        }
 24540  
 24541        select[4] = 1;
 24542        r.hoverData.selecting = true;
 24543        r.redrawHint('select', true);
 24544        r.redraw();
 24545      }; // trigger context drag if rmouse down
 24546  
 24547  
 24548      if (r.hoverData.which === 3) {
 24549        // but only if over threshold
 24550        if (isOverThresholdDrag) {
 24551          var cxtEvt = {
 24552            originalEvent: e,
 24553            type: 'cxtdrag',
 24554            position: {
 24555              x: pos[0],
 24556              y: pos[1]
 24557            }
 24558          };
 24559  
 24560          if (down) {
 24561            down.emit(cxtEvt);
 24562          } else {
 24563            cy.emit(cxtEvt);
 24564          }
 24565  
 24566          r.hoverData.cxtDragged = true;
 24567  
 24568          if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
 24569            if (r.hoverData.cxtOver) {
 24570              r.hoverData.cxtOver.emit({
 24571                originalEvent: e,
 24572                type: 'cxtdragout',
 24573                position: {
 24574                  x: pos[0],
 24575                  y: pos[1]
 24576                }
 24577              });
 24578            }
 24579  
 24580            r.hoverData.cxtOver = near;
 24581  
 24582            if (near) {
 24583              near.emit({
 24584                originalEvent: e,
 24585                type: 'cxtdragover',
 24586                position: {
 24587                  x: pos[0],
 24588                  y: pos[1]
 24589                }
 24590              });
 24591            }
 24592          }
 24593        } // Check if we are drag panning the entire graph
 24594  
 24595      } else if (r.hoverData.dragging) {
 24596        preventDefault = true;
 24597  
 24598        if (cy.panningEnabled() && cy.userPanningEnabled()) {
 24599          var deltaP;
 24600  
 24601          if (r.hoverData.justStartedPan) {
 24602            var mdPos = r.hoverData.mdownPos;
 24603            deltaP = {
 24604              x: (pos[0] - mdPos[0]) * zoom,
 24605              y: (pos[1] - mdPos[1]) * zoom
 24606            };
 24607            r.hoverData.justStartedPan = false;
 24608          } else {
 24609            deltaP = {
 24610              x: disp[0] * zoom,
 24611              y: disp[1] * zoom
 24612            };
 24613          }
 24614  
 24615          cy.panBy(deltaP);
 24616          r.hoverData.dragged = true;
 24617        } // Needs reproject due to pan changing viewport
 24618  
 24619  
 24620        pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
 24621      } else if (select[4] == 1 && (down == null || down.pannable())) {
 24622        if (isOverThresholdDrag) {
 24623          if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
 24624            goIntoBoxMode();
 24625          } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
 24626            var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
 24627  
 24628            if (allowPassthrough) {
 24629              r.hoverData.dragging = true;
 24630              r.hoverData.justStartedPan = true;
 24631              select[4] = 0;
 24632              r.data.bgActivePosistion = array2point(mdownPos);
 24633              r.redrawHint('select', true);
 24634              r.redraw();
 24635            }
 24636          }
 24637  
 24638          if (down && down.pannable() && down.active()) {
 24639            down.unactivate();
 24640          }
 24641        }
 24642      } else {
 24643        if (down && down.pannable() && down.active()) {
 24644          down.unactivate();
 24645        }
 24646  
 24647        if ((!down || !down.grabbed()) && near != last) {
 24648          if (last) {
 24649            triggerEvents(last, ['mouseout', 'tapdragout'], e, {
 24650              x: pos[0],
 24651              y: pos[1]
 24652            });
 24653          }
 24654  
 24655          if (near) {
 24656            triggerEvents(near, ['mouseover', 'tapdragover'], e, {
 24657              x: pos[0],
 24658              y: pos[1]
 24659            });
 24660          }
 24661  
 24662          r.hoverData.last = near;
 24663        }
 24664  
 24665        if (down) {
 24666          if (isOverThresholdDrag) {
 24667            // then we can take action
 24668            if (cy.boxSelectionEnabled() && multSelKeyDown) {
 24669              // then selection overrides
 24670              if (down && down.grabbed()) {
 24671                freeDraggedElements(draggedElements);
 24672                down.emit('freeon');
 24673                draggedElements.emit('free');
 24674  
 24675                if (r.dragData.didDrag) {
 24676                  down.emit('dragfreeon');
 24677                  draggedElements.emit('dragfree');
 24678                }
 24679              }
 24680  
 24681              goIntoBoxMode();
 24682            } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
 24683              // drag node
 24684              var justStartedDrag = !r.dragData.didDrag;
 24685  
 24686              if (justStartedDrag) {
 24687                r.redrawHint('eles', true);
 24688              }
 24689  
 24690              r.dragData.didDrag = true; // indicate that we actually did drag the node
 24691  
 24692              var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
 24693  
 24694              if (!r.hoverData.draggingEles) {
 24695                addNodesToDrag(draggedElements, {
 24696                  inDragLayer: true
 24697                });
 24698              }
 24699  
 24700              var totalShift = {
 24701                x: 0,
 24702                y: 0
 24703              };
 24704  
 24705              if (number(disp[0]) && number(disp[1])) {
 24706                totalShift.x += disp[0];
 24707                totalShift.y += disp[1];
 24708  
 24709                if (justStartedDrag) {
 24710                  var dragDelta = r.hoverData.dragDelta;
 24711  
 24712                  if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
 24713                    totalShift.x += dragDelta[0];
 24714                    totalShift.y += dragDelta[1];
 24715                  }
 24716                }
 24717              }
 24718  
 24719              for (var i = 0; i < draggedElements.length; i++) {
 24720                var dEle = draggedElements[i];
 24721  
 24722                if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
 24723                  toTrigger.merge(dEle);
 24724                }
 24725              }
 24726  
 24727              r.hoverData.draggingEles = true;
 24728              toTrigger.silentShift(totalShift).emit('position drag');
 24729              r.redrawHint('drag', true);
 24730              r.redraw();
 24731            }
 24732          } else {
 24733            // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
 24734            updateDragDelta();
 24735          }
 24736        } // prevent the dragging from triggering text selection on the page
 24737  
 24738  
 24739        preventDefault = true;
 24740      }
 24741  
 24742      select[2] = pos[0];
 24743      select[3] = pos[1];
 24744  
 24745      if (preventDefault) {
 24746        if (e.stopPropagation) e.stopPropagation();
 24747        if (e.preventDefault) e.preventDefault();
 24748        return false;
 24749      }
 24750    }, false);
 24751    r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
 24752      // eslint-disable-line no-undef
 24753      var capture = r.hoverData.capture;
 24754  
 24755      if (!capture) {
 24756        return;
 24757      }
 24758  
 24759      r.hoverData.capture = false;
 24760      var cy = r.cy;
 24761      var pos = r.projectIntoViewport(e.clientX, e.clientY);
 24762      var select = r.selection;
 24763      var near = r.findNearestElement(pos[0], pos[1], true, false);
 24764      var draggedElements = r.dragData.possibleDragElements;
 24765      var down = r.hoverData.down;
 24766      var multSelKeyDown = isMultSelKeyDown(e);
 24767  
 24768      if (r.data.bgActivePosistion) {
 24769        r.redrawHint('select', true);
 24770        r.redraw();
 24771      }
 24772  
 24773      r.hoverData.tapholdCancelled = true;
 24774      r.data.bgActivePosistion = undefined; // not active bg now
 24775  
 24776      if (down) {
 24777        down.unactivate();
 24778      }
 24779  
 24780      if (r.hoverData.which === 3) {
 24781        var cxtEvt = {
 24782          originalEvent: e,
 24783          type: 'cxttapend',
 24784          position: {
 24785            x: pos[0],
 24786            y: pos[1]
 24787          }
 24788        };
 24789  
 24790        if (down) {
 24791          down.emit(cxtEvt);
 24792        } else {
 24793          cy.emit(cxtEvt);
 24794        }
 24795  
 24796        if (!r.hoverData.cxtDragged) {
 24797          var cxtTap = {
 24798            originalEvent: e,
 24799            type: 'cxttap',
 24800            position: {
 24801              x: pos[0],
 24802              y: pos[1]
 24803            }
 24804          };
 24805  
 24806          if (down) {
 24807            down.emit(cxtTap);
 24808          } else {
 24809            cy.emit(cxtTap);
 24810          }
 24811        }
 24812  
 24813        r.hoverData.cxtDragged = false;
 24814        r.hoverData.which = null;
 24815      } else if (r.hoverData.which === 1) {
 24816        triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
 24817          x: pos[0],
 24818          y: pos[1]
 24819        });
 24820  
 24821        if (!r.dragData.didDrag // didn't move a node around
 24822        && !r.hoverData.dragged // didn't pan
 24823        && !r.hoverData.selecting // not box selection
 24824        && !r.hoverData.isOverThresholdDrag // didn't move too much
 24825        ) {
 24826            triggerEvents(down, ['click', 'tap', 'vclick'], e, {
 24827              x: pos[0],
 24828              y: pos[1]
 24829            });
 24830          } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
 24831  
 24832  
 24833        if (down == null && // not mousedown on node
 24834        !r.dragData.didDrag // didn't move the node around
 24835        && !r.hoverData.selecting // not box selection
 24836        && !r.hoverData.dragged // didn't pan
 24837        && !isMultSelKeyDown(e)) {
 24838          cy.$(isSelected).unselect(['tapunselect']);
 24839  
 24840          if (draggedElements.length > 0) {
 24841            r.redrawHint('eles', true);
 24842          }
 24843  
 24844          r.dragData.possibleDragElements = draggedElements = cy.collection();
 24845        } // Single selection
 24846  
 24847  
 24848        if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
 24849          if (near != null && near._private.selectable) {
 24850            if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
 24851              if (near.selected()) {
 24852                near.unselect(['tapunselect']);
 24853              } else {
 24854                near.select(['tapselect']);
 24855              }
 24856            } else {
 24857              if (!multSelKeyDown) {
 24858                cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
 24859                near.select(['tapselect']);
 24860              }
 24861            }
 24862  
 24863            r.redrawHint('eles', true);
 24864          }
 24865        }
 24866  
 24867        if (r.hoverData.selecting) {
 24868          var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
 24869          r.redrawHint('select', true);
 24870  
 24871          if (box.length > 0) {
 24872            r.redrawHint('eles', true);
 24873          }
 24874  
 24875          cy.emit({
 24876            type: 'boxend',
 24877            originalEvent: e,
 24878            position: {
 24879              x: pos[0],
 24880              y: pos[1]
 24881            }
 24882          });
 24883  
 24884          var eleWouldBeSelected = function eleWouldBeSelected(ele) {
 24885            return ele.selectable() && !ele.selected();
 24886          };
 24887  
 24888          if (cy.selectionType() === 'additive') {
 24889            box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
 24890          } else {
 24891            if (!multSelKeyDown) {
 24892              cy.$(isSelected).unmerge(box).unselect();
 24893            }
 24894  
 24895            box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
 24896          } // always need redraw in case eles unselectable
 24897  
 24898  
 24899          r.redraw();
 24900        } // Cancel drag pan
 24901  
 24902  
 24903        if (r.hoverData.dragging) {
 24904          r.hoverData.dragging = false;
 24905          r.redrawHint('select', true);
 24906          r.redrawHint('eles', true);
 24907          r.redraw();
 24908        }
 24909  
 24910        if (!select[4]) {
 24911          r.redrawHint('drag', true);
 24912          r.redrawHint('eles', true);
 24913          var downWasGrabbed = down && down.grabbed();
 24914          freeDraggedElements(draggedElements);
 24915  
 24916          if (downWasGrabbed) {
 24917            down.emit('freeon');
 24918            draggedElements.emit('free');
 24919  
 24920            if (r.dragData.didDrag) {
 24921              down.emit('dragfreeon');
 24922              draggedElements.emit('dragfree');
 24923            }
 24924          }
 24925        }
 24926      } // else not right mouse
 24927  
 24928  
 24929      select[4] = 0;
 24930      r.hoverData.down = null;
 24931      r.hoverData.cxtStarted = false;
 24932      r.hoverData.draggingEles = false;
 24933      r.hoverData.selecting = false;
 24934      r.hoverData.isOverThresholdDrag = false;
 24935      r.dragData.didDrag = false;
 24936      r.hoverData.dragged = false;
 24937      r.hoverData.dragDelta = [];
 24938      r.hoverData.mdownPos = null;
 24939      r.hoverData.mdownGPos = null;
 24940    }, false);
 24941  
 24942    var wheelHandler = function wheelHandler(e) {
 24943      if (r.scrollingPage) {
 24944        return;
 24945      } // while scrolling, ignore wheel-to-zoom
 24946  
 24947  
 24948      var cy = r.cy;
 24949      var pos = r.projectIntoViewport(e.clientX, e.clientY);
 24950      var rpos = [pos[0] * cy.zoom() + cy.pan().x, pos[1] * cy.zoom() + cy.pan().y];
 24951  
 24952      if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
 24953        // if pan dragging or cxt dragging, wheel movements make no zoom
 24954        e.preventDefault();
 24955        return;
 24956      }
 24957  
 24958      if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
 24959        e.preventDefault();
 24960        r.data.wheelZooming = true;
 24961        clearTimeout(r.data.wheelTimeout);
 24962        r.data.wheelTimeout = setTimeout(function () {
 24963          r.data.wheelZooming = false;
 24964          r.redrawHint('eles', true);
 24965          r.redraw();
 24966        }, 150);
 24967        var diff;
 24968  
 24969        if (e.deltaY != null) {
 24970          diff = e.deltaY / -250;
 24971        } else if (e.wheelDeltaY != null) {
 24972          diff = e.wheelDeltaY / 1000;
 24973        } else {
 24974          diff = e.wheelDelta / 1000;
 24975        }
 24976  
 24977        diff = diff * r.wheelSensitivity;
 24978        var needsWheelFix = e.deltaMode === 1;
 24979  
 24980        if (needsWheelFix) {
 24981          // fixes slow wheel events on ff/linux and ff/windows
 24982          diff *= 33;
 24983        }
 24984  
 24985        cy.zoom({
 24986          level: cy.zoom() * Math.pow(10, diff),
 24987          renderedPosition: {
 24988            x: rpos[0],
 24989            y: rpos[1]
 24990          }
 24991        });
 24992      }
 24993    }; // Functions to help with whether mouse wheel should trigger zooming
 24994    // --
 24995  
 24996  
 24997    r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
 24998    // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
 24999    // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
 25000    // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
 25001  
 25002    r.registerBinding(window, 'scroll', function scrollHandler(e) {
 25003      // eslint-disable-line no-unused-vars
 25004      r.scrollingPage = true;
 25005      clearTimeout(r.scrollingPageTimeout);
 25006      r.scrollingPageTimeout = setTimeout(function () {
 25007        r.scrollingPage = false;
 25008      }, 250);
 25009    }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
 25010    // Handle mouseout on Cytoscape container
 25011  
 25012    r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
 25013      var pos = r.projectIntoViewport(e.clientX, e.clientY);
 25014      r.cy.emit({
 25015        originalEvent: e,
 25016        type: 'mouseout',
 25017        position: {
 25018          x: pos[0],
 25019          y: pos[1]
 25020        }
 25021      });
 25022    }, false);
 25023    r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
 25024      var pos = r.projectIntoViewport(e.clientX, e.clientY);
 25025      r.cy.emit({
 25026        originalEvent: e,
 25027        type: 'mouseover',
 25028        position: {
 25029          x: pos[0],
 25030          y: pos[1]
 25031        }
 25032      });
 25033    }, false);
 25034    var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
 25035  
 25036    var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
 25037  
 25038    var center1, modelCenter1; // center point on start pinch to zoom
 25039  
 25040    var offsetLeft, offsetTop;
 25041    var containerWidth, containerHeight;
 25042    var twoFingersStartInside;
 25043  
 25044    var distance = function distance(x1, y1, x2, y2) {
 25045      return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
 25046    };
 25047  
 25048    var distanceSq = function distanceSq(x1, y1, x2, y2) {
 25049      return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
 25050    };
 25051  
 25052    var touchstartHandler;
 25053    r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
 25054      if (!eventInContainer(e)) {
 25055        return;
 25056      }
 25057  
 25058      blurActiveDomElement();
 25059      r.touchData.capture = true;
 25060      r.data.bgActivePosistion = undefined;
 25061      var cy = r.cy;
 25062      var now = r.touchData.now;
 25063      var earlier = r.touchData.earlier;
 25064  
 25065      if (e.touches[0]) {
 25066        var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
 25067        now[0] = pos[0];
 25068        now[1] = pos[1];
 25069      }
 25070  
 25071      if (e.touches[1]) {
 25072        var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
 25073        now[2] = pos[0];
 25074        now[3] = pos[1];
 25075      }
 25076  
 25077      if (e.touches[2]) {
 25078        var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
 25079        now[4] = pos[0];
 25080        now[5] = pos[1];
 25081      } // record starting points for pinch-to-zoom
 25082  
 25083  
 25084      if (e.touches[1]) {
 25085        r.touchData.singleTouchMoved = true;
 25086        freeDraggedElements(r.dragData.touchDragEles);
 25087        var offsets = r.findContainerClientCoords();
 25088        offsetLeft = offsets[0];
 25089        offsetTop = offsets[1];
 25090        containerWidth = offsets[2];
 25091        containerHeight = offsets[3];
 25092        f1x1 = e.touches[0].clientX - offsetLeft;
 25093        f1y1 = e.touches[0].clientY - offsetTop;
 25094        f2x1 = e.touches[1].clientX - offsetLeft;
 25095        f2y1 = e.touches[1].clientY - offsetTop;
 25096        twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
 25097        var pan = cy.pan();
 25098        var zoom = cy.zoom();
 25099        distance1 = distance(f1x1, f1y1, f2x1, f2y1);
 25100        distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
 25101        center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
 25102        modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
 25103  
 25104        var cxtDistThreshold = 200;
 25105        var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
 25106  
 25107        if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
 25108          var near1 = r.findNearestElement(now[0], now[1], true, true);
 25109          var near2 = r.findNearestElement(now[2], now[3], true, true);
 25110  
 25111          if (near1 && near1.isNode()) {
 25112            near1.activate().emit({
 25113              originalEvent: e,
 25114              type: 'cxttapstart',
 25115              position: {
 25116                x: now[0],
 25117                y: now[1]
 25118              }
 25119            });
 25120            r.touchData.start = near1;
 25121          } else if (near2 && near2.isNode()) {
 25122            near2.activate().emit({
 25123              originalEvent: e,
 25124              type: 'cxttapstart',
 25125              position: {
 25126                x: now[0],
 25127                y: now[1]
 25128              }
 25129            });
 25130            r.touchData.start = near2;
 25131          } else {
 25132            cy.emit({
 25133              originalEvent: e,
 25134              type: 'cxttapstart',
 25135              position: {
 25136                x: now[0],
 25137                y: now[1]
 25138              }
 25139            });
 25140          }
 25141  
 25142          if (r.touchData.start) {
 25143            r.touchData.start._private.grabbed = false;
 25144          }
 25145  
 25146          r.touchData.cxt = true;
 25147          r.touchData.cxtDragged = false;
 25148          r.data.bgActivePosistion = undefined;
 25149          r.redraw();
 25150          return;
 25151        }
 25152      }
 25153  
 25154      if (e.touches[2]) {
 25155        // ignore
 25156        // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
 25157        if (cy.boxSelectionEnabled()) {
 25158          e.preventDefault();
 25159        }
 25160      } else if (e.touches[1]) ; else if (e.touches[0]) {
 25161        var nears = r.findNearestElements(now[0], now[1], true, true);
 25162        var near = nears[0];
 25163  
 25164        if (near != null) {
 25165          near.activate();
 25166          r.touchData.start = near;
 25167          r.touchData.starts = nears;
 25168  
 25169          if (r.nodeIsGrabbable(near)) {
 25170            var draggedEles = r.dragData.touchDragEles = cy.collection();
 25171            var selectedNodes = null;
 25172            r.redrawHint('eles', true);
 25173            r.redrawHint('drag', true);
 25174  
 25175            if (near.selected()) {
 25176              // reset drag elements, since near will be added again
 25177              selectedNodes = cy.$(function (ele) {
 25178                return ele.selected() && r.nodeIsGrabbable(ele);
 25179              });
 25180              addNodesToDrag(selectedNodes, {
 25181                addToList: draggedEles
 25182              });
 25183            } else {
 25184              addNodeToDrag(near, {
 25185                addToList: draggedEles
 25186              });
 25187            }
 25188  
 25189            setGrabTarget(near);
 25190  
 25191            var makeEvent = function makeEvent(type) {
 25192              return {
 25193                originalEvent: e,
 25194                type: type,
 25195                position: {
 25196                  x: now[0],
 25197                  y: now[1]
 25198                }
 25199              };
 25200            };
 25201  
 25202            near.emit(makeEvent('grabon'));
 25203  
 25204            if (selectedNodes) {
 25205              selectedNodes.forEach(function (n) {
 25206                n.emit(makeEvent('grab'));
 25207              });
 25208            } else {
 25209              near.emit(makeEvent('grab'));
 25210            }
 25211          }
 25212        }
 25213  
 25214        triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
 25215          x: now[0],
 25216          y: now[1]
 25217        });
 25218  
 25219        if (near == null) {
 25220          r.data.bgActivePosistion = {
 25221            x: pos[0],
 25222            y: pos[1]
 25223          };
 25224          r.redrawHint('select', true);
 25225          r.redraw();
 25226        } // Tap, taphold
 25227        // -----
 25228  
 25229  
 25230        r.touchData.singleTouchMoved = false;
 25231        r.touchData.singleTouchStartTime = +new Date();
 25232        clearTimeout(r.touchData.tapholdTimeout);
 25233        r.touchData.tapholdTimeout = setTimeout(function () {
 25234          if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
 25235          && !r.touchData.selecting // box selection shouldn't allow taphold through
 25236          ) {
 25237              triggerEvents(r.touchData.start, ['taphold'], e, {
 25238                x: now[0],
 25239                y: now[1]
 25240              });
 25241            }
 25242        }, r.tapholdDuration);
 25243      }
 25244  
 25245      if (e.touches.length >= 1) {
 25246        var sPos = r.touchData.startPosition = [];
 25247  
 25248        for (var i = 0; i < now.length; i++) {
 25249          sPos[i] = earlier[i] = now[i];
 25250        }
 25251  
 25252        var touch0 = e.touches[0];
 25253        r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
 25254      }
 25255    }, false);
 25256    var touchmoveHandler;
 25257    r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
 25258      // eslint-disable-line no-undef
 25259      var capture = r.touchData.capture;
 25260  
 25261      if (!capture && !eventInContainer(e)) {
 25262        return;
 25263      }
 25264  
 25265      var select = r.selection;
 25266      var cy = r.cy;
 25267      var now = r.touchData.now;
 25268      var earlier = r.touchData.earlier;
 25269      var zoom = cy.zoom();
 25270  
 25271      if (e.touches[0]) {
 25272        var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
 25273        now[0] = pos[0];
 25274        now[1] = pos[1];
 25275      }
 25276  
 25277      if (e.touches[1]) {
 25278        var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
 25279        now[2] = pos[0];
 25280        now[3] = pos[1];
 25281      }
 25282  
 25283      if (e.touches[2]) {
 25284        var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
 25285        now[4] = pos[0];
 25286        now[5] = pos[1];
 25287      }
 25288  
 25289      var startGPos = r.touchData.startGPosition;
 25290      var isOverThresholdDrag;
 25291  
 25292      if (capture && e.touches[0] && startGPos) {
 25293        var disp = [];
 25294  
 25295        for (var j = 0; j < now.length; j++) {
 25296          disp[j] = now[j] - earlier[j];
 25297        }
 25298  
 25299        var dx = e.touches[0].clientX - startGPos[0];
 25300        var dx2 = dx * dx;
 25301        var dy = e.touches[0].clientY - startGPos[1];
 25302        var dy2 = dy * dy;
 25303        var dist2 = dx2 + dy2;
 25304        isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
 25305      } // context swipe cancelling
 25306  
 25307  
 25308      if (capture && r.touchData.cxt) {
 25309        e.preventDefault();
 25310        var f1x2 = e.touches[0].clientX - offsetLeft,
 25311            f1y2 = e.touches[0].clientY - offsetTop;
 25312        var f2x2 = e.touches[1].clientX - offsetLeft,
 25313            f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
 25314  
 25315        var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
 25316        var factorSq = distance2Sq / distance1Sq;
 25317        var distThreshold = 150;
 25318        var distThresholdSq = distThreshold * distThreshold;
 25319        var factorThreshold = 1.5;
 25320        var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
 25321  
 25322        if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
 25323          r.touchData.cxt = false;
 25324          r.data.bgActivePosistion = undefined;
 25325          r.redrawHint('select', true);
 25326          var cxtEvt = {
 25327            originalEvent: e,
 25328            type: 'cxttapend',
 25329            position: {
 25330              x: now[0],
 25331              y: now[1]
 25332            }
 25333          };
 25334  
 25335          if (r.touchData.start) {
 25336            r.touchData.start.unactivate().emit(cxtEvt);
 25337            r.touchData.start = null;
 25338          } else {
 25339            cy.emit(cxtEvt);
 25340          }
 25341        }
 25342      } // context swipe
 25343  
 25344  
 25345      if (capture && r.touchData.cxt) {
 25346        var cxtEvt = {
 25347          originalEvent: e,
 25348          type: 'cxtdrag',
 25349          position: {
 25350            x: now[0],
 25351            y: now[1]
 25352          }
 25353        };
 25354        r.data.bgActivePosistion = undefined;
 25355        r.redrawHint('select', true);
 25356  
 25357        if (r.touchData.start) {
 25358          r.touchData.start.emit(cxtEvt);
 25359        } else {
 25360          cy.emit(cxtEvt);
 25361        }
 25362  
 25363        if (r.touchData.start) {
 25364          r.touchData.start._private.grabbed = false;
 25365        }
 25366  
 25367        r.touchData.cxtDragged = true;
 25368        var near = r.findNearestElement(now[0], now[1], true, true);
 25369  
 25370        if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
 25371          if (r.touchData.cxtOver) {
 25372            r.touchData.cxtOver.emit({
 25373              originalEvent: e,
 25374              type: 'cxtdragout',
 25375              position: {
 25376                x: now[0],
 25377                y: now[1]
 25378              }
 25379            });
 25380          }
 25381  
 25382          r.touchData.cxtOver = near;
 25383  
 25384          if (near) {
 25385            near.emit({
 25386              originalEvent: e,
 25387              type: 'cxtdragover',
 25388              position: {
 25389                x: now[0],
 25390                y: now[1]
 25391              }
 25392            });
 25393          }
 25394        } // box selection
 25395  
 25396      } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
 25397        e.preventDefault();
 25398        r.data.bgActivePosistion = undefined;
 25399        this.lastThreeTouch = +new Date();
 25400  
 25401        if (!r.touchData.selecting) {
 25402          cy.emit({
 25403            originalEvent: e,
 25404            type: 'boxstart',
 25405            position: {
 25406              x: now[0],
 25407              y: now[1]
 25408            }
 25409          });
 25410        }
 25411  
 25412        r.touchData.selecting = true;
 25413        r.touchData.didSelect = true;
 25414        select[4] = 1;
 25415  
 25416        if (!select || select.length === 0 || select[0] === undefined) {
 25417          select[0] = (now[0] + now[2] + now[4]) / 3;
 25418          select[1] = (now[1] + now[3] + now[5]) / 3;
 25419          select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
 25420          select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
 25421        } else {
 25422          select[2] = (now[0] + now[2] + now[4]) / 3;
 25423          select[3] = (now[1] + now[3] + now[5]) / 3;
 25424        }
 25425  
 25426        r.redrawHint('select', true);
 25427        r.redraw(); // pinch to zoom
 25428      } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
 25429      && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
 25430        // two fingers => pinch to zoom
 25431        e.preventDefault();
 25432        r.data.bgActivePosistion = undefined;
 25433        r.redrawHint('select', true);
 25434        var draggedEles = r.dragData.touchDragEles;
 25435  
 25436        if (draggedEles) {
 25437          r.redrawHint('drag', true);
 25438  
 25439          for (var i = 0; i < draggedEles.length; i++) {
 25440            var de_p = draggedEles[i]._private;
 25441            de_p.grabbed = false;
 25442            de_p.rscratch.inDragLayer = false;
 25443          }
 25444        }
 25445  
 25446        var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
 25447  
 25448        var f1x2 = e.touches[0].clientX - offsetLeft,
 25449            f1y2 = e.touches[0].clientY - offsetTop;
 25450        var f2x2 = e.touches[1].clientX - offsetLeft,
 25451            f2y2 = e.touches[1].clientY - offsetTop;
 25452        var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
 25453        // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
 25454  
 25455        var factor = distance2 / distance1;
 25456  
 25457        if (twoFingersStartInside) {
 25458          // delta finger1
 25459          var df1x = f1x2 - f1x1;
 25460          var df1y = f1y2 - f1y1; // delta finger 2
 25461  
 25462          var df2x = f2x2 - f2x1;
 25463          var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
 25464          // i.e. so pinching cancels out and moving together pans
 25465  
 25466          var tx = (df1x + df2x) / 2;
 25467          var ty = (df1y + df2y) / 2; // now calculate the zoom
 25468  
 25469          var zoom1 = cy.zoom();
 25470          var zoom2 = zoom1 * factor;
 25471          var pan1 = cy.pan(); // the model center point converted to the current rendered pos
 25472  
 25473          var ctrx = modelCenter1[0] * zoom1 + pan1.x;
 25474          var ctry = modelCenter1[1] * zoom1 + pan1.y;
 25475          var pan2 = {
 25476            x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
 25477            y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
 25478          }; // remove dragged eles
 25479  
 25480          if (_start && _start.active()) {
 25481            var draggedEles = r.dragData.touchDragEles;
 25482            freeDraggedElements(draggedEles);
 25483            r.redrawHint('drag', true);
 25484            r.redrawHint('eles', true);
 25485  
 25486            _start.unactivate().emit('freeon');
 25487  
 25488            draggedEles.emit('free');
 25489  
 25490            if (r.dragData.didDrag) {
 25491              _start.emit('dragfreeon');
 25492  
 25493              draggedEles.emit('dragfree');
 25494            }
 25495          }
 25496  
 25497          cy.viewport({
 25498            zoom: zoom2,
 25499            pan: pan2,
 25500            cancelOnFailedZoom: true
 25501          });
 25502          distance1 = distance2;
 25503          f1x1 = f1x2;
 25504          f1y1 = f1y2;
 25505          f2x1 = f2x2;
 25506          f2y1 = f2y2;
 25507          r.pinching = true;
 25508        } // Re-project
 25509  
 25510  
 25511        if (e.touches[0]) {
 25512          var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
 25513          now[0] = pos[0];
 25514          now[1] = pos[1];
 25515        }
 25516  
 25517        if (e.touches[1]) {
 25518          var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
 25519          now[2] = pos[0];
 25520          now[3] = pos[1];
 25521        }
 25522  
 25523        if (e.touches[2]) {
 25524          var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
 25525          now[4] = pos[0];
 25526          now[5] = pos[1];
 25527        }
 25528      } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
 25529      ) {
 25530          var start = r.touchData.start;
 25531          var last = r.touchData.last;
 25532          var near;
 25533  
 25534          if (!r.hoverData.draggingEles && !r.swipePanning) {
 25535            near = r.findNearestElement(now[0], now[1], true, true);
 25536          }
 25537  
 25538          if (capture && start != null) {
 25539            e.preventDefault();
 25540          } // dragging nodes
 25541  
 25542  
 25543          if (capture && start != null && r.nodeIsDraggable(start)) {
 25544            if (isOverThresholdDrag) {
 25545              // then dragging can happen
 25546              var draggedEles = r.dragData.touchDragEles;
 25547              var justStartedDrag = !r.dragData.didDrag;
 25548  
 25549              if (justStartedDrag) {
 25550                addNodesToDrag(draggedEles, {
 25551                  inDragLayer: true
 25552                });
 25553              }
 25554  
 25555              r.dragData.didDrag = true;
 25556              var totalShift = {
 25557                x: 0,
 25558                y: 0
 25559              };
 25560  
 25561              if (number(disp[0]) && number(disp[1])) {
 25562                totalShift.x += disp[0];
 25563                totalShift.y += disp[1];
 25564  
 25565                if (justStartedDrag) {
 25566                  r.redrawHint('eles', true);
 25567                  var dragDelta = r.touchData.dragDelta;
 25568  
 25569                  if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
 25570                    totalShift.x += dragDelta[0];
 25571                    totalShift.y += dragDelta[1];
 25572                  }
 25573                }
 25574              }
 25575  
 25576              r.hoverData.draggingEles = true;
 25577              draggedEles.silentShift(totalShift).emit('position drag');
 25578              r.redrawHint('drag', true);
 25579  
 25580              if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
 25581                r.redrawHint('eles', true);
 25582              }
 25583  
 25584              r.redraw();
 25585            } else {
 25586              // otherise keep track of drag delta for later
 25587              var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
 25588  
 25589              if (dragDelta.length === 0) {
 25590                dragDelta.push(disp[0]);
 25591                dragDelta.push(disp[1]);
 25592              } else {
 25593                dragDelta[0] += disp[0];
 25594                dragDelta[1] += disp[1];
 25595              }
 25596            }
 25597          } // touchmove
 25598  
 25599  
 25600          {
 25601            triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
 25602              x: now[0],
 25603              y: now[1]
 25604            });
 25605  
 25606            if ((!start || !start.grabbed()) && near != last) {
 25607              if (last) {
 25608                last.emit({
 25609                  originalEvent: e,
 25610                  type: 'tapdragout',
 25611                  position: {
 25612                    x: now[0],
 25613                    y: now[1]
 25614                  }
 25615                });
 25616              }
 25617  
 25618              if (near) {
 25619                near.emit({
 25620                  originalEvent: e,
 25621                  type: 'tapdragover',
 25622                  position: {
 25623                    x: now[0],
 25624                    y: now[1]
 25625                  }
 25626                });
 25627              }
 25628            }
 25629  
 25630            r.touchData.last = near;
 25631          } // check to cancel taphold
 25632  
 25633          if (capture) {
 25634            for (var i = 0; i < now.length; i++) {
 25635              if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
 25636                r.touchData.singleTouchMoved = true;
 25637              }
 25638            }
 25639          } // panning
 25640  
 25641  
 25642          if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
 25643            var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
 25644  
 25645            if (allowPassthrough) {
 25646              e.preventDefault();
 25647  
 25648              if (!r.data.bgActivePosistion) {
 25649                r.data.bgActivePosistion = array2point(r.touchData.startPosition);
 25650              }
 25651  
 25652              if (r.swipePanning) {
 25653                cy.panBy({
 25654                  x: disp[0] * zoom,
 25655                  y: disp[1] * zoom
 25656                });
 25657              } else if (isOverThresholdDrag) {
 25658                r.swipePanning = true;
 25659                cy.panBy({
 25660                  x: dx * zoom,
 25661                  y: dy * zoom
 25662                });
 25663  
 25664                if (start) {
 25665                  start.unactivate();
 25666                  r.redrawHint('select', true);
 25667                  r.touchData.start = null;
 25668                }
 25669              }
 25670            } // Re-project
 25671  
 25672  
 25673            var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
 25674            now[0] = pos[0];
 25675            now[1] = pos[1];
 25676          }
 25677        }
 25678  
 25679      for (var j = 0; j < now.length; j++) {
 25680        earlier[j] = now[j];
 25681      } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
 25682  
 25683  
 25684      if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
 25685        r.data.bgActivePosistion = undefined;
 25686        r.redrawHint('select', true);
 25687        r.redraw();
 25688      }
 25689    }, false);
 25690    var touchcancelHandler;
 25691    r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
 25692      // eslint-disable-line no-unused-vars
 25693      var start = r.touchData.start;
 25694      r.touchData.capture = false;
 25695  
 25696      if (start) {
 25697        start.unactivate();
 25698      }
 25699    });
 25700    var touchendHandler;
 25701    r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
 25702      // eslint-disable-line no-unused-vars
 25703      var start = r.touchData.start;
 25704      var capture = r.touchData.capture;
 25705  
 25706      if (capture) {
 25707        if (e.touches.length === 0) {
 25708          r.touchData.capture = false;
 25709        }
 25710  
 25711        e.preventDefault();
 25712      } else {
 25713        return;
 25714      }
 25715  
 25716      var select = r.selection;
 25717      r.swipePanning = false;
 25718      r.hoverData.draggingEles = false;
 25719      var cy = r.cy;
 25720      var zoom = cy.zoom();
 25721      var now = r.touchData.now;
 25722      var earlier = r.touchData.earlier;
 25723  
 25724      if (e.touches[0]) {
 25725        var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
 25726        now[0] = pos[0];
 25727        now[1] = pos[1];
 25728      }
 25729  
 25730      if (e.touches[1]) {
 25731        var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
 25732        now[2] = pos[0];
 25733        now[3] = pos[1];
 25734      }
 25735  
 25736      if (e.touches[2]) {
 25737        var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
 25738        now[4] = pos[0];
 25739        now[5] = pos[1];
 25740      }
 25741  
 25742      if (start) {
 25743        start.unactivate();
 25744      }
 25745  
 25746      var ctxTapend;
 25747  
 25748      if (r.touchData.cxt) {
 25749        ctxTapend = {
 25750          originalEvent: e,
 25751          type: 'cxttapend',
 25752          position: {
 25753            x: now[0],
 25754            y: now[1]
 25755          }
 25756        };
 25757  
 25758        if (start) {
 25759          start.emit(ctxTapend);
 25760        } else {
 25761          cy.emit(ctxTapend);
 25762        }
 25763  
 25764        if (!r.touchData.cxtDragged) {
 25765          var ctxTap = {
 25766            originalEvent: e,
 25767            type: 'cxttap',
 25768            position: {
 25769              x: now[0],
 25770              y: now[1]
 25771            }
 25772          };
 25773  
 25774          if (start) {
 25775            start.emit(ctxTap);
 25776          } else {
 25777            cy.emit(ctxTap);
 25778          }
 25779        }
 25780  
 25781        if (r.touchData.start) {
 25782          r.touchData.start._private.grabbed = false;
 25783        }
 25784  
 25785        r.touchData.cxt = false;
 25786        r.touchData.start = null;
 25787        r.redraw();
 25788        return;
 25789      } // no more box selection if we don't have three fingers
 25790  
 25791  
 25792      if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
 25793        r.touchData.selecting = false;
 25794        var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
 25795        select[0] = undefined;
 25796        select[1] = undefined;
 25797        select[2] = undefined;
 25798        select[3] = undefined;
 25799        select[4] = 0;
 25800        r.redrawHint('select', true);
 25801        cy.emit({
 25802          type: 'boxend',
 25803          originalEvent: e,
 25804          position: {
 25805            x: now[0],
 25806            y: now[1]
 25807          }
 25808        });
 25809  
 25810        var eleWouldBeSelected = function eleWouldBeSelected(ele) {
 25811          return ele.selectable() && !ele.selected();
 25812        };
 25813  
 25814        box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
 25815  
 25816        if (box.nonempty()) {
 25817          r.redrawHint('eles', true);
 25818        }
 25819  
 25820        r.redraw();
 25821      }
 25822  
 25823      if (start != null) {
 25824        start.unactivate();
 25825      }
 25826  
 25827      if (e.touches[2]) {
 25828        r.data.bgActivePosistion = undefined;
 25829        r.redrawHint('select', true);
 25830      } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
 25831        r.data.bgActivePosistion = undefined;
 25832        r.redrawHint('select', true);
 25833        var draggedEles = r.dragData.touchDragEles;
 25834  
 25835        if (start != null) {
 25836          var startWasGrabbed = start._private.grabbed;
 25837          freeDraggedElements(draggedEles);
 25838          r.redrawHint('drag', true);
 25839          r.redrawHint('eles', true);
 25840  
 25841          if (startWasGrabbed) {
 25842            start.emit('freeon');
 25843            draggedEles.emit('free');
 25844  
 25845            if (r.dragData.didDrag) {
 25846              start.emit('dragfreeon');
 25847              draggedEles.emit('dragfree');
 25848            }
 25849          }
 25850  
 25851          triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
 25852            x: now[0],
 25853            y: now[1]
 25854          });
 25855          start.unactivate();
 25856          r.touchData.start = null;
 25857        } else {
 25858          var near = r.findNearestElement(now[0], now[1], true, true);
 25859          triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
 25860            x: now[0],
 25861            y: now[1]
 25862          });
 25863        }
 25864  
 25865        var dx = r.touchData.startPosition[0] - now[0];
 25866        var dx2 = dx * dx;
 25867        var dy = r.touchData.startPosition[1] - now[1];
 25868        var dy2 = dy * dy;
 25869        var dist2 = dx2 + dy2;
 25870        var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
 25871  
 25872        if (!r.touchData.singleTouchMoved) {
 25873          if (!start) {
 25874            cy.$(':selected').unselect(['tapunselect']);
 25875          }
 25876  
 25877          triggerEvents(start, ['tap', 'vclick'], e, {
 25878            x: now[0],
 25879            y: now[1]
 25880          });
 25881        } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
 25882  
 25883  
 25884        if (start != null && !r.dragData.didDrag // didn't drag nodes around
 25885        && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
 25886        ) {
 25887            if (cy.selectionType() === 'single') {
 25888              cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
 25889              start.select(['tapselect']);
 25890            } else {
 25891              if (start.selected()) {
 25892                start.unselect(['tapunselect']);
 25893              } else {
 25894                start.select(['tapselect']);
 25895              }
 25896            }
 25897  
 25898            r.redrawHint('eles', true);
 25899          }
 25900  
 25901        r.touchData.singleTouchMoved = true;
 25902      }
 25903  
 25904      for (var j = 0; j < now.length; j++) {
 25905        earlier[j] = now[j];
 25906      }
 25907  
 25908      r.dragData.didDrag = false; // reset for next touchstart
 25909  
 25910      if (e.touches.length === 0) {
 25911        r.touchData.dragDelta = [];
 25912        r.touchData.startPosition = null;
 25913        r.touchData.startGPosition = null;
 25914        r.touchData.didSelect = false;
 25915      }
 25916  
 25917      if (e.touches.length < 2) {
 25918        if (e.touches.length === 1) {
 25919          // the old start global pos'n may not be the same finger that remains
 25920          r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
 25921        }
 25922  
 25923        r.pinching = false;
 25924        r.redrawHint('eles', true);
 25925        r.redraw();
 25926      } //r.redraw();
 25927  
 25928    }, false); // fallback compatibility layer for ms pointer events
 25929  
 25930    if (typeof TouchEvent === 'undefined') {
 25931      var pointers = [];
 25932  
 25933      var makeTouch = function makeTouch(e) {
 25934        return {
 25935          clientX: e.clientX,
 25936          clientY: e.clientY,
 25937          force: 1,
 25938          identifier: e.pointerId,
 25939          pageX: e.pageX,
 25940          pageY: e.pageY,
 25941          radiusX: e.width / 2,
 25942          radiusY: e.height / 2,
 25943          screenX: e.screenX,
 25944          screenY: e.screenY,
 25945          target: e.target
 25946        };
 25947      };
 25948  
 25949      var makePointer = function makePointer(e) {
 25950        return {
 25951          event: e,
 25952          touch: makeTouch(e)
 25953        };
 25954      };
 25955  
 25956      var addPointer = function addPointer(e) {
 25957        pointers.push(makePointer(e));
 25958      };
 25959  
 25960      var removePointer = function removePointer(e) {
 25961        for (var i = 0; i < pointers.length; i++) {
 25962          var p = pointers[i];
 25963  
 25964          if (p.event.pointerId === e.pointerId) {
 25965            pointers.splice(i, 1);
 25966            return;
 25967          }
 25968        }
 25969      };
 25970  
 25971      var updatePointer = function updatePointer(e) {
 25972        var p = pointers.filter(function (p) {
 25973          return p.event.pointerId === e.pointerId;
 25974        })[0];
 25975        p.event = e;
 25976        p.touch = makeTouch(e);
 25977      };
 25978  
 25979      var addTouchesToEvent = function addTouchesToEvent(e) {
 25980        e.touches = pointers.map(function (p) {
 25981          return p.touch;
 25982        });
 25983      };
 25984  
 25985      var pointerIsMouse = function pointerIsMouse(e) {
 25986        return e.pointerType === 'mouse' || e.pointerType === 4;
 25987      };
 25988  
 25989      r.registerBinding(r.container, 'pointerdown', function (e) {
 25990        if (pointerIsMouse(e)) {
 25991          return;
 25992        } // mouse already handled
 25993  
 25994  
 25995        e.preventDefault();
 25996        addPointer(e);
 25997        addTouchesToEvent(e);
 25998        touchstartHandler(e);
 25999      });
 26000      r.registerBinding(r.container, 'pointerup', function (e) {
 26001        if (pointerIsMouse(e)) {
 26002          return;
 26003        } // mouse already handled
 26004  
 26005  
 26006        removePointer(e);
 26007        addTouchesToEvent(e);
 26008        touchendHandler(e);
 26009      });
 26010      r.registerBinding(r.container, 'pointercancel', function (e) {
 26011        if (pointerIsMouse(e)) {
 26012          return;
 26013        } // mouse already handled
 26014  
 26015  
 26016        removePointer(e);
 26017        addTouchesToEvent(e);
 26018        touchcancelHandler(e);
 26019      });
 26020      r.registerBinding(r.container, 'pointermove', function (e) {
 26021        if (pointerIsMouse(e)) {
 26022          return;
 26023        } // mouse already handled
 26024  
 26025  
 26026        e.preventDefault();
 26027        updatePointer(e);
 26028        addTouchesToEvent(e);
 26029        touchmoveHandler(e);
 26030      });
 26031    }
 26032  };
 26033  
 26034  var BRp$d = {};
 26035  
 26036  BRp$d.generatePolygon = function (name, points) {
 26037    return this.nodeShapes[name] = {
 26038      renderer: this,
 26039      name: name,
 26040      points: points,
 26041      draw: function draw(context, centerX, centerY, width, height) {
 26042        this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
 26043      },
 26044      intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26045        return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
 26046      },
 26047      checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26048        return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
 26049      }
 26050    };
 26051  };
 26052  
 26053  BRp$d.generateEllipse = function () {
 26054    return this.nodeShapes['ellipse'] = {
 26055      renderer: this,
 26056      name: 'ellipse',
 26057      draw: function draw(context, centerX, centerY, width, height) {
 26058        this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
 26059      },
 26060      intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26061        return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
 26062      },
 26063      checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26064        return checkInEllipse(x, y, width, height, centerX, centerY, padding);
 26065      }
 26066    };
 26067  };
 26068  
 26069  BRp$d.generateRoundPolygon = function (name, points) {
 26070    // Pre-compute control points
 26071    // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
 26072    // the unit vectors.
 26073    // For simplicity the layout will be:
 26074    // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
 26075    var allPoints = new Array(points.length * 2);
 26076  
 26077    for (var i = 0; i < points.length / 2; i++) {
 26078      var sourceIndex = i * 2;
 26079      var destIndex = void 0;
 26080  
 26081      if (i < points.length / 2 - 1) {
 26082        destIndex = (i + 1) * 2;
 26083      } else {
 26084        destIndex = 0;
 26085      }
 26086  
 26087      allPoints[i * 4] = points[sourceIndex];
 26088      allPoints[i * 4 + 1] = points[sourceIndex + 1];
 26089      var xDest = points[destIndex] - points[sourceIndex];
 26090      var yDest = points[destIndex + 1] - points[sourceIndex + 1];
 26091      var norm = Math.sqrt(xDest * xDest + yDest * yDest);
 26092      allPoints[i * 4 + 2] = xDest / norm;
 26093      allPoints[i * 4 + 3] = yDest / norm;
 26094    }
 26095  
 26096    return this.nodeShapes[name] = {
 26097      renderer: this,
 26098      name: name,
 26099      points: allPoints,
 26100      draw: function draw(context, centerX, centerY, width, height) {
 26101        this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
 26102      },
 26103      intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26104        return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
 26105      },
 26106      checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26107        return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
 26108      }
 26109    };
 26110  };
 26111  
 26112  BRp$d.generateRoundRectangle = function () {
 26113    return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
 26114      renderer: this,
 26115      name: 'round-rectangle',
 26116      points: generateUnitNgonPointsFitToSquare(4, 0),
 26117      draw: function draw(context, centerX, centerY, width, height) {
 26118        this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
 26119      },
 26120      intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26121        return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
 26122      },
 26123      checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26124        var cornerRadius = getRoundRectangleRadius(width, height);
 26125        var diam = cornerRadius * 2; // Check hBox
 26126  
 26127        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
 26128          return true;
 26129        } // Check vBox
 26130  
 26131  
 26132        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
 26133          return true;
 26134        } // Check top left quarter circle
 26135  
 26136  
 26137        if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
 26138          return true;
 26139        } // Check top right quarter circle
 26140  
 26141  
 26142        if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
 26143          return true;
 26144        } // Check bottom right quarter circle
 26145  
 26146  
 26147        if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
 26148          return true;
 26149        } // Check bottom left quarter circle
 26150  
 26151  
 26152        if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
 26153          return true;
 26154        }
 26155  
 26156        return false;
 26157      }
 26158    };
 26159  };
 26160  
 26161  BRp$d.generateCutRectangle = function () {
 26162    return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
 26163      renderer: this,
 26164      name: 'cut-rectangle',
 26165      cornerLength: getCutRectangleCornerLength(),
 26166      points: generateUnitNgonPointsFitToSquare(4, 0),
 26167      draw: function draw(context, centerX, centerY, width, height) {
 26168        this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
 26169      },
 26170      generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
 26171        var cl = this.cornerLength;
 26172        var hh = height / 2;
 26173        var hw = width / 2;
 26174        var xBegin = centerX - hw;
 26175        var xEnd = centerX + hw;
 26176        var yBegin = centerY - hh;
 26177        var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
 26178  
 26179        return {
 26180          topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
 26181          topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
 26182          bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
 26183          bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
 26184        };
 26185      },
 26186      intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26187        var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
 26188        var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
 26189        return polygonIntersectLine(x, y, pts, nodeX, nodeY);
 26190      },
 26191      checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26192        // Check hBox
 26193        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
 26194          return true;
 26195        } // Check vBox
 26196  
 26197  
 26198        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
 26199          return true;
 26200        }
 26201  
 26202        var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
 26203        return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
 26204      }
 26205    };
 26206  };
 26207  
 26208  BRp$d.generateBarrel = function () {
 26209    return this.nodeShapes['barrel'] = {
 26210      renderer: this,
 26211      name: 'barrel',
 26212      points: generateUnitNgonPointsFitToSquare(4, 0),
 26213      draw: function draw(context, centerX, centerY, width, height) {
 26214        this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
 26215      },
 26216      intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26217        // use two fixed t values for the bezier curve approximation
 26218        var t0 = 0.15;
 26219        var t1 = 0.5;
 26220        var t2 = 0.85;
 26221        var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
 26222  
 26223        var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
 26224          // approximate curve pts based on the two t values
 26225          var m0 = qbezierPtAt({
 26226            x: pts[0],
 26227            y: pts[1]
 26228          }, {
 26229            x: pts[2],
 26230            y: pts[3]
 26231          }, {
 26232            x: pts[4],
 26233            y: pts[5]
 26234          }, t0);
 26235          var m1 = qbezierPtAt({
 26236            x: pts[0],
 26237            y: pts[1]
 26238          }, {
 26239            x: pts[2],
 26240            y: pts[3]
 26241          }, {
 26242            x: pts[4],
 26243            y: pts[5]
 26244          }, t1);
 26245          var m2 = qbezierPtAt({
 26246            x: pts[0],
 26247            y: pts[1]
 26248          }, {
 26249            x: pts[2],
 26250            y: pts[3]
 26251          }, {
 26252            x: pts[4],
 26253            y: pts[5]
 26254          }, t2);
 26255          return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
 26256        };
 26257  
 26258        var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
 26259        return polygonIntersectLine(x, y, pts, nodeX, nodeY);
 26260      },
 26261      generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
 26262        var hh = height / 2;
 26263        var hw = width / 2;
 26264        var xBegin = centerX - hw;
 26265        var xEnd = centerX + hw;
 26266        var yBegin = centerY - hh;
 26267        var yEnd = centerY + hh;
 26268        var curveConstants = getBarrelCurveConstants(width, height);
 26269        var hOffset = curveConstants.heightOffset;
 26270        var wOffset = curveConstants.widthOffset;
 26271        var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
 26272  
 26273        var pts = {
 26274          topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
 26275          topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
 26276          bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
 26277          bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
 26278        };
 26279        pts.topLeft.isTop = true;
 26280        pts.topRight.isTop = true;
 26281        pts.bottomLeft.isBottom = true;
 26282        pts.bottomRight.isBottom = true;
 26283        return pts;
 26284      },
 26285      checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26286        var curveConstants = getBarrelCurveConstants(width, height);
 26287        var hOffset = curveConstants.heightOffset;
 26288        var wOffset = curveConstants.widthOffset; // Check hBox
 26289  
 26290        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
 26291          return true;
 26292        } // Check vBox
 26293  
 26294  
 26295        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
 26296          return true;
 26297        }
 26298  
 26299        var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
 26300  
 26301        var getCurveT = function getCurveT(x, y, curvePts) {
 26302          var x0 = curvePts[4];
 26303          var x1 = curvePts[2];
 26304          var x2 = curvePts[0];
 26305          var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
 26306  
 26307          var y2 = curvePts[1];
 26308          var xMin = Math.min(x0, x2);
 26309          var xMax = Math.max(x0, x2);
 26310          var yMin = Math.min(y0, y2);
 26311          var yMax = Math.max(y0, y2);
 26312  
 26313          if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
 26314            var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
 26315            var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
 26316            var validRoots = roots.filter(function (r) {
 26317              return 0 <= r && r <= 1;
 26318            });
 26319  
 26320            if (validRoots.length > 0) {
 26321              return validRoots[0];
 26322            }
 26323          }
 26324  
 26325          return null;
 26326        };
 26327  
 26328        var curveRegions = Object.keys(barrelCurvePts);
 26329  
 26330        for (var i = 0; i < curveRegions.length; i++) {
 26331          var corner = curveRegions[i];
 26332          var cornerPts = barrelCurvePts[corner];
 26333          var t = getCurveT(x, y, cornerPts);
 26334  
 26335          if (t == null) {
 26336            continue;
 26337          }
 26338  
 26339          var y0 = cornerPts[5];
 26340          var y1 = cornerPts[3];
 26341          var y2 = cornerPts[1];
 26342          var bezY = qbezierAt(y0, y1, y2, t);
 26343  
 26344          if (cornerPts.isTop && bezY <= y) {
 26345            return true;
 26346          }
 26347  
 26348          if (cornerPts.isBottom && y <= bezY) {
 26349            return true;
 26350          }
 26351        }
 26352  
 26353        return false;
 26354      }
 26355    };
 26356  };
 26357  
 26358  BRp$d.generateBottomRoundrectangle = function () {
 26359    return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
 26360      renderer: this,
 26361      name: 'bottom-round-rectangle',
 26362      points: generateUnitNgonPointsFitToSquare(4, 0),
 26363      draw: function draw(context, centerX, centerY, width, height) {
 26364        this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
 26365      },
 26366      intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26367        var topStartX = nodeX - (width / 2 + padding);
 26368        var topStartY = nodeY - (height / 2 + padding);
 26369        var topEndY = topStartY;
 26370        var topEndX = nodeX + (width / 2 + padding);
 26371        var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
 26372  
 26373        if (topIntersections.length > 0) {
 26374          return topIntersections;
 26375        }
 26376  
 26377        return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
 26378      },
 26379      checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26380        var cornerRadius = getRoundRectangleRadius(width, height);
 26381        var diam = 2 * cornerRadius; // Check hBox
 26382  
 26383        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
 26384          return true;
 26385        } // Check vBox
 26386  
 26387  
 26388        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
 26389          return true;
 26390        } // check non-rounded top side
 26391  
 26392  
 26393        var outerWidth = width / 2 + 2 * padding;
 26394        var outerHeight = height / 2 + 2 * padding;
 26395        var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
 26396  
 26397        if (pointInsidePolygonPoints(x, y, points)) {
 26398          return true;
 26399        } // Check bottom right quarter circle
 26400  
 26401  
 26402        if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
 26403          return true;
 26404        } // Check bottom left quarter circle
 26405  
 26406  
 26407        if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
 26408          return true;
 26409        }
 26410  
 26411        return false;
 26412      }
 26413    };
 26414  };
 26415  
 26416  BRp$d.registerNodeShapes = function () {
 26417    var nodeShapes = this.nodeShapes = {};
 26418    var renderer = this;
 26419    this.generateEllipse();
 26420    this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
 26421    this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
 26422    this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
 26423    nodeShapes['square'] = nodeShapes['rectangle'];
 26424    this.generateRoundRectangle();
 26425    this.generateCutRectangle();
 26426    this.generateBarrel();
 26427    this.generateBottomRoundrectangle();
 26428    {
 26429      var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
 26430      this.generatePolygon('diamond', diamondPoints);
 26431      this.generateRoundPolygon('round-diamond', diamondPoints);
 26432    }
 26433    this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
 26434    this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
 26435    this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
 26436    this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
 26437    this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
 26438    this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
 26439    this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
 26440    this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
 26441    var star5Points = new Array(20);
 26442    {
 26443      var outerPoints = generateUnitNgonPoints(5, 0);
 26444      var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
 26445  
 26446      var innerRadius = 0.5 * (3 - Math.sqrt(5));
 26447      innerRadius *= 1.57;
 26448  
 26449      for (var i = 0; i < innerPoints.length / 2; i++) {
 26450        innerPoints[i * 2] *= innerRadius;
 26451        innerPoints[i * 2 + 1] *= innerRadius;
 26452      }
 26453  
 26454      for (var i = 0; i < 20 / 4; i++) {
 26455        star5Points[i * 4] = outerPoints[i * 2];
 26456        star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
 26457        star5Points[i * 4 + 2] = innerPoints[i * 2];
 26458        star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
 26459      }
 26460    }
 26461    star5Points = fitPolygonToSquare(star5Points);
 26462    this.generatePolygon('star', star5Points);
 26463    this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
 26464    this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
 26465    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]);
 26466    {
 26467      var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
 26468      this.generatePolygon('tag', tagPoints);
 26469      this.generateRoundPolygon('round-tag', tagPoints);
 26470    }
 26471  
 26472    nodeShapes.makePolygon = function (points) {
 26473      // use caching on user-specified polygons so they are as fast as native shapes
 26474      var key = points.join('$');
 26475      var name = 'polygon-' + key;
 26476      var shape;
 26477  
 26478      if (shape = this[name]) {
 26479        // got cached shape
 26480        return shape;
 26481      } // create and cache new shape
 26482  
 26483  
 26484      return renderer.generatePolygon(name, points);
 26485    };
 26486  };
 26487  
 26488  var BRp$e = {};
 26489  
 26490  BRp$e.timeToRender = function () {
 26491    return this.redrawTotalTime / this.redrawCount;
 26492  };
 26493  
 26494  BRp$e.redraw = function (options) {
 26495    options = options || staticEmptyObject();
 26496    var r = this;
 26497  
 26498    if (r.averageRedrawTime === undefined) {
 26499      r.averageRedrawTime = 0;
 26500    }
 26501  
 26502    if (r.lastRedrawTime === undefined) {
 26503      r.lastRedrawTime = 0;
 26504    }
 26505  
 26506    if (r.lastDrawTime === undefined) {
 26507      r.lastDrawTime = 0;
 26508    }
 26509  
 26510    r.requestedFrame = true;
 26511    r.renderOptions = options;
 26512  };
 26513  
 26514  BRp$e.beforeRender = function (fn, priority) {
 26515    // the renderer can't add tick callbacks when destroyed
 26516    if (this.destroyed) {
 26517      return;
 26518    }
 26519  
 26520    if (priority == null) {
 26521      error('Priority is not optional for beforeRender');
 26522    }
 26523  
 26524    var cbs = this.beforeRenderCallbacks;
 26525    cbs.push({
 26526      fn: fn,
 26527      priority: priority
 26528    }); // higher priority callbacks executed first
 26529  
 26530    cbs.sort(function (a, b) {
 26531      return b.priority - a.priority;
 26532    });
 26533  };
 26534  
 26535  var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
 26536    var cbs = r.beforeRenderCallbacks;
 26537  
 26538    for (var i = 0; i < cbs.length; i++) {
 26539      cbs[i].fn(willDraw, startTime);
 26540    }
 26541  };
 26542  
 26543  BRp$e.startRenderLoop = function () {
 26544    var r = this;
 26545    var cy = r.cy;
 26546  
 26547    if (r.renderLoopStarted) {
 26548      return;
 26549    } else {
 26550      r.renderLoopStarted = true;
 26551    }
 26552  
 26553    var renderFn = function renderFn(requestTime) {
 26554      if (r.destroyed) {
 26555        return;
 26556      }
 26557  
 26558      if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
 26559        beforeRenderCallbacks(r, true, requestTime);
 26560        var startTime = performanceNow();
 26561        r.render(r.renderOptions);
 26562        var endTime = r.lastDrawTime = performanceNow();
 26563  
 26564        if (r.averageRedrawTime === undefined) {
 26565          r.averageRedrawTime = endTime - startTime;
 26566        }
 26567  
 26568        if (r.redrawCount === undefined) {
 26569          r.redrawCount = 0;
 26570        }
 26571  
 26572        r.redrawCount++;
 26573  
 26574        if (r.redrawTotalTime === undefined) {
 26575          r.redrawTotalTime = 0;
 26576        }
 26577  
 26578        var duration = endTime - startTime;
 26579        r.redrawTotalTime += duration;
 26580        r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
 26581  
 26582        r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
 26583        r.requestedFrame = false;
 26584      } else {
 26585        beforeRenderCallbacks(r, false, requestTime);
 26586      }
 26587  
 26588      r.skipFrame = false;
 26589      requestAnimationFrame(renderFn);
 26590    };
 26591  
 26592    requestAnimationFrame(renderFn);
 26593  };
 26594  
 26595  var BaseRenderer = function BaseRenderer(options) {
 26596    this.init(options);
 26597  };
 26598  
 26599  var BR = BaseRenderer;
 26600  var BRp$f = BR.prototype;
 26601  BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
 26602  
 26603  BRp$f.init = function (options) {
 26604    var r = this;
 26605    r.options = options;
 26606    r.cy = options.cy;
 26607    var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
 26608  
 26609    if (window$1) {
 26610      var document = window$1.document;
 26611      var head = document.head;
 26612      var stylesheetId = '__________cytoscape_stylesheet';
 26613      var className = '__________cytoscape_container';
 26614      var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
 26615  
 26616      if (ctr.className.indexOf(className) < 0) {
 26617        ctr.className = (ctr.className || '') + ' ' + className;
 26618      }
 26619  
 26620      if (!stylesheetAlreadyExists) {
 26621        var stylesheet = document.createElement('style');
 26622        stylesheet.id = stylesheetId;
 26623        stylesheet.innerHTML = '.' + className + ' { position: relative; }';
 26624        head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
 26625      }
 26626  
 26627      var computedStyle = window$1.getComputedStyle(ctr);
 26628      var position = computedStyle.getPropertyValue('position');
 26629  
 26630      if (position === 'static') {
 26631        warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
 26632      }
 26633    }
 26634  
 26635    r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
 26636  
 26637    r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
 26638  
 26639    r.hoverData = {
 26640      down: null,
 26641      last: null,
 26642      downTime: null,
 26643      triggerMode: null,
 26644      dragging: false,
 26645      initialPan: [null, null],
 26646      capture: false
 26647    };
 26648    r.dragData = {
 26649      possibleDragElements: []
 26650    };
 26651    r.touchData = {
 26652      start: null,
 26653      capture: false,
 26654      // These 3 fields related to tap, taphold events
 26655      startPosition: [null, null, null, null, null, null],
 26656      singleTouchStartTime: null,
 26657      singleTouchMoved: true,
 26658      now: [null, null, null, null, null, null],
 26659      earlier: [null, null, null, null, null, null]
 26660    };
 26661    r.redraws = 0;
 26662    r.showFps = options.showFps;
 26663    r.debug = options.debug;
 26664    r.hideEdgesOnViewport = options.hideEdgesOnViewport;
 26665    r.textureOnViewport = options.textureOnViewport;
 26666    r.wheelSensitivity = options.wheelSensitivity;
 26667    r.motionBlurEnabled = options.motionBlur; // on by default
 26668  
 26669    r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
 26670    r.motionBlur = options.motionBlur; // for initial kick off
 26671  
 26672    r.motionBlurOpacity = options.motionBlurOpacity;
 26673    r.motionBlurTransparency = 1 - r.motionBlurOpacity;
 26674    r.motionBlurPxRatio = 1;
 26675    r.mbPxRBlurry = 1; //0.8;
 26676  
 26677    r.minMbLowQualFrames = 4;
 26678    r.fullQualityMb = false;
 26679    r.clearedForMotionBlur = [];
 26680    r.desktopTapThreshold = options.desktopTapThreshold;
 26681    r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
 26682    r.touchTapThreshold = options.touchTapThreshold;
 26683    r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
 26684    r.tapholdDuration = 500;
 26685    r.bindings = [];
 26686    r.beforeRenderCallbacks = [];
 26687    r.beforeRenderPriorities = {
 26688      // higher priority execs before lower one
 26689      animations: 400,
 26690      eleCalcs: 300,
 26691      eleTxrDeq: 200,
 26692      lyrTxrDeq: 150,
 26693      lyrTxrSkip: 100
 26694    };
 26695    r.registerNodeShapes();
 26696    r.registerArrowShapes();
 26697    r.registerCalculationListeners();
 26698  };
 26699  
 26700  BRp$f.notify = function (eventName, eles) {
 26701    var r = this;
 26702    var cy = r.cy; // the renderer can't be notified after it's destroyed
 26703  
 26704    if (this.destroyed) {
 26705      return;
 26706    }
 26707  
 26708    if (eventName === 'init') {
 26709      r.load();
 26710      return;
 26711    }
 26712  
 26713    if (eventName === 'destroy') {
 26714      r.destroy();
 26715      return;
 26716    }
 26717  
 26718    if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
 26719      r.invalidateCachedZSortedEles();
 26720    }
 26721  
 26722    if (eventName === 'viewport') {
 26723      r.redrawHint('select', true);
 26724    }
 26725  
 26726    if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
 26727      r.invalidateContainerClientCoordsCache();
 26728      r.matchCanvasSize(r.container);
 26729    }
 26730  
 26731    r.redrawHint('eles', true);
 26732    r.redrawHint('drag', true);
 26733    this.startRenderLoop();
 26734    this.redraw();
 26735  };
 26736  
 26737  BRp$f.destroy = function () {
 26738    var r = this;
 26739    r.destroyed = true;
 26740    r.cy.stopAnimationLoop();
 26741  
 26742    for (var i = 0; i < r.bindings.length; i++) {
 26743      var binding = r.bindings[i];
 26744      var b = binding;
 26745      var tgt = b.target;
 26746      (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
 26747    }
 26748  
 26749    r.bindings = [];
 26750    r.beforeRenderCallbacks = [];
 26751    r.onUpdateEleCalcsFns = [];
 26752  
 26753    if (r.removeObserver) {
 26754      r.removeObserver.disconnect();
 26755    }
 26756  
 26757    if (r.styleObserver) {
 26758      r.styleObserver.disconnect();
 26759    }
 26760  
 26761    if (r.resizeObserver) {
 26762      r.resizeObserver.disconnect();
 26763    }
 26764  
 26765    if (r.labelCalcDiv) {
 26766      try {
 26767        document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
 26768      } catch (e) {// ie10 issue #1014
 26769      }
 26770    }
 26771  };
 26772  
 26773  BRp$f.isHeadless = function () {
 26774    return false;
 26775  };
 26776  
 26777  [BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
 26778    extend(BRp$f, props);
 26779  });
 26780  
 26781  var fullFpsTime = 1000 / 60; // assume 60 frames per second
 26782  
 26783  var defs = {
 26784    setupDequeueing: function setupDequeueing(opts) {
 26785      return function setupDequeueingImpl() {
 26786        var self = this;
 26787        var r = this.renderer;
 26788  
 26789        if (self.dequeueingSetup) {
 26790          return;
 26791        } else {
 26792          self.dequeueingSetup = true;
 26793        }
 26794  
 26795        var queueRedraw = util(function () {
 26796          r.redrawHint('eles', true);
 26797          r.redrawHint('drag', true);
 26798          r.redraw();
 26799        }, opts.deqRedrawThreshold);
 26800  
 26801        var dequeue = function dequeue(willDraw, frameStartTime) {
 26802          var startTime = performanceNow();
 26803          var avgRenderTime = r.averageRedrawTime;
 26804          var renderTime = r.lastRedrawTime;
 26805          var deqd = [];
 26806          var extent = r.cy.extent();
 26807          var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
 26808          // queue won't automatically be flushed before dequeueing starts
 26809  
 26810          if (!willDraw) {
 26811            r.flushRenderedStyleQueue();
 26812          }
 26813  
 26814          while (true) {
 26815            // eslint-disable-line no-constant-condition
 26816            var now = performanceNow();
 26817            var duration = now - startTime;
 26818            var frameDuration = now - frameStartTime;
 26819  
 26820            if (renderTime < fullFpsTime) {
 26821              // if we're rendering faster than the ideal fps, then do dequeueing
 26822              // during all of the remaining frame time
 26823              var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
 26824  
 26825              if (frameDuration >= opts.deqFastCost * timeAvailable) {
 26826                break;
 26827              }
 26828            } else {
 26829              if (willDraw) {
 26830                if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
 26831                  break;
 26832                }
 26833              } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
 26834                break;
 26835              }
 26836            }
 26837  
 26838            var thisDeqd = opts.deq(self, pixelRatio, extent);
 26839  
 26840            if (thisDeqd.length > 0) {
 26841              for (var i = 0; i < thisDeqd.length; i++) {
 26842                deqd.push(thisDeqd[i]);
 26843              }
 26844            } else {
 26845              break;
 26846            }
 26847          } // callbacks on dequeue
 26848  
 26849  
 26850          if (deqd.length > 0) {
 26851            opts.onDeqd(self, deqd);
 26852  
 26853            if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
 26854              queueRedraw();
 26855            }
 26856          }
 26857        };
 26858  
 26859        var priority = opts.priority || noop;
 26860        r.beforeRender(dequeue, priority(self));
 26861      };
 26862    }
 26863  };
 26864  
 26865  // Uses keys so elements may share the same cache.
 26866  
 26867  var ElementTextureCacheLookup =
 26868  /*#__PURE__*/
 26869  function () {
 26870    function ElementTextureCacheLookup(getKey) {
 26871      var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
 26872  
 26873      _classCallCheck(this, ElementTextureCacheLookup);
 26874  
 26875      this.idsByKey = new Map$1();
 26876      this.keyForId = new Map$1();
 26877      this.cachesByLvl = new Map$1();
 26878      this.lvls = [];
 26879      this.getKey = getKey;
 26880      this.doesEleInvalidateKey = doesEleInvalidateKey;
 26881    }
 26882  
 26883    _createClass(ElementTextureCacheLookup, [{
 26884      key: "getIdsFor",
 26885      value: function getIdsFor(key) {
 26886        if (key == null) {
 26887          error("Can not get id list for null key");
 26888        }
 26889  
 26890        var idsByKey = this.idsByKey;
 26891        var ids = this.idsByKey.get(key);
 26892  
 26893        if (!ids) {
 26894          ids = new Set$1();
 26895          idsByKey.set(key, ids);
 26896        }
 26897  
 26898        return ids;
 26899      }
 26900    }, {
 26901      key: "addIdForKey",
 26902      value: function addIdForKey(key, id) {
 26903        if (key != null) {
 26904          this.getIdsFor(key).add(id);
 26905        }
 26906      }
 26907    }, {
 26908      key: "deleteIdForKey",
 26909      value: function deleteIdForKey(key, id) {
 26910        if (key != null) {
 26911          this.getIdsFor(key)["delete"](id);
 26912        }
 26913      }
 26914    }, {
 26915      key: "getNumberOfIdsForKey",
 26916      value: function getNumberOfIdsForKey(key) {
 26917        if (key == null) {
 26918          return 0;
 26919        } else {
 26920          return this.getIdsFor(key).size;
 26921        }
 26922      }
 26923    }, {
 26924      key: "updateKeyMappingFor",
 26925      value: function updateKeyMappingFor(ele) {
 26926        var id = ele.id();
 26927        var prevKey = this.keyForId.get(id);
 26928        var currKey = this.getKey(ele);
 26929        this.deleteIdForKey(prevKey, id);
 26930        this.addIdForKey(currKey, id);
 26931        this.keyForId.set(id, currKey);
 26932      }
 26933    }, {
 26934      key: "deleteKeyMappingFor",
 26935      value: function deleteKeyMappingFor(ele) {
 26936        var id = ele.id();
 26937        var prevKey = this.keyForId.get(id);
 26938        this.deleteIdForKey(prevKey, id);
 26939        this.keyForId["delete"](id);
 26940      }
 26941    }, {
 26942      key: "keyHasChangedFor",
 26943      value: function keyHasChangedFor(ele) {
 26944        var id = ele.id();
 26945        var prevKey = this.keyForId.get(id);
 26946        var newKey = this.getKey(ele);
 26947        return prevKey !== newKey;
 26948      }
 26949    }, {
 26950      key: "isInvalid",
 26951      value: function isInvalid(ele) {
 26952        return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
 26953      }
 26954    }, {
 26955      key: "getCachesAt",
 26956      value: function getCachesAt(lvl) {
 26957        var cachesByLvl = this.cachesByLvl,
 26958            lvls = this.lvls;
 26959        var caches = cachesByLvl.get(lvl);
 26960  
 26961        if (!caches) {
 26962          caches = new Map$1();
 26963          cachesByLvl.set(lvl, caches);
 26964          lvls.push(lvl);
 26965        }
 26966  
 26967        return caches;
 26968      }
 26969    }, {
 26970      key: "getCache",
 26971      value: function getCache(key, lvl) {
 26972        return this.getCachesAt(lvl).get(key);
 26973      }
 26974    }, {
 26975      key: "get",
 26976      value: function get(ele, lvl) {
 26977        var key = this.getKey(ele);
 26978        var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
 26979  
 26980        if (cache != null) {
 26981          this.updateKeyMappingFor(ele);
 26982        }
 26983  
 26984        return cache;
 26985      }
 26986    }, {
 26987      key: "getForCachedKey",
 26988      value: function getForCachedKey(ele, lvl) {
 26989        var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
 26990  
 26991        var cache = this.getCache(key, lvl);
 26992        return cache;
 26993      }
 26994    }, {
 26995      key: "hasCache",
 26996      value: function hasCache(key, lvl) {
 26997        return this.getCachesAt(lvl).has(key);
 26998      }
 26999    }, {
 27000      key: "has",
 27001      value: function has(ele, lvl) {
 27002        var key = this.getKey(ele);
 27003        return this.hasCache(key, lvl);
 27004      }
 27005    }, {
 27006      key: "setCache",
 27007      value: function setCache(key, lvl, cache) {
 27008        cache.key = key;
 27009        this.getCachesAt(lvl).set(key, cache);
 27010      }
 27011    }, {
 27012      key: "set",
 27013      value: function set(ele, lvl, cache) {
 27014        var key = this.getKey(ele);
 27015        this.setCache(key, lvl, cache);
 27016        this.updateKeyMappingFor(ele);
 27017      }
 27018    }, {
 27019      key: "deleteCache",
 27020      value: function deleteCache(key, lvl) {
 27021        this.getCachesAt(lvl)["delete"](key);
 27022      }
 27023    }, {
 27024      key: "delete",
 27025      value: function _delete(ele, lvl) {
 27026        var key = this.getKey(ele);
 27027        this.deleteCache(key, lvl);
 27028      }
 27029    }, {
 27030      key: "invalidateKey",
 27031      value: function invalidateKey(key) {
 27032        var _this = this;
 27033  
 27034        this.lvls.forEach(function (lvl) {
 27035          return _this.deleteCache(key, lvl);
 27036        });
 27037      } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
 27038  
 27039    }, {
 27040      key: "invalidate",
 27041      value: function invalidate(ele) {
 27042        var id = ele.id();
 27043        var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
 27044  
 27045        this.deleteKeyMappingFor(ele);
 27046        var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
 27047  
 27048        if (entireKeyInvalidated) {
 27049          // clear mapping for current key
 27050          this.invalidateKey(key);
 27051        }
 27052  
 27053        return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
 27054      }
 27055    }]);
 27056  
 27057    return ElementTextureCacheLookup;
 27058  }();
 27059  
 27060  var minTxrH = 25; // the size of the texture cache for small height eles (special case)
 27061  
 27062  var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
 27063  
 27064  var minLvl = -4; // when scaling smaller than that we don't need to re-render
 27065  
 27066  var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
 27067  
 27068  var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
 27069  
 27070  var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
 27071  
 27072  var defTxrWidth = 1024; // default/minimum texture width
 27073  
 27074  var maxTxrW = 1024; // the maximum width of a texture
 27075  
 27076  var maxTxrH = 1024; // the maximum height of a texture
 27077  
 27078  var minUtility = 0.2; // if usage of texture is less than this, it is retired
 27079  
 27080  var maxFullness = 0.8; // fullness of texture after which queue removal is checked
 27081  
 27082  var maxFullnessChecks = 10; // dequeued after this many checks
 27083  
 27084  var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
 27085  
 27086  var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
 27087  
 27088  var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
 27089  
 27090  var deqFastCost = 0.9; // % of frame time to be used when >60fps
 27091  
 27092  var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
 27093  
 27094  var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
 27095  
 27096  var getTxrReasons = {
 27097    dequeue: 'dequeue',
 27098    downscale: 'downscale',
 27099    highQuality: 'highQuality'
 27100  };
 27101  var initDefaults = defaults({
 27102    getKey: null,
 27103    doesEleInvalidateKey: falsify,
 27104    drawElement: null,
 27105    getBoundingBox: null,
 27106    getRotationPoint: null,
 27107    getRotationOffset: null,
 27108    isVisible: trueify,
 27109    allowEdgeTxrCaching: true,
 27110    allowParentTxrCaching: true
 27111  });
 27112  
 27113  var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
 27114    var self = this;
 27115    self.renderer = renderer;
 27116    self.onDequeues = [];
 27117    var opts = initDefaults(initOptions);
 27118    extend(self, opts);
 27119    self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
 27120    self.setupDequeueing();
 27121  };
 27122  
 27123  var ETCp = ElementTextureCache.prototype;
 27124  ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
 27125  
 27126  ETCp.getTextureQueue = function (txrH) {
 27127    var self = this;
 27128    self.eleImgCaches = self.eleImgCaches || {};
 27129    return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
 27130  }; // the list of usused textures which can be recycled (in use in texture queue)
 27131  
 27132  
 27133  ETCp.getRetiredTextureQueue = function (txrH) {
 27134    var self = this;
 27135    var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
 27136    var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
 27137    return rtxtrQ;
 27138  }; // queue of element draw requests at different scale levels
 27139  
 27140  
 27141  ETCp.getElementQueue = function () {
 27142    var self = this;
 27143    var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) {
 27144      return b.reqs - a.reqs;
 27145    });
 27146    return q;
 27147  }; // queue of element draw requests at different scale levels (element id lookup)
 27148  
 27149  
 27150  ETCp.getElementKeyToQueue = function () {
 27151    var self = this;
 27152    var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
 27153    return k2q;
 27154  };
 27155  
 27156  ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
 27157    var self = this;
 27158    var r = this.renderer;
 27159    var zoom = r.cy.zoom();
 27160    var lookup = this.lookup;
 27161  
 27162    if (bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible()) {
 27163      return null;
 27164    }
 27165  
 27166    if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
 27167      return null;
 27168    }
 27169  
 27170    if (lvl == null) {
 27171      lvl = Math.ceil(log2(zoom * pxRatio));
 27172    }
 27173  
 27174    if (lvl < minLvl) {
 27175      lvl = minLvl;
 27176    } else if (zoom >= maxZoom || lvl > maxLvl) {
 27177      return null;
 27178    }
 27179  
 27180    var scale = Math.pow(2, lvl);
 27181    var eleScaledH = bb.h * scale;
 27182    var eleScaledW = bb.w * scale;
 27183    var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
 27184  
 27185    if (!this.isVisible(ele, scaledLabelShown)) {
 27186      return null;
 27187    }
 27188  
 27189    var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
 27190  
 27191    if (eleCache && eleCache.invalidated) {
 27192      eleCache.invalidated = false;
 27193      eleCache.texture.invalidatedWidth -= eleCache.width;
 27194    }
 27195  
 27196    if (eleCache) {
 27197      return eleCache;
 27198    }
 27199  
 27200    var txrH; // which texture height this ele belongs to
 27201  
 27202    if (eleScaledH <= minTxrH) {
 27203      txrH = minTxrH;
 27204    } else if (eleScaledH <= txrStepH) {
 27205      txrH = txrStepH;
 27206    } else {
 27207      txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
 27208    }
 27209  
 27210    if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
 27211      return null; // caching large elements is not efficient
 27212    }
 27213  
 27214    var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
 27215  
 27216    var txr = txrQ[txrQ.length - 2];
 27217  
 27218    var addNewTxr = function addNewTxr() {
 27219      return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
 27220    }; // try the last one if there is no second last one
 27221  
 27222  
 27223    if (!txr) {
 27224      txr = txrQ[txrQ.length - 1];
 27225    } // if the last one doesn't exist, we need a first one
 27226  
 27227  
 27228    if (!txr) {
 27229      txr = addNewTxr();
 27230    } // if there's no room in the current texture, we need a new one
 27231  
 27232  
 27233    if (txr.width - txr.usedWidth < eleScaledW) {
 27234      txr = addNewTxr();
 27235    }
 27236  
 27237    var scalableFrom = function scalableFrom(otherCache) {
 27238      return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
 27239    };
 27240  
 27241    var deqing = reason && reason === getTxrReasons.dequeue;
 27242    var highQualityReq = reason && reason === getTxrReasons.highQuality;
 27243    var downscaleReq = reason && reason === getTxrReasons.downscale;
 27244    var higherCache; // the nearest cache with a higher level
 27245  
 27246    for (var l = lvl + 1; l <= maxLvl; l++) {
 27247      var c = lookup.get(ele, l);
 27248  
 27249      if (c) {
 27250        higherCache = c;
 27251        break;
 27252      }
 27253    }
 27254  
 27255    var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
 27256  
 27257    var downscale = function downscale() {
 27258      txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
 27259    }; // reset ele area in texture
 27260  
 27261  
 27262    txr.context.setTransform(1, 0, 0, 1, 0, 0);
 27263    txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
 27264  
 27265    if (scalableFrom(oneUpCache)) {
 27266      // then we can relatively cheaply rescale the existing image w/o rerendering
 27267      downscale();
 27268    } else if (scalableFrom(higherCache)) {
 27269      // then use the higher cache for now and queue the next level down
 27270      // to cheaply scale towards the smaller level
 27271      if (highQualityReq) {
 27272        for (var _l = higherCache.level; _l > lvl; _l--) {
 27273          oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
 27274        }
 27275  
 27276        downscale();
 27277      } else {
 27278        self.queueElement(ele, higherCache.level - 1);
 27279        return higherCache;
 27280      }
 27281    } else {
 27282      var lowerCache; // the nearest cache with a lower level
 27283  
 27284      if (!deqing && !highQualityReq && !downscaleReq) {
 27285        for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
 27286          var _c = lookup.get(ele, _l2);
 27287  
 27288          if (_c) {
 27289            lowerCache = _c;
 27290            break;
 27291          }
 27292        }
 27293      }
 27294  
 27295      if (scalableFrom(lowerCache)) {
 27296        // then use the lower quality cache for now and queue the better one for later
 27297        self.queueElement(ele, lvl);
 27298        return lowerCache;
 27299      }
 27300  
 27301      txr.context.translate(txr.usedWidth, 0);
 27302      txr.context.scale(scale, scale);
 27303      this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
 27304      txr.context.scale(1 / scale, 1 / scale);
 27305      txr.context.translate(-txr.usedWidth, 0);
 27306    }
 27307  
 27308    eleCache = {
 27309      x: txr.usedWidth,
 27310      texture: txr,
 27311      level: lvl,
 27312      scale: scale,
 27313      width: eleScaledW,
 27314      height: eleScaledH,
 27315      scaledLabelShown: scaledLabelShown
 27316    };
 27317    txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
 27318    txr.eleCaches.push(eleCache);
 27319    lookup.set(ele, lvl, eleCache);
 27320    self.checkTextureFullness(txr);
 27321    return eleCache;
 27322  };
 27323  
 27324  ETCp.invalidateElements = function (eles) {
 27325    for (var i = 0; i < eles.length; i++) {
 27326      this.invalidateElement(eles[i]);
 27327    }
 27328  };
 27329  
 27330  ETCp.invalidateElement = function (ele) {
 27331    var self = this;
 27332    var lookup = self.lookup;
 27333    var caches = [];
 27334    var invalid = lookup.isInvalid(ele);
 27335  
 27336    if (!invalid) {
 27337      return; // override the invalidation request if the element key has not changed
 27338    }
 27339  
 27340    for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
 27341      var cache = lookup.getForCachedKey(ele, lvl);
 27342  
 27343      if (cache) {
 27344        caches.push(cache);
 27345      }
 27346    }
 27347  
 27348    var noOtherElesUseCache = lookup.invalidate(ele);
 27349  
 27350    if (noOtherElesUseCache) {
 27351      for (var i = 0; i < caches.length; i++) {
 27352        var _cache = caches[i];
 27353        var txr = _cache.texture; // remove space from the texture it belongs to
 27354  
 27355        txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
 27356  
 27357        _cache.invalidated = true; // retire the texture if its utility is low
 27358  
 27359        self.checkTextureUtility(txr);
 27360      }
 27361    } // remove from queue since the old req was for the old state
 27362  
 27363  
 27364    self.removeFromQueue(ele);
 27365  };
 27366  
 27367  ETCp.checkTextureUtility = function (txr) {
 27368    // invalidate all entries in the cache if the cache size is small
 27369    if (txr.invalidatedWidth >= minUtility * txr.width) {
 27370      this.retireTexture(txr);
 27371    }
 27372  };
 27373  
 27374  ETCp.checkTextureFullness = function (txr) {
 27375    // if texture has been mostly filled and passed over several times, remove
 27376    // it from the queue so we don't need to waste time looking at it to put new things
 27377    var self = this;
 27378    var txrQ = self.getTextureQueue(txr.height);
 27379  
 27380    if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
 27381      removeFromArray(txrQ, txr);
 27382    } else {
 27383      txr.fullnessChecks++;
 27384    }
 27385  };
 27386  
 27387  ETCp.retireTexture = function (txr) {
 27388    var self = this;
 27389    var txrH = txr.height;
 27390    var txrQ = self.getTextureQueue(txrH);
 27391    var lookup = this.lookup; // retire the texture from the active / searchable queue:
 27392  
 27393    removeFromArray(txrQ, txr);
 27394    txr.retired = true; // remove the refs from the eles to the caches:
 27395  
 27396    var eleCaches = txr.eleCaches;
 27397  
 27398    for (var i = 0; i < eleCaches.length; i++) {
 27399      var eleCache = eleCaches[i];
 27400      lookup.deleteCache(eleCache.key, eleCache.level);
 27401    }
 27402  
 27403    clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
 27404  
 27405    var rtxtrQ = self.getRetiredTextureQueue(txrH);
 27406    rtxtrQ.push(txr);
 27407  };
 27408  
 27409  ETCp.addTexture = function (txrH, minW) {
 27410    var self = this;
 27411    var txrQ = self.getTextureQueue(txrH);
 27412    var txr = {};
 27413    txrQ.push(txr);
 27414    txr.eleCaches = [];
 27415    txr.height = txrH;
 27416    txr.width = Math.max(defTxrWidth, minW);
 27417    txr.usedWidth = 0;
 27418    txr.invalidatedWidth = 0;
 27419    txr.fullnessChecks = 0;
 27420    txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
 27421    txr.context = txr.canvas.getContext('2d');
 27422    return txr;
 27423  };
 27424  
 27425  ETCp.recycleTexture = function (txrH, minW) {
 27426    var self = this;
 27427    var txrQ = self.getTextureQueue(txrH);
 27428    var rtxtrQ = self.getRetiredTextureQueue(txrH);
 27429  
 27430    for (var i = 0; i < rtxtrQ.length; i++) {
 27431      var txr = rtxtrQ[i];
 27432  
 27433      if (txr.width >= minW) {
 27434        txr.retired = false;
 27435        txr.usedWidth = 0;
 27436        txr.invalidatedWidth = 0;
 27437        txr.fullnessChecks = 0;
 27438        clearArray(txr.eleCaches);
 27439        txr.context.setTransform(1, 0, 0, 1, 0, 0);
 27440        txr.context.clearRect(0, 0, txr.width, txr.height);
 27441        removeFromArray(rtxtrQ, txr);
 27442        txrQ.push(txr);
 27443        return txr;
 27444      }
 27445    }
 27446  };
 27447  
 27448  ETCp.queueElement = function (ele, lvl) {
 27449    var self = this;
 27450    var q = self.getElementQueue();
 27451    var k2q = self.getElementKeyToQueue();
 27452    var key = this.getKey(ele);
 27453    var existingReq = k2q[key];
 27454  
 27455    if (existingReq) {
 27456      // use the max lvl b/c in between lvls are cheap to make
 27457      existingReq.level = Math.max(existingReq.level, lvl);
 27458      existingReq.eles.merge(ele);
 27459      existingReq.reqs++;
 27460      q.updateItem(existingReq);
 27461    } else {
 27462      var req = {
 27463        eles: ele.spawn().merge(ele),
 27464        level: lvl,
 27465        reqs: 1,
 27466        key: key
 27467      };
 27468      q.push(req);
 27469      k2q[key] = req;
 27470    }
 27471  };
 27472  
 27473  ETCp.dequeue = function (pxRatio
 27474  /*, extent*/
 27475  ) {
 27476    var self = this;
 27477    var q = self.getElementQueue();
 27478    var k2q = self.getElementKeyToQueue();
 27479    var dequeued = [];
 27480    var lookup = self.lookup;
 27481  
 27482    for (var i = 0; i < maxDeqSize; i++) {
 27483      if (q.size() > 0) {
 27484        var req = q.pop();
 27485        var key = req.key;
 27486        var ele = req.eles[0]; // all eles have the same key
 27487  
 27488        var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
 27489  
 27490        k2q[key] = null; // dequeueing isn't necessary with an existing cache
 27491  
 27492        if (cacheExists) {
 27493          continue;
 27494        }
 27495  
 27496        dequeued.push(req);
 27497        var bb = self.getBoundingBox(ele);
 27498        self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
 27499      } else {
 27500        break;
 27501      }
 27502    }
 27503  
 27504    return dequeued;
 27505  };
 27506  
 27507  ETCp.removeFromQueue = function (ele) {
 27508    var self = this;
 27509    var q = self.getElementQueue();
 27510    var k2q = self.getElementKeyToQueue();
 27511    var key = this.getKey(ele);
 27512    var req = k2q[key];
 27513  
 27514    if (req != null) {
 27515      if (req.eles.length === 1) {
 27516        // remove if last ele in the req
 27517        // bring to front of queue
 27518        req.reqs = MAX_INT;
 27519        q.updateItem(req);
 27520        q.pop(); // remove from queue
 27521  
 27522        k2q[key] = null; // remove from lookup map
 27523      } else {
 27524        // otherwise just remove ele from req
 27525        req.eles.unmerge(ele);
 27526      }
 27527    }
 27528  };
 27529  
 27530  ETCp.onDequeue = function (fn) {
 27531    this.onDequeues.push(fn);
 27532  };
 27533  
 27534  ETCp.offDequeue = function (fn) {
 27535    removeFromArray(this.onDequeues, fn);
 27536  };
 27537  
 27538  ETCp.setupDequeueing = defs.setupDequeueing({
 27539    deqRedrawThreshold: deqRedrawThreshold,
 27540    deqCost: deqCost,
 27541    deqAvgCost: deqAvgCost,
 27542    deqNoDrawCost: deqNoDrawCost,
 27543    deqFastCost: deqFastCost,
 27544    deq: function deq(self, pxRatio, extent) {
 27545      return self.dequeue(pxRatio, extent);
 27546    },
 27547    onDeqd: function onDeqd(self, deqd) {
 27548      for (var i = 0; i < self.onDequeues.length; i++) {
 27549        var fn = self.onDequeues[i];
 27550        fn(deqd);
 27551      }
 27552    },
 27553    shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
 27554      for (var i = 0; i < deqd.length; i++) {
 27555        var eles = deqd[i].eles;
 27556  
 27557        for (var j = 0; j < eles.length; j++) {
 27558          var bb = eles[j].boundingBox();
 27559  
 27560          if (boundingBoxesIntersect(bb, extent)) {
 27561            return true;
 27562          }
 27563        }
 27564      }
 27565  
 27566      return false;
 27567    },
 27568    priority: function priority(self) {
 27569      return self.renderer.beforeRenderPriorities.eleTxrDeq;
 27570    }
 27571  });
 27572  
 27573  var defNumLayers = 1; // default number of layers to use
 27574  
 27575  var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
 27576  
 27577  var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
 27578  
 27579  var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
 27580  
 27581  var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
 27582  
 27583  var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
 27584  
 27585  var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
 27586  
 27587  var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
 27588  
 27589  var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
 27590  
 27591  var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
 27592  
 27593  var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
 27594  
 27595  var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
 27596  
 27597  var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
 27598  
 27599  var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
 27600  // var log = function(){ console.log.apply( console, arguments ); };
 27601  
 27602  var LayeredTextureCache = function LayeredTextureCache(renderer) {
 27603    var self = this;
 27604    var r = self.renderer = renderer;
 27605    var cy = r.cy;
 27606    self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
 27607  
 27608    self.firstGet = true;
 27609    self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
 27610    self.skipping = false;
 27611    self.eleTxrDeqs = cy.collection();
 27612    self.scheduleElementRefinement = util(function () {
 27613      self.refineElementTextures(self.eleTxrDeqs);
 27614      self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
 27615    }, refineEleDebounceTime);
 27616    r.beforeRender(function (willDraw, now) {
 27617      if (now - self.lastInvalidationTime <= invalidThreshold) {
 27618        self.skipping = true;
 27619      } else {
 27620        self.skipping = false;
 27621      }
 27622    }, r.beforeRenderPriorities.lyrTxrSkip);
 27623  
 27624    var qSort = function qSort(a, b) {
 27625      return b.reqs - a.reqs;
 27626    };
 27627  
 27628    self.layersQueue = new Heap(qSort);
 27629    self.setupDequeueing();
 27630  };
 27631  
 27632  var LTCp = LayeredTextureCache.prototype;
 27633  var layerIdPool = 0;
 27634  var MAX_INT$1 = Math.pow(2, 53) - 1;
 27635  
 27636  LTCp.makeLayer = function (bb, lvl) {
 27637    var scale = Math.pow(2, lvl);
 27638    var w = Math.ceil(bb.w * scale);
 27639    var h = Math.ceil(bb.h * scale);
 27640    var canvas = this.renderer.makeOffscreenCanvas(w, h);
 27641    var layer = {
 27642      id: layerIdPool = ++layerIdPool % MAX_INT$1,
 27643      bb: bb,
 27644      level: lvl,
 27645      width: w,
 27646      height: h,
 27647      canvas: canvas,
 27648      context: canvas.getContext('2d'),
 27649      eles: [],
 27650      elesQueue: [],
 27651      reqs: 0
 27652    }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
 27653  
 27654    var cxt = layer.context;
 27655    var dx = -layer.bb.x1;
 27656    var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
 27657  
 27658    cxt.scale(scale, scale);
 27659    cxt.translate(dx, dy);
 27660    return layer;
 27661  };
 27662  
 27663  LTCp.getLayers = function (eles, pxRatio, lvl) {
 27664    var self = this;
 27665    var r = self.renderer;
 27666    var cy = r.cy;
 27667    var zoom = cy.zoom();
 27668    var firstGet = self.firstGet;
 27669    self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
 27670    //log eles.map(function(ele){ return ele.id() }) );
 27671  
 27672    if (lvl == null) {
 27673      lvl = Math.ceil(log2(zoom * pxRatio));
 27674  
 27675      if (lvl < minLvl$1) {
 27676        lvl = minLvl$1;
 27677      } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
 27678        return null;
 27679      }
 27680    }
 27681  
 27682    self.validateLayersElesOrdering(lvl, eles);
 27683    var layersByLvl = self.layersByLevel;
 27684    var scale = Math.pow(2, lvl);
 27685    var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
 27686    var bb;
 27687    var lvlComplete = self.levelIsComplete(lvl, eles);
 27688    var tmpLayers;
 27689  
 27690    var checkTempLevels = function checkTempLevels() {
 27691      var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
 27692        self.validateLayersElesOrdering(l, eles);
 27693  
 27694        if (self.levelIsComplete(l, eles)) {
 27695          tmpLayers = layersByLvl[l];
 27696          return true;
 27697        }
 27698      };
 27699  
 27700      var checkLvls = function checkLvls(dir) {
 27701        if (tmpLayers) {
 27702          return;
 27703        }
 27704  
 27705        for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
 27706          if (canUseAsTmpLvl(l)) {
 27707            break;
 27708          }
 27709        }
 27710      };
 27711  
 27712      checkLvls(+1);
 27713      checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
 27714  
 27715      for (var i = layers.length - 1; i >= 0; i--) {
 27716        var layer = layers[i];
 27717  
 27718        if (layer.invalid) {
 27719          removeFromArray(layers, layer);
 27720        }
 27721      }
 27722    };
 27723  
 27724    if (!lvlComplete) {
 27725      // if the current level is incomplete, then use the closest, best quality layerset temporarily
 27726      // and later queue the current layerset so we can get the proper quality level soon
 27727      checkTempLevels();
 27728    } else {
 27729      // log('level complete, using existing layers\n--');
 27730      return layers;
 27731    }
 27732  
 27733    var getBb = function getBb() {
 27734      if (!bb) {
 27735        bb = makeBoundingBox();
 27736  
 27737        for (var i = 0; i < eles.length; i++) {
 27738          updateBoundingBox(bb, eles[i].boundingBox());
 27739        }
 27740      }
 27741  
 27742      return bb;
 27743    };
 27744  
 27745    var makeLayer = function makeLayer(opts) {
 27746      opts = opts || {};
 27747      var after = opts.after;
 27748      getBb();
 27749      var area = bb.w * scale * (bb.h * scale);
 27750  
 27751      if (area > maxLayerArea) {
 27752        return null;
 27753      }
 27754  
 27755      var layer = self.makeLayer(bb, lvl);
 27756  
 27757      if (after != null) {
 27758        var index = layers.indexOf(after) + 1;
 27759        layers.splice(index, 0, layer);
 27760      } else if (opts.insert === undefined || opts.insert) {
 27761        // no after specified => first layer made so put at start
 27762        layers.unshift(layer);
 27763      } // if( tmpLayers ){
 27764      //self.queueLayer( layer );
 27765      // }
 27766  
 27767  
 27768      return layer;
 27769    };
 27770  
 27771    if (self.skipping && !firstGet) {
 27772      // log('skip layers');
 27773      return null;
 27774    } // log('do layers');
 27775  
 27776  
 27777    var layer = null;
 27778    var maxElesPerLayer = eles.length / defNumLayers;
 27779    var allowLazyQueueing =  !firstGet;
 27780  
 27781    for (var i = 0; i < eles.length; i++) {
 27782      var ele = eles[i];
 27783      var rs = ele._private.rscratch;
 27784      var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
 27785  
 27786      var existingLayer = caches[lvl];
 27787  
 27788      if (existingLayer) {
 27789        // reuse layer for later eles
 27790        // log('reuse layer for', ele.id());
 27791        layer = existingLayer;
 27792        continue;
 27793      }
 27794  
 27795      if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
 27796        // log('make new layer for ele %s', ele.id());
 27797        layer = makeLayer({
 27798          insert: true,
 27799          after: layer
 27800        }); // if now layer can be built then we can't use layers at this level
 27801  
 27802        if (!layer) {
 27803          return null;
 27804        } // log('new layer with id %s', layer.id);
 27805  
 27806      }
 27807  
 27808      if (tmpLayers || allowLazyQueueing) {
 27809        // log('queue ele %s in layer %s', ele.id(), layer.id);
 27810        self.queueLayer(layer, ele);
 27811      } else {
 27812        // log('draw ele %s in layer %s', ele.id(), layer.id);
 27813        self.drawEleInLayer(layer, ele, lvl, pxRatio);
 27814      }
 27815  
 27816      layer.eles.push(ele);
 27817      caches[lvl] = layer;
 27818    } // log('--');
 27819  
 27820  
 27821    if (tmpLayers) {
 27822      // then we only queued the current layerset and can't draw it yet
 27823      return tmpLayers;
 27824    }
 27825  
 27826    if (allowLazyQueueing) {
 27827      // log('lazy queue level', lvl);
 27828      return null;
 27829    }
 27830  
 27831    return layers;
 27832  }; // a layer may want to use an ele cache of a higher level to avoid blurriness
 27833  // so the layer level might not equal the ele level
 27834  
 27835  
 27836  LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
 27837    return lvl;
 27838  };
 27839  
 27840  LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
 27841    var self = this;
 27842    var r = this.renderer;
 27843    var context = layer.context;
 27844    var bb = ele.boundingBox();
 27845  
 27846    if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
 27847      return;
 27848    }
 27849  
 27850    lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
 27851  
 27852    {
 27853      r.setImgSmoothing(context, false);
 27854    }
 27855  
 27856    {
 27857      r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
 27858    }
 27859  
 27860    {
 27861      r.setImgSmoothing(context, true);
 27862    }
 27863  };
 27864  
 27865  LTCp.levelIsComplete = function (lvl, eles) {
 27866    var self = this;
 27867    var layers = self.layersByLevel[lvl];
 27868  
 27869    if (!layers || layers.length === 0) {
 27870      return false;
 27871    }
 27872  
 27873    var numElesInLayers = 0;
 27874  
 27875    for (var i = 0; i < layers.length; i++) {
 27876      var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
 27877  
 27878      if (layer.reqs > 0) {
 27879        return false;
 27880      } // if the layer is invalid, the level is not complete
 27881  
 27882  
 27883      if (layer.invalid) {
 27884        return false;
 27885      }
 27886  
 27887      numElesInLayers += layer.eles.length;
 27888    } // we should have exactly the number of eles passed in to be complete
 27889  
 27890  
 27891    if (numElesInLayers !== eles.length) {
 27892      return false;
 27893    }
 27894  
 27895    return true;
 27896  };
 27897  
 27898  LTCp.validateLayersElesOrdering = function (lvl, eles) {
 27899    var layers = this.layersByLevel[lvl];
 27900  
 27901    if (!layers) {
 27902      return;
 27903    } // if in a layer the eles are not in the same order, then the layer is invalid
 27904    // (i.e. there is an ele in between the eles in the layer)
 27905  
 27906  
 27907    for (var i = 0; i < layers.length; i++) {
 27908      var layer = layers[i];
 27909      var offset = -1; // find the offset
 27910  
 27911      for (var j = 0; j < eles.length; j++) {
 27912        if (layer.eles[0] === eles[j]) {
 27913          offset = j;
 27914          break;
 27915        }
 27916      }
 27917  
 27918      if (offset < 0) {
 27919        // then the layer has nonexistant elements and is invalid
 27920        this.invalidateLayer(layer);
 27921        continue;
 27922      } // the eles in the layer must be in the same continuous order, else the layer is invalid
 27923  
 27924  
 27925      var o = offset;
 27926  
 27927      for (var j = 0; j < layer.eles.length; j++) {
 27928        if (layer.eles[j] !== eles[o + j]) {
 27929          // log('invalidate based on ordering', layer.id);
 27930          this.invalidateLayer(layer);
 27931          break;
 27932        }
 27933      }
 27934    }
 27935  };
 27936  
 27937  LTCp.updateElementsInLayers = function (eles, update) {
 27938    var self = this;
 27939    var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
 27940    // layer itself along the way
 27941  
 27942    for (var i = 0; i < eles.length; i++) {
 27943      var req = isEles ? null : eles[i];
 27944      var ele = isEles ? eles[i] : eles[i].ele;
 27945      var rs = ele._private.rscratch;
 27946      var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
 27947  
 27948      for (var l = minLvl$1; l <= maxLvl$1; l++) {
 27949        var layer = caches[l];
 27950  
 27951        if (!layer) {
 27952          continue;
 27953        } // if update is a request from the ele cache, then it affects only
 27954        // the matching level
 27955  
 27956  
 27957        if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
 27958          continue;
 27959        }
 27960  
 27961        update(layer, ele, req);
 27962      }
 27963    }
 27964  };
 27965  
 27966  LTCp.haveLayers = function () {
 27967    var self = this;
 27968    var haveLayers = false;
 27969  
 27970    for (var l = minLvl$1; l <= maxLvl$1; l++) {
 27971      var layers = self.layersByLevel[l];
 27972  
 27973      if (layers && layers.length > 0) {
 27974        haveLayers = true;
 27975        break;
 27976      }
 27977    }
 27978  
 27979    return haveLayers;
 27980  };
 27981  
 27982  LTCp.invalidateElements = function (eles) {
 27983    var self = this;
 27984  
 27985    if (eles.length === 0) {
 27986      return;
 27987    }
 27988  
 27989    self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
 27990  
 27991    if (eles.length === 0 || !self.haveLayers()) {
 27992      return;
 27993    }
 27994  
 27995    self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
 27996      self.invalidateLayer(layer);
 27997    });
 27998  };
 27999  
 28000  LTCp.invalidateLayer = function (layer) {
 28001    // log('update invalidate layer time');
 28002    this.lastInvalidationTime = performanceNow();
 28003  
 28004    if (layer.invalid) {
 28005      return;
 28006    } // save cycles
 28007  
 28008  
 28009    var lvl = layer.level;
 28010    var eles = layer.eles;
 28011    var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
 28012  
 28013    removeFromArray(layers, layer); // layer.eles = [];
 28014  
 28015    layer.elesQueue = [];
 28016    layer.invalid = true;
 28017  
 28018    if (layer.replacement) {
 28019      layer.replacement.invalid = true;
 28020    }
 28021  
 28022    for (var i = 0; i < eles.length; i++) {
 28023      var caches = eles[i]._private.rscratch.imgLayerCaches;
 28024  
 28025      if (caches) {
 28026        caches[lvl] = null;
 28027      }
 28028    }
 28029  };
 28030  
 28031  LTCp.refineElementTextures = function (eles) {
 28032    var self = this; // log('refine', eles.length);
 28033  
 28034    self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
 28035      var rLyr = layer.replacement;
 28036  
 28037      if (!rLyr) {
 28038        rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
 28039        rLyr.replaces = layer;
 28040        rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
 28041      }
 28042  
 28043      if (!rLyr.reqs) {
 28044        for (var i = 0; i < rLyr.eles.length; i++) {
 28045          self.queueLayer(rLyr, rLyr.eles[i]);
 28046        } // log('queue replacement layer refinement', rLyr.id);
 28047  
 28048      }
 28049    });
 28050  };
 28051  
 28052  LTCp.enqueueElementRefinement = function (ele) {
 28053  
 28054    this.eleTxrDeqs.merge(ele);
 28055    this.scheduleElementRefinement();
 28056  };
 28057  
 28058  LTCp.queueLayer = function (layer, ele) {
 28059    var self = this;
 28060    var q = self.layersQueue;
 28061    var elesQ = layer.elesQueue;
 28062    var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
 28063  
 28064    if (layer.replacement) {
 28065      return;
 28066    }
 28067  
 28068    if (ele) {
 28069      if (hasId[ele.id()]) {
 28070        return;
 28071      }
 28072  
 28073      elesQ.push(ele);
 28074      hasId[ele.id()] = true;
 28075    }
 28076  
 28077    if (layer.reqs) {
 28078      layer.reqs++;
 28079      q.updateItem(layer);
 28080    } else {
 28081      layer.reqs = 1;
 28082      q.push(layer);
 28083    }
 28084  };
 28085  
 28086  LTCp.dequeue = function (pxRatio) {
 28087    var self = this;
 28088    var q = self.layersQueue;
 28089    var deqd = [];
 28090    var eleDeqs = 0;
 28091  
 28092    while (eleDeqs < maxDeqSize$1) {
 28093      if (q.size() === 0) {
 28094        break;
 28095      }
 28096  
 28097      var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
 28098  
 28099      if (layer.replacement) {
 28100        // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
 28101        q.pop();
 28102        continue;
 28103      } // if this is a replacement layer that has been superceded, then forget it
 28104  
 28105  
 28106      if (layer.replaces && layer !== layer.replaces.replacement) {
 28107        // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
 28108        q.pop();
 28109        continue;
 28110      }
 28111  
 28112      if (layer.invalid) {
 28113        // log('replacement layer %s is invalid; dequeued', layer.id);
 28114        q.pop();
 28115        continue;
 28116      }
 28117  
 28118      var ele = layer.elesQueue.shift();
 28119  
 28120      if (ele) {
 28121        // log('dequeue layer %s', layer.id);
 28122        self.drawEleInLayer(layer, ele, layer.level, pxRatio);
 28123        eleDeqs++;
 28124      }
 28125  
 28126      if (deqd.length === 0) {
 28127        // we need only one entry in deqd to queue redrawing etc
 28128        deqd.push(true);
 28129      } // if the layer has all its eles done, then remove from the queue
 28130  
 28131  
 28132      if (layer.elesQueue.length === 0) {
 28133        q.pop();
 28134        layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
 28135        // when a replacement layer is dequeued, it replaces the old layer in the level
 28136  
 28137        if (layer.replaces) {
 28138          self.applyLayerReplacement(layer);
 28139        }
 28140  
 28141        self.requestRedraw();
 28142      }
 28143    }
 28144  
 28145    return deqd;
 28146  };
 28147  
 28148  LTCp.applyLayerReplacement = function (layer) {
 28149    var self = this;
 28150    var layersInLevel = self.layersByLevel[layer.level];
 28151    var replaced = layer.replaces;
 28152    var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
 28153    // refs would be a mistake (i.e. overwriting the true active layer)
 28154  
 28155    if (index < 0 || replaced.invalid) {
 28156      // log('replacement layer would have no effect', layer.id);
 28157      return;
 28158    }
 28159  
 28160    layersInLevel[index] = layer; // replace level ref
 28161    // replace refs in eles
 28162  
 28163    for (var i = 0; i < layer.eles.length; i++) {
 28164      var _p = layer.eles[i]._private;
 28165      var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
 28166  
 28167      if (cache) {
 28168        cache[layer.level] = layer;
 28169      }
 28170    } // log('apply replacement layer %s over %s', layer.id, replaced.id);
 28171  
 28172  
 28173    self.requestRedraw();
 28174  };
 28175  
 28176  LTCp.requestRedraw = util(function () {
 28177    var r = this.renderer;
 28178    r.redrawHint('eles', true);
 28179    r.redrawHint('drag', true);
 28180    r.redraw();
 28181  }, 100);
 28182  LTCp.setupDequeueing = defs.setupDequeueing({
 28183    deqRedrawThreshold: deqRedrawThreshold$1,
 28184    deqCost: deqCost$1,
 28185    deqAvgCost: deqAvgCost$1,
 28186    deqNoDrawCost: deqNoDrawCost$1,
 28187    deqFastCost: deqFastCost$1,
 28188    deq: function deq(self, pxRatio) {
 28189      return self.dequeue(pxRatio);
 28190    },
 28191    onDeqd: noop,
 28192    shouldRedraw: trueify,
 28193    priority: function priority(self) {
 28194      return self.renderer.beforeRenderPriorities.lyrTxrDeq;
 28195    }
 28196  });
 28197  
 28198  var CRp = {};
 28199  var impl;
 28200  
 28201  function polygon(context, points) {
 28202    for (var i = 0; i < points.length; i++) {
 28203      var pt = points[i];
 28204      context.lineTo(pt.x, pt.y);
 28205    }
 28206  }
 28207  
 28208  function triangleBackcurve(context, points, controlPoint) {
 28209    var firstPt;
 28210  
 28211    for (var i = 0; i < points.length; i++) {
 28212      var pt = points[i];
 28213  
 28214      if (i === 0) {
 28215        firstPt = pt;
 28216      }
 28217  
 28218      context.lineTo(pt.x, pt.y);
 28219    }
 28220  
 28221    context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
 28222  }
 28223  
 28224  function triangleTee(context, trianglePoints, teePoints) {
 28225    if (context.beginPath) {
 28226      context.beginPath();
 28227    }
 28228  
 28229    var triPts = trianglePoints;
 28230  
 28231    for (var i = 0; i < triPts.length; i++) {
 28232      var pt = triPts[i];
 28233      context.lineTo(pt.x, pt.y);
 28234    }
 28235  
 28236    var teePts = teePoints;
 28237    var firstTeePt = teePoints[0];
 28238    context.moveTo(firstTeePt.x, firstTeePt.y);
 28239  
 28240    for (var i = 1; i < teePts.length; i++) {
 28241      var pt = teePts[i];
 28242      context.lineTo(pt.x, pt.y);
 28243    }
 28244  
 28245    if (context.closePath) {
 28246      context.closePath();
 28247    }
 28248  }
 28249  
 28250  function circle(context, rx, ry, r) {
 28251    context.arc(rx, ry, r, 0, Math.PI * 2, false);
 28252  }
 28253  
 28254  CRp.arrowShapeImpl = function (name) {
 28255    return (impl || (impl = {
 28256      'polygon': polygon,
 28257      'triangle-backcurve': triangleBackcurve,
 28258      'triangle-tee': triangleTee,
 28259      'triangle-cross': triangleTee,
 28260      'circle': circle
 28261    }))[name];
 28262  };
 28263  
 28264  var CRp$1 = {};
 28265  
 28266  CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
 28267    var r = this;
 28268  
 28269    if (ele.isNode()) {
 28270      r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
 28271    } else {
 28272      r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
 28273    }
 28274  };
 28275  
 28276  CRp$1.drawElementOverlay = function (context, ele) {
 28277    var r = this;
 28278  
 28279    if (ele.isNode()) {
 28280      r.drawNodeOverlay(context, ele);
 28281    } else {
 28282      r.drawEdgeOverlay(context, ele);
 28283    }
 28284  };
 28285  
 28286  CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
 28287    var r = this;
 28288    var bb = eleTxrCache.getBoundingBox(ele);
 28289  
 28290    if (bb.w === 0 || bb.h === 0) {
 28291      return;
 28292    } // ignore zero size case
 28293  
 28294  
 28295    var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
 28296  
 28297    if (eleCache != null) {
 28298      var opacity = getOpacity(r, ele);
 28299  
 28300      if (opacity === 0) {
 28301        return;
 28302      }
 28303  
 28304      var theta = getRotation(r, ele);
 28305      var x1 = bb.x1,
 28306          y1 = bb.y1,
 28307          w = bb.w,
 28308          h = bb.h;
 28309      var x, y, sx, sy, smooth;
 28310  
 28311      if (theta !== 0) {
 28312        var rotPt = eleTxrCache.getRotationPoint(ele);
 28313        sx = rotPt.x;
 28314        sy = rotPt.y;
 28315        context.translate(sx, sy);
 28316        context.rotate(theta);
 28317        smooth = r.getImgSmoothing(context);
 28318  
 28319        if (!smooth) {
 28320          r.setImgSmoothing(context, true);
 28321        }
 28322  
 28323        var off = eleTxrCache.getRotationOffset(ele);
 28324        x = off.x;
 28325        y = off.y;
 28326      } else {
 28327        x = x1;
 28328        y = y1;
 28329      }
 28330  
 28331      var oldGlobalAlpha;
 28332  
 28333      if (opacity !== 1) {
 28334        oldGlobalAlpha = context.globalAlpha;
 28335        context.globalAlpha = oldGlobalAlpha * opacity;
 28336      }
 28337  
 28338      context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
 28339  
 28340      if (opacity !== 1) {
 28341        context.globalAlpha = oldGlobalAlpha;
 28342      }
 28343  
 28344      if (theta !== 0) {
 28345        context.rotate(-theta);
 28346        context.translate(-sx, -sy);
 28347  
 28348        if (!smooth) {
 28349          r.setImgSmoothing(context, false);
 28350        }
 28351      }
 28352    } else {
 28353      eleTxrCache.drawElement(context, ele); // direct draw fallback
 28354    }
 28355  };
 28356  
 28357  var getZeroRotation = function getZeroRotation() {
 28358    return 0;
 28359  };
 28360  
 28361  var getLabelRotation = function getLabelRotation(r, ele) {
 28362    return r.getTextAngle(ele, null);
 28363  };
 28364  
 28365  var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
 28366    return r.getTextAngle(ele, 'source');
 28367  };
 28368  
 28369  var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
 28370    return r.getTextAngle(ele, 'target');
 28371  };
 28372  
 28373  var getOpacity = function getOpacity(r, ele) {
 28374    return ele.effectiveOpacity();
 28375  };
 28376  
 28377  var getTextOpacity = function getTextOpacity(e, ele) {
 28378    return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
 28379  };
 28380  
 28381  CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
 28382    var r = this;
 28383    var _r$data = r.data,
 28384        eleTxrCache = _r$data.eleTxrCache,
 28385        lblTxrCache = _r$data.lblTxrCache,
 28386        slbTxrCache = _r$data.slbTxrCache,
 28387        tlbTxrCache = _r$data.tlbTxrCache;
 28388    var bb = ele.boundingBox();
 28389    var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
 28390  
 28391    if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
 28392      return;
 28393    }
 28394  
 28395    if (!extent || boundingBoxesIntersect(bb, extent)) {
 28396      var isEdge = ele.isEdge();
 28397  
 28398      var badLine = ele.element()._private.rscratch.badLine;
 28399  
 28400      r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
 28401  
 28402      if (!isEdge || !badLine) {
 28403        r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
 28404      }
 28405  
 28406      if (isEdge && !badLine) {
 28407        r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
 28408        r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
 28409      }
 28410  
 28411      r.drawElementOverlay(context, ele);
 28412    }
 28413  };
 28414  
 28415  CRp$1.drawElements = function (context, eles) {
 28416    var r = this;
 28417  
 28418    for (var i = 0; i < eles.length; i++) {
 28419      var ele = eles[i];
 28420      r.drawElement(context, ele);
 28421    }
 28422  };
 28423  
 28424  CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
 28425    var r = this;
 28426  
 28427    for (var i = 0; i < eles.length; i++) {
 28428      var ele = eles[i];
 28429      r.drawCachedElement(context, ele, pxRatio, extent);
 28430    }
 28431  };
 28432  
 28433  CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
 28434    var r = this;
 28435  
 28436    for (var i = 0; i < eles.length; i++) {
 28437      var ele = eles[i];
 28438  
 28439      if (!ele.isNode()) {
 28440        continue;
 28441      }
 28442  
 28443      r.drawCachedElement(context, ele, pxRatio, extent);
 28444    }
 28445  };
 28446  
 28447  CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
 28448    var r = this;
 28449    var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
 28450  
 28451    if (layers) {
 28452      for (var i = 0; i < layers.length; i++) {
 28453        var layer = layers[i];
 28454        var bb = layer.bb;
 28455  
 28456        if (bb.w === 0 || bb.h === 0) {
 28457          continue;
 28458        }
 28459  
 28460        context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
 28461      }
 28462    } else {
 28463      // fall back on plain caching if no layers
 28464      r.drawCachedElements(context, eles, pxRatio, extent);
 28465    }
 28466  };
 28467  
 28468  /* global Path2D */
 28469  var CRp$2 = {};
 28470  
 28471  CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
 28472    var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
 28473    var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
 28474    var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
 28475    var r = this;
 28476    var rs = edge._private.rscratch;
 28477  
 28478    if (shouldDrawOpacity && !edge.visible()) {
 28479      return;
 28480    } // if bezier ctrl pts can not be calculated, then die
 28481  
 28482  
 28483    if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
 28484      // isNaN in case edge is impossible and browser bugs (e.g. safari)
 28485      return;
 28486    }
 28487  
 28488    var bb;
 28489  
 28490    if (shiftToOriginWithBb) {
 28491      bb = shiftToOriginWithBb;
 28492      context.translate(-bb.x1, -bb.y1);
 28493    }
 28494  
 28495    var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
 28496    var lineStyle = edge.pstyle('line-style').value;
 28497    var edgeWidth = edge.pstyle('width').pfValue;
 28498    var lineCap = edge.pstyle('line-cap').value;
 28499  
 28500    var drawLine = function drawLine() {
 28501      var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
 28502      context.lineWidth = edgeWidth;
 28503      context.lineCap = lineCap;
 28504      r.eleStrokeStyle(context, edge, strokeOpacity);
 28505      r.drawEdgePath(edge, context, rs.allpts, lineStyle);
 28506      context.lineCap = 'butt'; // reset for other drawing functions
 28507    };
 28508  
 28509    var drawOverlay = function drawOverlay() {
 28510      if (!shouldDrawOverlay) {
 28511        return;
 28512      }
 28513  
 28514      r.drawEdgeOverlay(context, edge);
 28515    };
 28516  
 28517    var drawArrows = function drawArrows() {
 28518      var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
 28519      r.drawArrowheads(context, edge, arrowOpacity);
 28520    };
 28521  
 28522    var drawText = function drawText() {
 28523      r.drawElementText(context, edge, null, drawLabel);
 28524    };
 28525  
 28526    context.lineJoin = 'round';
 28527    var ghost = edge.pstyle('ghost').value === 'yes';
 28528  
 28529    if (ghost) {
 28530      var gx = edge.pstyle('ghost-offset-x').pfValue;
 28531      var gy = edge.pstyle('ghost-offset-y').pfValue;
 28532      var ghostOpacity = edge.pstyle('ghost-opacity').value;
 28533      var effectiveGhostOpacity = opacity * ghostOpacity;
 28534      context.translate(gx, gy);
 28535      drawLine(effectiveGhostOpacity);
 28536      drawArrows(effectiveGhostOpacity);
 28537      context.translate(-gx, -gy);
 28538    }
 28539  
 28540    drawLine();
 28541    drawArrows();
 28542    drawOverlay();
 28543    drawText();
 28544  
 28545    if (shiftToOriginWithBb) {
 28546      context.translate(bb.x1, bb.y1);
 28547    }
 28548  };
 28549  
 28550  CRp$2.drawEdgeOverlay = function (context, edge) {
 28551    if (!edge.visible()) {
 28552      return;
 28553    }
 28554  
 28555    var overlayOpacity = edge.pstyle('overlay-opacity').value;
 28556  
 28557    if (overlayOpacity === 0) {
 28558      return;
 28559    }
 28560  
 28561    var r = this;
 28562    var usePaths = r.usePaths();
 28563    var rs = edge._private.rscratch;
 28564    var overlayPadding = edge.pstyle('overlay-padding').pfValue;
 28565    var overlayWidth = 2 * overlayPadding;
 28566    var overlayColor = edge.pstyle('overlay-color').value;
 28567    context.lineWidth = overlayWidth;
 28568  
 28569    if (rs.edgeType === 'self' && !usePaths) {
 28570      context.lineCap = 'butt';
 28571    } else {
 28572      context.lineCap = 'round';
 28573    }
 28574  
 28575    r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
 28576    r.drawEdgePath(edge, context, rs.allpts, 'solid');
 28577  };
 28578  
 28579  CRp$2.drawEdgePath = function (edge, context, pts, type) {
 28580    var rs = edge._private.rscratch;
 28581    var canvasCxt = context;
 28582    var path;
 28583    var pathCacheHit = false;
 28584    var usePaths = this.usePaths();
 28585    var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
 28586    var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
 28587  
 28588    if (usePaths) {
 28589      var pathCacheKey = pts.join('$');
 28590      var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
 28591  
 28592      if (keyMatches) {
 28593        path = context = rs.pathCache;
 28594        pathCacheHit = true;
 28595      } else {
 28596        path = context = new Path2D();
 28597        rs.pathCacheKey = pathCacheKey;
 28598        rs.pathCache = path;
 28599      }
 28600    }
 28601  
 28602    if (canvasCxt.setLineDash) {
 28603      // for very outofdate browsers
 28604      switch (type) {
 28605        case 'dotted':
 28606          canvasCxt.setLineDash([1, 1]);
 28607          break;
 28608  
 28609        case 'dashed':
 28610          canvasCxt.setLineDash(lineDashPattern);
 28611          canvasCxt.lineDashOffset = lineDashOffset;
 28612          break;
 28613  
 28614        case 'solid':
 28615          canvasCxt.setLineDash([]);
 28616          break;
 28617      }
 28618    }
 28619  
 28620    if (!pathCacheHit && !rs.badLine) {
 28621      if (context.beginPath) {
 28622        context.beginPath();
 28623      }
 28624  
 28625      context.moveTo(pts[0], pts[1]);
 28626  
 28627      switch (rs.edgeType) {
 28628        case 'bezier':
 28629        case 'self':
 28630        case 'compound':
 28631        case 'multibezier':
 28632          for (var i = 2; i + 3 < pts.length; i += 4) {
 28633            context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
 28634          }
 28635  
 28636          break;
 28637  
 28638        case 'straight':
 28639        case 'segments':
 28640        case 'haystack':
 28641          for (var _i = 2; _i + 1 < pts.length; _i += 2) {
 28642            context.lineTo(pts[_i], pts[_i + 1]);
 28643          }
 28644  
 28645          break;
 28646      }
 28647    }
 28648  
 28649    context = canvasCxt;
 28650  
 28651    if (usePaths) {
 28652      context.stroke(path);
 28653    } else {
 28654      context.stroke();
 28655    } // reset any line dashes
 28656  
 28657  
 28658    if (context.setLineDash) {
 28659      // for very outofdate browsers
 28660      context.setLineDash([]);
 28661    }
 28662  };
 28663  
 28664  CRp$2.drawArrowheads = function (context, edge, opacity) {
 28665    var rs = edge._private.rscratch;
 28666    var isHaystack = rs.edgeType === 'haystack';
 28667  
 28668    if (!isHaystack) {
 28669      this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
 28670    }
 28671  
 28672    this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
 28673    this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
 28674  
 28675    if (!isHaystack) {
 28676      this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
 28677    }
 28678  };
 28679  
 28680  CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
 28681    if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
 28682      return;
 28683    }
 28684  
 28685    var self = this;
 28686    var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
 28687  
 28688    if (arrowShape === 'none') {
 28689      return;
 28690    }
 28691  
 28692    var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
 28693    var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
 28694    var edgeWidth = edge.pstyle('width').pfValue;
 28695    var edgeOpacity = edge.pstyle('opacity').value;
 28696  
 28697    if (opacity === undefined) {
 28698      opacity = edgeOpacity;
 28699    }
 28700  
 28701    var gco = context.globalCompositeOperation;
 28702  
 28703    if (opacity !== 1 || arrowFill === 'hollow') {
 28704      // then extra clear is needed
 28705      context.globalCompositeOperation = 'destination-out';
 28706      self.colorFillStyle(context, 255, 255, 255, 1);
 28707      self.colorStrokeStyle(context, 255, 255, 255, 1);
 28708      self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
 28709      context.globalCompositeOperation = gco;
 28710    } // otherwise, the opaque arrow clears it for free :)
 28711  
 28712  
 28713    var color = edge.pstyle(prefix + '-arrow-color').value;
 28714    self.colorFillStyle(context, color[0], color[1], color[2], opacity);
 28715    self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
 28716    self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
 28717  };
 28718  
 28719  CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
 28720    var r = this;
 28721    var usePaths = this.usePaths() && shape !== 'triangle-cross';
 28722    var pathCacheHit = false;
 28723    var path;
 28724    var canvasContext = context;
 28725    var translation = {
 28726      x: x,
 28727      y: y
 28728    };
 28729    var scale = edge.pstyle('arrow-scale').value;
 28730    var size = this.getArrowWidth(edgeWidth, scale);
 28731    var shapeImpl = r.arrowShapes[shape];
 28732  
 28733    if (usePaths) {
 28734      var cache = r.arrowPathCache = r.arrowPathCache || [];
 28735      var key = hashString(shape);
 28736      var cachedPath = cache[key];
 28737  
 28738      if (cachedPath != null) {
 28739        path = context = cachedPath;
 28740        pathCacheHit = true;
 28741      } else {
 28742        path = context = new Path2D();
 28743        cache[key] = path;
 28744      }
 28745    }
 28746  
 28747    if (!pathCacheHit) {
 28748      if (context.beginPath) {
 28749        context.beginPath();
 28750      }
 28751  
 28752      if (usePaths) {
 28753        // store in the path cache with values easily manipulated later
 28754        shapeImpl.draw(context, 1, 0, {
 28755          x: 0,
 28756          y: 0
 28757        }, 1);
 28758      } else {
 28759        shapeImpl.draw(context, size, angle, translation, edgeWidth);
 28760      }
 28761  
 28762      if (context.closePath) {
 28763        context.closePath();
 28764      }
 28765    }
 28766  
 28767    context = canvasContext;
 28768  
 28769    if (usePaths) {
 28770      // set transform to arrow position/orientation
 28771      context.translate(x, y);
 28772      context.rotate(angle);
 28773      context.scale(size, size);
 28774    }
 28775  
 28776    if (fill === 'filled' || fill === 'both') {
 28777      if (usePaths) {
 28778        context.fill(path);
 28779      } else {
 28780        context.fill();
 28781      }
 28782    }
 28783  
 28784    if (fill === 'hollow' || fill === 'both') {
 28785      context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
 28786      context.lineJoin = 'miter';
 28787  
 28788      if (usePaths) {
 28789        context.stroke(path);
 28790      } else {
 28791        context.stroke();
 28792      }
 28793    }
 28794  
 28795    if (usePaths) {
 28796      // reset transform by applying inverse
 28797      context.scale(1 / size, 1 / size);
 28798      context.rotate(-angle);
 28799      context.translate(-x, -y);
 28800    }
 28801  };
 28802  
 28803  var CRp$3 = {};
 28804  
 28805  CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
 28806    // detect problematic cases for old browsers with bad images (cheaper than try-catch)
 28807    if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
 28808      return;
 28809    }
 28810  
 28811    context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
 28812  };
 28813  
 28814  CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
 28815    var r = this;
 28816    var pos = node.position();
 28817    var nodeX = pos.x;
 28818    var nodeY = pos.y;
 28819    var styleObj = node.cy().style();
 28820    var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
 28821    var fit = getIndexedStyle(node, 'background-fit', 'value', index);
 28822    var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
 28823    var nodeW = node.width();
 28824    var nodeH = node.height();
 28825    var paddingX2 = node.padding() * 2;
 28826    var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
 28827    var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
 28828    var rs = node._private.rscratch;
 28829    var clip = getIndexedStyle(node, 'background-clip', 'value', index);
 28830    var shouldClip = clip === 'node';
 28831    var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
 28832    var imgW = img.width || img.cachedW;
 28833    var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
 28834  
 28835    if (null == imgW || null == imgH) {
 28836      document.body.appendChild(img); // eslint-disable-line no-undef
 28837  
 28838      imgW = img.cachedW = img.width || img.offsetWidth;
 28839      imgH = img.cachedH = img.height || img.offsetHeight;
 28840      document.body.removeChild(img); // eslint-disable-line no-undef
 28841    }
 28842  
 28843    var w = imgW;
 28844    var h = imgH;
 28845  
 28846    if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
 28847      if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
 28848        w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
 28849      } else {
 28850        w = getIndexedStyle(node, 'background-width', 'pfValue', index);
 28851      }
 28852    }
 28853  
 28854    if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
 28855      if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
 28856        h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
 28857      } else {
 28858        h = getIndexedStyle(node, 'background-height', 'pfValue', index);
 28859      }
 28860    }
 28861  
 28862    if (w === 0 || h === 0) {
 28863      return; // no point in drawing empty image (and chrome is broken in this case)
 28864    }
 28865  
 28866    if (fit === 'contain') {
 28867      var scale = Math.min(nodeTW / w, nodeTH / h);
 28868      w *= scale;
 28869      h *= scale;
 28870    } else if (fit === 'cover') {
 28871      var scale = Math.max(nodeTW / w, nodeTH / h);
 28872      w *= scale;
 28873      h *= scale;
 28874    }
 28875  
 28876    var x = nodeX - nodeTW / 2; // left
 28877  
 28878    var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
 28879    var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
 28880  
 28881    if (posXUnits === '%') {
 28882      x += (nodeTW - w) * posXPfVal;
 28883    } else {
 28884      x += posXPfVal;
 28885    }
 28886  
 28887    var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
 28888    var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
 28889  
 28890    if (offXUnits === '%') {
 28891      x += (nodeTW - w) * offXPfVal;
 28892    } else {
 28893      x += offXPfVal;
 28894    }
 28895  
 28896    var y = nodeY - nodeTH / 2; // top
 28897  
 28898    var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
 28899    var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
 28900  
 28901    if (posYUnits === '%') {
 28902      y += (nodeTH - h) * posYPfVal;
 28903    } else {
 28904      y += posYPfVal;
 28905    }
 28906  
 28907    var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
 28908    var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
 28909  
 28910    if (offYUnits === '%') {
 28911      y += (nodeTH - h) * offYPfVal;
 28912    } else {
 28913      y += offYPfVal;
 28914    }
 28915  
 28916    if (rs.pathCache) {
 28917      x -= nodeX;
 28918      y -= nodeY;
 28919      nodeX = 0;
 28920      nodeY = 0;
 28921    }
 28922  
 28923    var gAlpha = context.globalAlpha;
 28924    context.globalAlpha = imgOpacity;
 28925  
 28926    if (repeat === 'no-repeat') {
 28927      if (shouldClip) {
 28928        context.save();
 28929  
 28930        if (rs.pathCache) {
 28931          context.clip(rs.pathCache);
 28932        } else {
 28933          r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
 28934          context.clip();
 28935        }
 28936      }
 28937  
 28938      r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
 28939  
 28940      if (shouldClip) {
 28941        context.restore();
 28942      }
 28943    } else {
 28944      var pattern = context.createPattern(img, repeat);
 28945      context.fillStyle = pattern;
 28946      r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
 28947      context.translate(x, y);
 28948      context.fill();
 28949      context.translate(-x, -y);
 28950    }
 28951  
 28952    context.globalAlpha = gAlpha;
 28953  };
 28954  
 28955  var CRp$4 = {};
 28956  
 28957  CRp$4.eleTextBiggerThanMin = function (ele, scale) {
 28958    if (!scale) {
 28959      var zoom = ele.cy().zoom();
 28960      var pxRatio = this.getPixelRatio();
 28961      var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
 28962  
 28963      scale = Math.pow(2, lvl);
 28964    }
 28965  
 28966    var computedSize = ele.pstyle('font-size').pfValue * scale;
 28967    var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
 28968  
 28969    if (computedSize < minSize) {
 28970      return false;
 28971    }
 28972  
 28973    return true;
 28974  };
 28975  
 28976  CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
 28977    var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
 28978    var r = this;
 28979  
 28980    if (force == null) {
 28981      if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
 28982        return;
 28983      }
 28984    } else if (force === false) {
 28985      return;
 28986    }
 28987  
 28988    if (ele.isNode()) {
 28989      var label = ele.pstyle('label');
 28990  
 28991      if (!label || !label.value) {
 28992        return;
 28993      }
 28994  
 28995      var justification = r.getLabelJustification(ele);
 28996      context.textAlign = justification;
 28997      context.textBaseline = 'bottom';
 28998    } else {
 28999      var badLine = ele.element()._private.rscratch.badLine;
 29000  
 29001      var _label = ele.pstyle('label');
 29002  
 29003      var srcLabel = ele.pstyle('source-label');
 29004      var tgtLabel = ele.pstyle('target-label');
 29005  
 29006      if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
 29007        return;
 29008      }
 29009  
 29010      context.textAlign = 'center';
 29011      context.textBaseline = 'bottom';
 29012    }
 29013  
 29014    var applyRotation = !shiftToOriginWithBb;
 29015    var bb;
 29016  
 29017    if (shiftToOriginWithBb) {
 29018      bb = shiftToOriginWithBb;
 29019      context.translate(-bb.x1, -bb.y1);
 29020    }
 29021  
 29022    if (prefix == null) {
 29023      r.drawText(context, ele, null, applyRotation, useEleOpacity);
 29024  
 29025      if (ele.isEdge()) {
 29026        r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
 29027        r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
 29028      }
 29029    } else {
 29030      r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
 29031    }
 29032  
 29033    if (shiftToOriginWithBb) {
 29034      context.translate(bb.x1, bb.y1);
 29035    }
 29036  };
 29037  
 29038  CRp$4.getFontCache = function (context) {
 29039    var cache;
 29040    this.fontCaches = this.fontCaches || [];
 29041  
 29042    for (var i = 0; i < this.fontCaches.length; i++) {
 29043      cache = this.fontCaches[i];
 29044  
 29045      if (cache.context === context) {
 29046        return cache;
 29047      }
 29048    }
 29049  
 29050    cache = {
 29051      context: context
 29052    };
 29053    this.fontCaches.push(cache);
 29054    return cache;
 29055  }; // set up canvas context with font
 29056  // returns transformed text string
 29057  
 29058  
 29059  CRp$4.setupTextStyle = function (context, ele) {
 29060    var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
 29061    // Font style
 29062    var labelStyle = ele.pstyle('font-style').strValue;
 29063    var labelSize = ele.pstyle('font-size').pfValue + 'px';
 29064    var labelFamily = ele.pstyle('font-family').strValue;
 29065    var labelWeight = ele.pstyle('font-weight').strValue;
 29066    var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
 29067    var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
 29068    var color = ele.pstyle('color').value;
 29069    var outlineColor = ele.pstyle('text-outline-color').value;
 29070    context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
 29071    context.lineJoin = 'round'; // so text outlines aren't jagged
 29072  
 29073    this.colorFillStyle(context, color[0], color[1], color[2], opacity);
 29074    this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
 29075  }; // TODO ensure re-used
 29076  
 29077  
 29078  function roundRect(ctx, x, y, width, height) {
 29079    var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
 29080    ctx.beginPath();
 29081    ctx.moveTo(x + radius, y);
 29082    ctx.lineTo(x + width - radius, y);
 29083    ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
 29084    ctx.lineTo(x + width, y + height - radius);
 29085    ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
 29086    ctx.lineTo(x + radius, y + height);
 29087    ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
 29088    ctx.lineTo(x, y + radius);
 29089    ctx.quadraticCurveTo(x, y, x + radius, y);
 29090    ctx.closePath();
 29091    ctx.fill();
 29092  }
 29093  
 29094  CRp$4.getTextAngle = function (ele, prefix) {
 29095    var theta;
 29096    var _p = ele._private;
 29097    var rscratch = _p.rscratch;
 29098    var pdash = prefix ? prefix + '-' : '';
 29099    var rotation = ele.pstyle(pdash + 'text-rotation');
 29100    var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
 29101  
 29102    if (rotation.strValue === 'autorotate') {
 29103      theta = ele.isEdge() ? textAngle : 0;
 29104    } else if (rotation.strValue === 'none') {
 29105      theta = 0;
 29106    } else {
 29107      theta = rotation.pfValue;
 29108    }
 29109  
 29110    return theta;
 29111  };
 29112  
 29113  CRp$4.drawText = function (context, ele, prefix) {
 29114    var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
 29115    var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
 29116    var _p = ele._private;
 29117    var rscratch = _p.rscratch;
 29118    var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
 29119  
 29120    if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
 29121      return;
 29122    } // use 'main' as an alias for the main label (i.e. null prefix)
 29123  
 29124  
 29125    if (prefix === 'main') {
 29126      prefix = null;
 29127    }
 29128  
 29129    var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
 29130    var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
 29131    var orgTextX, orgTextY; // used for rotation
 29132  
 29133    var text = this.getLabelText(ele, prefix);
 29134  
 29135    if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
 29136      this.setupTextStyle(context, ele, useEleOpacity);
 29137      var pdash = prefix ? prefix + '-' : '';
 29138      var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
 29139      var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
 29140      var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
 29141      var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
 29142      var isEdge = ele.isEdge();
 29143      var halign = ele.pstyle('text-halign').value;
 29144      var valign = ele.pstyle('text-valign').value;
 29145  
 29146      if (isEdge) {
 29147        halign = 'center';
 29148        valign = 'center';
 29149      }
 29150  
 29151      textX += marginX;
 29152      textY += marginY;
 29153      var theta;
 29154  
 29155      if (!applyRotation) {
 29156        theta = 0;
 29157      } else {
 29158        theta = this.getTextAngle(ele, prefix);
 29159      }
 29160  
 29161      if (theta !== 0) {
 29162        orgTextX = textX;
 29163        orgTextY = textY;
 29164        context.translate(orgTextX, orgTextY);
 29165        context.rotate(theta);
 29166        textX = 0;
 29167        textY = 0;
 29168      }
 29169  
 29170      switch (valign) {
 29171        case 'top':
 29172          break;
 29173  
 29174        case 'center':
 29175          textY += textH / 2;
 29176          break;
 29177  
 29178        case 'bottom':
 29179          textY += textH;
 29180          break;
 29181      }
 29182  
 29183      var backgroundOpacity = ele.pstyle('text-background-opacity').value;
 29184      var borderOpacity = ele.pstyle('text-border-opacity').value;
 29185      var textBorderWidth = ele.pstyle('text-border-width').pfValue;
 29186      var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
 29187  
 29188      if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
 29189        var bgX = textX - backgroundPadding;
 29190  
 29191        switch (halign) {
 29192          case 'left':
 29193            bgX -= textW;
 29194            break;
 29195  
 29196          case 'center':
 29197            bgX -= textW / 2;
 29198            break;
 29199        }
 29200  
 29201        var bgY = textY - textH - backgroundPadding;
 29202        var bgW = textW + 2 * backgroundPadding;
 29203        var bgH = textH + 2 * backgroundPadding;
 29204  
 29205        if (backgroundOpacity > 0) {
 29206          var textFill = context.fillStyle;
 29207          var textBackgroundColor = ele.pstyle('text-background-color').value;
 29208          context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
 29209          var styleShape = ele.pstyle('text-background-shape').strValue;
 29210  
 29211          if (styleShape.indexOf('round') === 0) {
 29212            roundRect(context, bgX, bgY, bgW, bgH, 2);
 29213          } else {
 29214            context.fillRect(bgX, bgY, bgW, bgH);
 29215          }
 29216  
 29217          context.fillStyle = textFill;
 29218        }
 29219  
 29220        if (textBorderWidth > 0 && borderOpacity > 0) {
 29221          var textStroke = context.strokeStyle;
 29222          var textLineWidth = context.lineWidth;
 29223          var textBorderColor = ele.pstyle('text-border-color').value;
 29224          var textBorderStyle = ele.pstyle('text-border-style').value;
 29225          context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
 29226          context.lineWidth = textBorderWidth;
 29227  
 29228          if (context.setLineDash) {
 29229            // for very outofdate browsers
 29230            switch (textBorderStyle) {
 29231              case 'dotted':
 29232                context.setLineDash([1, 1]);
 29233                break;
 29234  
 29235              case 'dashed':
 29236                context.setLineDash([4, 2]);
 29237                break;
 29238  
 29239              case 'double':
 29240                context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
 29241  
 29242                context.setLineDash([]);
 29243                break;
 29244  
 29245              case 'solid':
 29246                context.setLineDash([]);
 29247                break;
 29248            }
 29249          }
 29250  
 29251          context.strokeRect(bgX, bgY, bgW, bgH);
 29252  
 29253          if (textBorderStyle === 'double') {
 29254            var whiteWidth = textBorderWidth / 2;
 29255            context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
 29256          }
 29257  
 29258          if (context.setLineDash) {
 29259            // for very outofdate browsers
 29260            context.setLineDash([]);
 29261          }
 29262  
 29263          context.lineWidth = textLineWidth;
 29264          context.strokeStyle = textStroke;
 29265        }
 29266      }
 29267  
 29268      var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
 29269  
 29270      if (lineWidth > 0) {
 29271        context.lineWidth = lineWidth;
 29272      }
 29273  
 29274      if (ele.pstyle('text-wrap').value === 'wrap') {
 29275        var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
 29276        var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
 29277        var halfTextW = textW / 2;
 29278        var justification = this.getLabelJustification(ele);
 29279  
 29280        if (justification === 'auto') ; else if (halign === 'left') {
 29281          // auto justification : right
 29282          if (justification === 'left') {
 29283            textX += -textW;
 29284          } else if (justification === 'center') {
 29285            textX += -halfTextW;
 29286          } // else same as auto
 29287  
 29288        } else if (halign === 'center') {
 29289          // auto justfication : center
 29290          if (justification === 'left') {
 29291            textX += -halfTextW;
 29292          } else if (justification === 'right') {
 29293            textX += halfTextW;
 29294          } // else same as auto
 29295  
 29296        } else if (halign === 'right') {
 29297          // auto justification : left
 29298          if (justification === 'center') {
 29299            textX += halfTextW;
 29300          } else if (justification === 'right') {
 29301            textX += textW;
 29302          } // else same as auto
 29303  
 29304        }
 29305  
 29306        switch (valign) {
 29307          case 'top':
 29308            textY -= (lines.length - 1) * lineHeight;
 29309            break;
 29310  
 29311          case 'center':
 29312          case 'bottom':
 29313            textY -= (lines.length - 1) * lineHeight;
 29314            break;
 29315        }
 29316  
 29317        for (var l = 0; l < lines.length; l++) {
 29318          if (lineWidth > 0) {
 29319            context.strokeText(lines[l], textX, textY);
 29320          }
 29321  
 29322          context.fillText(lines[l], textX, textY);
 29323          textY += lineHeight;
 29324        }
 29325      } else {
 29326        if (lineWidth > 0) {
 29327          context.strokeText(text, textX, textY);
 29328        }
 29329  
 29330        context.fillText(text, textX, textY);
 29331      }
 29332  
 29333      if (theta !== 0) {
 29334        context.rotate(-theta);
 29335        context.translate(-orgTextX, -orgTextY);
 29336      }
 29337    }
 29338  };
 29339  
 29340  /* global Path2D */
 29341  var CRp$5 = {};
 29342  
 29343  CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
 29344    var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
 29345    var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
 29346    var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
 29347    var r = this;
 29348    var nodeWidth, nodeHeight;
 29349    var _p = node._private;
 29350    var rs = _p.rscratch;
 29351    var pos = node.position();
 29352  
 29353    if (!number(pos.x) || !number(pos.y)) {
 29354      return; // can't draw node with undefined position
 29355    }
 29356  
 29357    if (shouldDrawOpacity && !node.visible()) {
 29358      return;
 29359    }
 29360  
 29361    var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
 29362    var usePaths = r.usePaths();
 29363    var path;
 29364    var pathCacheHit = false;
 29365    var padding = node.padding();
 29366    nodeWidth = node.width() + 2 * padding;
 29367    nodeHeight = node.height() + 2 * padding; //
 29368    // setup shift
 29369  
 29370    var bb;
 29371  
 29372    if (shiftToOriginWithBb) {
 29373      bb = shiftToOriginWithBb;
 29374      context.translate(-bb.x1, -bb.y1);
 29375    } //
 29376    // load bg image
 29377  
 29378  
 29379    var bgImgProp = node.pstyle('background-image');
 29380    var urls = bgImgProp.value;
 29381    var urlDefined = new Array(urls.length);
 29382    var image = new Array(urls.length);
 29383    var numImages = 0;
 29384  
 29385    for (var i = 0; i < urls.length; i++) {
 29386      var url = urls[i];
 29387      var defd = urlDefined[i] = url != null && url !== 'none';
 29388  
 29389      if (defd) {
 29390        var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
 29391        numImages++; // get image, and if not loaded then ask to redraw when later loaded
 29392  
 29393        image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
 29394          _p.backgroundTimestamp = Date.now();
 29395          node.emitAndNotify('background');
 29396        });
 29397      }
 29398    } //
 29399    // setup styles
 29400  
 29401  
 29402    var darkness = node.pstyle('background-blacken').value;
 29403    var borderWidth = node.pstyle('border-width').pfValue;
 29404    var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
 29405    var borderColor = node.pstyle('border-color').value;
 29406    var borderStyle = node.pstyle('border-style').value;
 29407    var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
 29408    context.lineJoin = 'miter'; // so borders are square with the node shape
 29409  
 29410    var setupShapeColor = function setupShapeColor() {
 29411      var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
 29412      r.eleFillStyle(context, node, bgOpy);
 29413    };
 29414  
 29415    var setupBorderColor = function setupBorderColor() {
 29416      var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
 29417      r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
 29418    }; //
 29419    // setup shape
 29420  
 29421  
 29422    var styleShape = node.pstyle('shape').strValue;
 29423    var shapePts = node.pstyle('shape-polygon-points').pfValue;
 29424  
 29425    if (usePaths) {
 29426      context.translate(pos.x, pos.y);
 29427      var pathCache = r.nodePathCache = r.nodePathCache || [];
 29428      var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
 29429      var cachedPath = pathCache[key];
 29430  
 29431      if (cachedPath != null) {
 29432        path = cachedPath;
 29433        pathCacheHit = true;
 29434        rs.pathCache = path;
 29435      } else {
 29436        path = new Path2D();
 29437        pathCache[key] = rs.pathCache = path;
 29438      }
 29439    }
 29440  
 29441    var drawShape = function drawShape() {
 29442      if (!pathCacheHit) {
 29443        var npos = pos;
 29444  
 29445        if (usePaths) {
 29446          npos = {
 29447            x: 0,
 29448            y: 0
 29449          };
 29450        }
 29451  
 29452        r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
 29453      }
 29454  
 29455      if (usePaths) {
 29456        context.fill(path);
 29457      } else {
 29458        context.fill();
 29459      }
 29460    };
 29461  
 29462    var drawImages = function drawImages() {
 29463      var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
 29464      var prevBging = _p.backgrounding;
 29465      var totalCompleted = 0;
 29466  
 29467      for (var _i = 0; _i < image.length; _i++) {
 29468        if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
 29469          totalCompleted++;
 29470          r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
 29471        }
 29472      }
 29473  
 29474      _p.backgrounding = !(totalCompleted === numImages);
 29475  
 29476      if (prevBging !== _p.backgrounding) {
 29477        // update style b/c :backgrounding state changed
 29478        node.updateStyle(false);
 29479      }
 29480    };
 29481  
 29482    var drawPie = function drawPie() {
 29483      var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
 29484      var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
 29485  
 29486      if (r.hasPie(node)) {
 29487        r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
 29488  
 29489        if (redrawShape) {
 29490          if (!usePaths) {
 29491            r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
 29492          }
 29493        }
 29494      }
 29495    };
 29496  
 29497    var darken = function darken() {
 29498      var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
 29499      var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
 29500      var c = darkness > 0 ? 0 : 255;
 29501  
 29502      if (darkness !== 0) {
 29503        r.colorFillStyle(context, c, c, c, opacity);
 29504  
 29505        if (usePaths) {
 29506          context.fill(path);
 29507        } else {
 29508          context.fill();
 29509        }
 29510      }
 29511    };
 29512  
 29513    var drawBorder = function drawBorder() {
 29514      if (borderWidth > 0) {
 29515        context.lineWidth = borderWidth;
 29516        context.lineCap = 'butt';
 29517  
 29518        if (context.setLineDash) {
 29519          // for very outofdate browsers
 29520          switch (borderStyle) {
 29521            case 'dotted':
 29522              context.setLineDash([1, 1]);
 29523              break;
 29524  
 29525            case 'dashed':
 29526              context.setLineDash([4, 2]);
 29527              break;
 29528  
 29529            case 'solid':
 29530            case 'double':
 29531              context.setLineDash([]);
 29532              break;
 29533          }
 29534        }
 29535  
 29536        if (usePaths) {
 29537          context.stroke(path);
 29538        } else {
 29539          context.stroke();
 29540        }
 29541  
 29542        if (borderStyle === 'double') {
 29543          context.lineWidth = borderWidth / 3;
 29544          var gco = context.globalCompositeOperation;
 29545          context.globalCompositeOperation = 'destination-out';
 29546  
 29547          if (usePaths) {
 29548            context.stroke(path);
 29549          } else {
 29550            context.stroke();
 29551          }
 29552  
 29553          context.globalCompositeOperation = gco;
 29554        } // reset in case we changed the border style
 29555  
 29556  
 29557        if (context.setLineDash) {
 29558          // for very outofdate browsers
 29559          context.setLineDash([]);
 29560        }
 29561      }
 29562    };
 29563  
 29564    var drawOverlay = function drawOverlay() {
 29565      if (shouldDrawOverlay) {
 29566        r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
 29567      }
 29568    };
 29569  
 29570    var drawText = function drawText() {
 29571      r.drawElementText(context, node, null, drawLabel);
 29572    };
 29573  
 29574    var ghost = node.pstyle('ghost').value === 'yes';
 29575  
 29576    if (ghost) {
 29577      var gx = node.pstyle('ghost-offset-x').pfValue;
 29578      var gy = node.pstyle('ghost-offset-y').pfValue;
 29579      var ghostOpacity = node.pstyle('ghost-opacity').value;
 29580      var effGhostOpacity = ghostOpacity * eleOpacity;
 29581      context.translate(gx, gy);
 29582      setupShapeColor(ghostOpacity * bgOpacity);
 29583      drawShape();
 29584      drawImages(effGhostOpacity);
 29585      drawPie(darkness !== 0 || borderWidth !== 0);
 29586      darken(effGhostOpacity);
 29587      setupBorderColor(ghostOpacity * borderOpacity);
 29588      drawBorder();
 29589      context.translate(-gx, -gy);
 29590    }
 29591  
 29592    setupShapeColor();
 29593    drawShape();
 29594    drawImages();
 29595    drawPie(darkness !== 0 || borderWidth !== 0);
 29596    darken();
 29597    setupBorderColor();
 29598    drawBorder();
 29599  
 29600    if (usePaths) {
 29601      context.translate(-pos.x, -pos.y);
 29602    }
 29603  
 29604    drawText();
 29605    drawOverlay(); //
 29606    // clean up shift
 29607  
 29608    if (shiftToOriginWithBb) {
 29609      context.translate(bb.x1, bb.y1);
 29610    }
 29611  };
 29612  
 29613  CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
 29614    var r = this;
 29615  
 29616    if (!node.visible()) {
 29617      return;
 29618    }
 29619  
 29620    var overlayPadding = node.pstyle('overlay-padding').pfValue;
 29621    var overlayOpacity = node.pstyle('overlay-opacity').value;
 29622    var overlayColor = node.pstyle('overlay-color').value;
 29623  
 29624    if (overlayOpacity > 0) {
 29625      pos = pos || node.position();
 29626  
 29627      if (nodeWidth == null || nodeHeight == null) {
 29628        var padding = node.padding();
 29629        nodeWidth = node.width() + 2 * padding;
 29630        nodeHeight = node.height() + 2 * padding;
 29631      }
 29632  
 29633      r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
 29634      r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
 29635      context.fill();
 29636    }
 29637  }; // does the node have at least one pie piece?
 29638  
 29639  
 29640  CRp$5.hasPie = function (node) {
 29641    node = node[0]; // ensure ele ref
 29642  
 29643    return node._private.hasPie;
 29644  };
 29645  
 29646  CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
 29647    node = node[0]; // ensure ele ref
 29648  
 29649    pos = pos || node.position();
 29650    var cyStyle = node.cy().style();
 29651    var pieSize = node.pstyle('pie-size');
 29652    var x = pos.x;
 29653    var y = pos.y;
 29654    var nodeW = node.width();
 29655    var nodeH = node.height();
 29656    var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
 29657  
 29658    var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
 29659  
 29660    var usePaths = this.usePaths();
 29661  
 29662    if (usePaths) {
 29663      x = 0;
 29664      y = 0;
 29665    }
 29666  
 29667    if (pieSize.units === '%') {
 29668      radius = radius * pieSize.pfValue;
 29669    } else if (pieSize.pfValue !== undefined) {
 29670      radius = pieSize.pfValue / 2;
 29671    }
 29672  
 29673    for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
 29674      // 1..N
 29675      var size = node.pstyle('pie-' + i + '-background-size').value;
 29676      var color = node.pstyle('pie-' + i + '-background-color').value;
 29677      var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
 29678      var percent = size / 100; // map integer range [0, 100] to [0, 1]
 29679      // percent can't push beyond 1
 29680  
 29681      if (percent + lastPercent > 1) {
 29682        percent = 1 - lastPercent;
 29683      }
 29684  
 29685      var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
 29686  
 29687      var angleDelta = 2 * Math.PI * percent;
 29688      var angleEnd = angleStart + angleDelta; // ignore if
 29689      // - zero size
 29690      // - we're already beyond the full circle
 29691      // - adding the current slice would go beyond the full circle
 29692  
 29693      if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
 29694        continue;
 29695      }
 29696  
 29697      context.beginPath();
 29698      context.moveTo(x, y);
 29699      context.arc(x, y, radius, angleStart, angleEnd);
 29700      context.closePath();
 29701      this.colorFillStyle(context, color[0], color[1], color[2], opacity);
 29702      context.fill();
 29703      lastPercent += percent;
 29704    }
 29705  };
 29706  
 29707  var CRp$6 = {};
 29708  var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
 29709  
 29710  CRp$6.getPixelRatio = function () {
 29711    var context = this.data.contexts[0];
 29712  
 29713    if (this.forcedPixelRatio != null) {
 29714      return this.forcedPixelRatio;
 29715    }
 29716  
 29717    var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
 29718    return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
 29719  };
 29720  
 29721  CRp$6.paintCache = function (context) {
 29722    var caches = this.paintCaches = this.paintCaches || [];
 29723    var needToCreateCache = true;
 29724    var cache;
 29725  
 29726    for (var i = 0; i < caches.length; i++) {
 29727      cache = caches[i];
 29728  
 29729      if (cache.context === context) {
 29730        needToCreateCache = false;
 29731        break;
 29732      }
 29733    }
 29734  
 29735    if (needToCreateCache) {
 29736      cache = {
 29737        context: context
 29738      };
 29739      caches.push(cache);
 29740    }
 29741  
 29742    return cache;
 29743  };
 29744  
 29745  CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
 29746    var gradientStyle;
 29747    var usePaths = this.usePaths();
 29748    var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
 29749        positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
 29750  
 29751    if (fill === 'radial-gradient') {
 29752      if (ele.isEdge()) {
 29753        var start = ele.sourceEndpoint(),
 29754            end = ele.targetEndpoint(),
 29755            mid = ele.midpoint();
 29756        var d1 = dist(start, mid);
 29757        var d2 = dist(end, mid);
 29758        gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
 29759      } else {
 29760        var pos = usePaths ? {
 29761          x: 0,
 29762          y: 0
 29763        } : ele.position(),
 29764            width = ele.paddedWidth(),
 29765            height = ele.paddedHeight();
 29766        gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
 29767      }
 29768    } else {
 29769      if (ele.isEdge()) {
 29770        var _start = ele.sourceEndpoint(),
 29771            _end = ele.targetEndpoint();
 29772  
 29773        gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
 29774      } else {
 29775        var _pos = usePaths ? {
 29776          x: 0,
 29777          y: 0
 29778        } : ele.position(),
 29779            _width = ele.paddedWidth(),
 29780            _height = ele.paddedHeight(),
 29781            halfWidth = _width / 2,
 29782            halfHeight = _height / 2;
 29783  
 29784        var direction = ele.pstyle('background-gradient-direction').value;
 29785  
 29786        switch (direction) {
 29787          case 'to-bottom':
 29788            gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
 29789            break;
 29790  
 29791          case 'to-top':
 29792            gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
 29793            break;
 29794  
 29795          case 'to-left':
 29796            gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
 29797            break;
 29798  
 29799          case 'to-right':
 29800            gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
 29801            break;
 29802  
 29803          case 'to-bottom-right':
 29804          case 'to-right-bottom':
 29805            gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
 29806            break;
 29807  
 29808          case 'to-top-right':
 29809          case 'to-right-top':
 29810            gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
 29811            break;
 29812  
 29813          case 'to-bottom-left':
 29814          case 'to-left-bottom':
 29815            gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
 29816            break;
 29817  
 29818          case 'to-top-left':
 29819          case 'to-left-top':
 29820            gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
 29821            break;
 29822        }
 29823      }
 29824    }
 29825  
 29826    if (!gradientStyle) return null; // invalid gradient style
 29827  
 29828    var hasPositions = positions.length === colors.length;
 29829    var length = colors.length;
 29830  
 29831    for (var i = 0; i < length; i++) {
 29832      gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
 29833    }
 29834  
 29835    return gradientStyle;
 29836  };
 29837  
 29838  CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
 29839    var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
 29840    if (!gradientStyle) return null; // error
 29841  
 29842    context.fillStyle = gradientStyle;
 29843  };
 29844  
 29845  CRp$6.colorFillStyle = function (context, r, g, b, a) {
 29846    context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
 29847    // var cache = this.paintCache(context);
 29848    // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
 29849    // if( cache.fillStyle !== fillStyle ){
 29850    //   context.fillStyle = cache.fillStyle = fillStyle;
 29851    // }
 29852  };
 29853  
 29854  CRp$6.eleFillStyle = function (context, ele, opacity) {
 29855    var backgroundFill = ele.pstyle('background-fill').value;
 29856  
 29857    if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
 29858      this.gradientFillStyle(context, ele, backgroundFill, opacity);
 29859    } else {
 29860      var backgroundColor = ele.pstyle('background-color').value;
 29861      this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
 29862    }
 29863  };
 29864  
 29865  CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
 29866    var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
 29867    if (!gradientStyle) return null; // error
 29868  
 29869    context.strokeStyle = gradientStyle;
 29870  };
 29871  
 29872  CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
 29873    context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
 29874    // var cache = this.paintCache(context);
 29875    // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
 29876    // if( cache.strokeStyle !== strokeStyle ){
 29877    //   context.strokeStyle = cache.strokeStyle = strokeStyle;
 29878    // }
 29879  };
 29880  
 29881  CRp$6.eleStrokeStyle = function (context, ele, opacity) {
 29882    var lineFill = ele.pstyle('line-fill').value;
 29883  
 29884    if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
 29885      this.gradientStrokeStyle(context, ele, lineFill, opacity);
 29886    } else {
 29887      var lineColor = ele.pstyle('line-color').value;
 29888      this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
 29889    }
 29890  }; // Resize canvas
 29891  
 29892  
 29893  CRp$6.matchCanvasSize = function (container) {
 29894    var r = this;
 29895    var data = r.data;
 29896    var bb = r.findContainerClientCoords();
 29897    var width = bb[2];
 29898    var height = bb[3];
 29899    var pixelRatio = r.getPixelRatio();
 29900    var mbPxRatio = r.motionBlurPxRatio;
 29901  
 29902    if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
 29903      pixelRatio = mbPxRatio;
 29904    }
 29905  
 29906    var canvasWidth = width * pixelRatio;
 29907    var canvasHeight = height * pixelRatio;
 29908    var canvas;
 29909  
 29910    if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
 29911      return; // save cycles if same
 29912    }
 29913  
 29914    r.fontCaches = null; // resizing resets the style
 29915  
 29916    var canvasContainer = data.canvasContainer;
 29917    canvasContainer.style.width = width + 'px';
 29918    canvasContainer.style.height = height + 'px';
 29919  
 29920    for (var i = 0; i < r.CANVAS_LAYERS; i++) {
 29921      canvas = data.canvases[i];
 29922      canvas.width = canvasWidth;
 29923      canvas.height = canvasHeight;
 29924      canvas.style.width = width + 'px';
 29925      canvas.style.height = height + 'px';
 29926    }
 29927  
 29928    for (var i = 0; i < r.BUFFER_COUNT; i++) {
 29929      canvas = data.bufferCanvases[i];
 29930      canvas.width = canvasWidth;
 29931      canvas.height = canvasHeight;
 29932      canvas.style.width = width + 'px';
 29933      canvas.style.height = height + 'px';
 29934    }
 29935  
 29936    r.textureMult = 1;
 29937  
 29938    if (pixelRatio <= 1) {
 29939      canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
 29940      r.textureMult = 2;
 29941      canvas.width = canvasWidth * r.textureMult;
 29942      canvas.height = canvasHeight * r.textureMult;
 29943    }
 29944  
 29945    r.canvasWidth = canvasWidth;
 29946    r.canvasHeight = canvasHeight;
 29947  };
 29948  
 29949  CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
 29950    this.render({
 29951      forcedContext: cxt,
 29952      forcedZoom: zoom,
 29953      forcedPan: pan,
 29954      drawAllLayers: true,
 29955      forcedPxRatio: pxRatio
 29956    });
 29957  };
 29958  
 29959  CRp$6.render = function (options) {
 29960    options = options || staticEmptyObject();
 29961    var forcedContext = options.forcedContext;
 29962    var drawAllLayers = options.drawAllLayers;
 29963    var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
 29964    var forcedZoom = options.forcedZoom;
 29965    var forcedPan = options.forcedPan;
 29966    var r = this;
 29967    var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
 29968    var cy = r.cy;
 29969    var data = r.data;
 29970    var needDraw = data.canvasNeedsRedraw;
 29971    var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
 29972    var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
 29973    var mbPxRatio = r.motionBlurPxRatio;
 29974    var hasCompoundNodes = cy.hasCompoundNodes();
 29975    var inNodeDragGesture = r.hoverData.draggingEles;
 29976    var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
 29977    motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
 29978    var motionBlurFadeEffect = motionBlur;
 29979  
 29980    if (!forcedContext) {
 29981      if (r.prevPxRatio !== pixelRatio) {
 29982        r.invalidateContainerClientCoordsCache();
 29983        r.matchCanvasSize(r.container);
 29984        r.redrawHint('eles', true);
 29985        r.redrawHint('drag', true);
 29986      }
 29987  
 29988      r.prevPxRatio = pixelRatio;
 29989    }
 29990  
 29991    if (!forcedContext && r.motionBlurTimeout) {
 29992      clearTimeout(r.motionBlurTimeout);
 29993    }
 29994  
 29995    if (motionBlur) {
 29996      if (r.mbFrames == null) {
 29997        r.mbFrames = 0;
 29998      }
 29999  
 30000      r.mbFrames++;
 30001  
 30002      if (r.mbFrames < 3) {
 30003        // need several frames before even high quality motionblur
 30004        motionBlurFadeEffect = false;
 30005      } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
 30006  
 30007  
 30008      if (r.mbFrames > r.minMbLowQualFrames) {
 30009        //r.fullQualityMb = false;
 30010        r.motionBlurPxRatio = r.mbPxRBlurry;
 30011      }
 30012    }
 30013  
 30014    if (r.clearingMotionBlur) {
 30015      r.motionBlurPxRatio = 1;
 30016    } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
 30017    // because a rogue async texture frame would clear needDraw
 30018  
 30019  
 30020    if (r.textureDrawLastFrame && !textureDraw) {
 30021      needDraw[r.NODE] = true;
 30022      needDraw[r.SELECT_BOX] = true;
 30023    }
 30024  
 30025    var style = cy.style();
 30026    var zoom = cy.zoom();
 30027    var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
 30028    var pan = cy.pan();
 30029    var effectivePan = {
 30030      x: pan.x,
 30031      y: pan.y
 30032    };
 30033    var vp = {
 30034      zoom: zoom,
 30035      pan: {
 30036        x: pan.x,
 30037        y: pan.y
 30038      }
 30039    };
 30040    var prevVp = r.prevViewport;
 30041    var viewportIsDiff = prevVp === undefined || vp.zoom !== prevVp.zoom || vp.pan.x !== prevVp.pan.x || vp.pan.y !== prevVp.pan.y; // we want the low quality motionblur only when the viewport is being manipulated etc (where it's not noticed)
 30042  
 30043    if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
 30044      r.motionBlurPxRatio = 1;
 30045    }
 30046  
 30047    if (forcedPan) {
 30048      effectivePan = forcedPan;
 30049    } // apply pixel ratio
 30050  
 30051  
 30052    effectiveZoom *= pixelRatio;
 30053    effectivePan.x *= pixelRatio;
 30054    effectivePan.y *= pixelRatio;
 30055    var eles = r.getCachedZSortedEles();
 30056  
 30057    function mbclear(context, x, y, w, h) {
 30058      var gco = context.globalCompositeOperation;
 30059      context.globalCompositeOperation = 'destination-out';
 30060      r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
 30061      context.fillRect(x, y, w, h);
 30062      context.globalCompositeOperation = gco;
 30063    }
 30064  
 30065    function setContextTransform(context, clear) {
 30066      var ePan, eZoom, w, h;
 30067  
 30068      if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
 30069        ePan = {
 30070          x: pan.x * mbPxRatio,
 30071          y: pan.y * mbPxRatio
 30072        };
 30073        eZoom = zoom * mbPxRatio;
 30074        w = r.canvasWidth * mbPxRatio;
 30075        h = r.canvasHeight * mbPxRatio;
 30076      } else {
 30077        ePan = effectivePan;
 30078        eZoom = effectiveZoom;
 30079        w = r.canvasWidth;
 30080        h = r.canvasHeight;
 30081      }
 30082  
 30083      context.setTransform(1, 0, 0, 1, 0, 0);
 30084  
 30085      if (clear === 'motionBlur') {
 30086        mbclear(context, 0, 0, w, h);
 30087      } else if (!forcedContext && (clear === undefined || clear)) {
 30088        context.clearRect(0, 0, w, h);
 30089      }
 30090  
 30091      if (!drawAllLayers) {
 30092        context.translate(ePan.x, ePan.y);
 30093        context.scale(eZoom, eZoom);
 30094      }
 30095  
 30096      if (forcedPan) {
 30097        context.translate(forcedPan.x, forcedPan.y);
 30098      }
 30099  
 30100      if (forcedZoom) {
 30101        context.scale(forcedZoom, forcedZoom);
 30102      }
 30103    }
 30104  
 30105    if (!textureDraw) {
 30106      r.textureDrawLastFrame = false;
 30107    }
 30108  
 30109    if (textureDraw) {
 30110      r.textureDrawLastFrame = true;
 30111  
 30112      if (!r.textureCache) {
 30113        r.textureCache = {};
 30114        r.textureCache.bb = cy.mutableElements().boundingBox();
 30115        r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
 30116        var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
 30117        cxt.setTransform(1, 0, 0, 1, 0, 0);
 30118        cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
 30119        r.render({
 30120          forcedContext: cxt,
 30121          drawOnlyNodeLayer: true,
 30122          forcedPxRatio: pixelRatio * r.textureMult
 30123        });
 30124        var vp = r.textureCache.viewport = {
 30125          zoom: cy.zoom(),
 30126          pan: cy.pan(),
 30127          width: r.canvasWidth,
 30128          height: r.canvasHeight
 30129        };
 30130        vp.mpan = {
 30131          x: (0 - vp.pan.x) / vp.zoom,
 30132          y: (0 - vp.pan.y) / vp.zoom
 30133        };
 30134      }
 30135  
 30136      needDraw[r.DRAG] = false;
 30137      needDraw[r.NODE] = false;
 30138      var context = data.contexts[r.NODE];
 30139      var texture = r.textureCache.texture;
 30140      var vp = r.textureCache.viewport;
 30141      context.setTransform(1, 0, 0, 1, 0, 0);
 30142  
 30143      if (motionBlur) {
 30144        mbclear(context, 0, 0, vp.width, vp.height);
 30145      } else {
 30146        context.clearRect(0, 0, vp.width, vp.height);
 30147      }
 30148  
 30149      var outsideBgColor = style.core('outside-texture-bg-color').value;
 30150      var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
 30151      r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
 30152      context.fillRect(0, 0, vp.width, vp.height);
 30153      var zoom = cy.zoom();
 30154      setContextTransform(context, false);
 30155      context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
 30156      context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
 30157    } else if (r.textureOnViewport && !forcedContext) {
 30158      // clear the cache since we don't need it
 30159      r.textureCache = null;
 30160    }
 30161  
 30162    var extent = cy.extent();
 30163    var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
 30164    var hideEdges = r.hideEdgesOnViewport && vpManip;
 30165    var needMbClear = [];
 30166    needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
 30167  
 30168    if (needMbClear[r.NODE]) {
 30169      r.clearedForMotionBlur[r.NODE] = true;
 30170    }
 30171  
 30172    needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
 30173  
 30174    if (needMbClear[r.DRAG]) {
 30175      r.clearedForMotionBlur[r.DRAG] = true;
 30176    }
 30177  
 30178    if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
 30179      var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
 30180      var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
 30181      var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
 30182      setContextTransform(context, clear);
 30183  
 30184      if (hideEdges) {
 30185        r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
 30186      } else {
 30187        r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
 30188      }
 30189  
 30190      if (r.debug) {
 30191        r.drawDebugPoints(context, eles.nondrag);
 30192      }
 30193  
 30194      if (!drawAllLayers && !motionBlur) {
 30195        needDraw[r.NODE] = false;
 30196      }
 30197    }
 30198  
 30199    if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
 30200      var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
 30201      var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
 30202      setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
 30203  
 30204      if (hideEdges) {
 30205        r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
 30206      } else {
 30207        r.drawCachedElements(context, eles.drag, pixelRatio, extent);
 30208      }
 30209  
 30210      if (r.debug) {
 30211        r.drawDebugPoints(context, eles.drag);
 30212      }
 30213  
 30214      if (!drawAllLayers && !motionBlur) {
 30215        needDraw[r.DRAG] = false;
 30216      }
 30217    }
 30218  
 30219    if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
 30220      var context = forcedContext || data.contexts[r.SELECT_BOX];
 30221      setContextTransform(context);
 30222  
 30223      if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
 30224        var zoom = r.cy.zoom();
 30225        var borderWidth = style.core('selection-box-border-width').value / zoom;
 30226        context.lineWidth = borderWidth;
 30227        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 + ')';
 30228        context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
 30229  
 30230        if (borderWidth > 0) {
 30231          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 + ')';
 30232          context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
 30233        }
 30234      }
 30235  
 30236      if (data.bgActivePosistion && !r.hoverData.selecting) {
 30237        var zoom = r.cy.zoom();
 30238        var pos = data.bgActivePosistion;
 30239        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 + ')';
 30240        context.beginPath();
 30241        context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
 30242        context.fill();
 30243      }
 30244  
 30245      var timeToRender = r.lastRedrawTime;
 30246  
 30247      if (r.showFps && timeToRender) {
 30248        timeToRender = Math.round(timeToRender);
 30249        var fps = Math.round(1000 / timeToRender);
 30250        context.setTransform(1, 0, 0, 1, 0, 0);
 30251        context.fillStyle = 'rgba(255, 0, 0, 0.75)';
 30252        context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
 30253        context.lineWidth = 1;
 30254        context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
 30255        var maxFps = 60;
 30256        context.strokeRect(0, 30, 250, 20);
 30257        context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
 30258      }
 30259  
 30260      if (!drawAllLayers) {
 30261        needDraw[r.SELECT_BOX] = false;
 30262      }
 30263    } // motionblur: blit rendered blurry frames
 30264  
 30265  
 30266    if (motionBlur && mbPxRatio !== 1) {
 30267      var cxtNode = data.contexts[r.NODE];
 30268      var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
 30269      var cxtDrag = data.contexts[r.DRAG];
 30270      var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
 30271  
 30272      var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
 30273        cxt.setTransform(1, 0, 0, 1, 0, 0);
 30274  
 30275        if (needClear || !motionBlurFadeEffect) {
 30276          cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
 30277        } else {
 30278          mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
 30279        }
 30280  
 30281        var pxr = mbPxRatio;
 30282        cxt.drawImage(txt, // img
 30283        0, 0, // sx, sy
 30284        r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
 30285        0, 0, // x, y
 30286        r.canvasWidth, r.canvasHeight // w, h
 30287        );
 30288      };
 30289  
 30290      if (needDraw[r.NODE] || needMbClear[r.NODE]) {
 30291        drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
 30292        needDraw[r.NODE] = false;
 30293      }
 30294  
 30295      if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
 30296        drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
 30297        needDraw[r.DRAG] = false;
 30298      }
 30299    }
 30300  
 30301    r.prevViewport = vp;
 30302  
 30303    if (r.clearingMotionBlur) {
 30304      r.clearingMotionBlur = false;
 30305      r.motionBlurCleared = true;
 30306      r.motionBlur = true;
 30307    }
 30308  
 30309    if (motionBlur) {
 30310      r.motionBlurTimeout = setTimeout(function () {
 30311        r.motionBlurTimeout = null;
 30312        r.clearedForMotionBlur[r.NODE] = false;
 30313        r.clearedForMotionBlur[r.DRAG] = false;
 30314        r.motionBlur = false;
 30315        r.clearingMotionBlur = !textureDraw;
 30316        r.mbFrames = 0;
 30317        needDraw[r.NODE] = true;
 30318        needDraw[r.DRAG] = true;
 30319        r.redraw();
 30320      }, motionBlurDelay);
 30321    }
 30322  
 30323    if (!forcedContext) {
 30324      cy.emit('render');
 30325    }
 30326  };
 30327  
 30328  var CRp$7 = {}; // @O Polygon drawing
 30329  
 30330  CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
 30331    var halfW = width / 2;
 30332    var halfH = height / 2;
 30333  
 30334    if (context.beginPath) {
 30335      context.beginPath();
 30336    }
 30337  
 30338    context.moveTo(x + halfW * points[0], y + halfH * points[1]);
 30339  
 30340    for (var i = 1; i < points.length / 2; i++) {
 30341      context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
 30342    }
 30343  
 30344    context.closePath();
 30345  };
 30346  
 30347  CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
 30348    var halfW = width / 2;
 30349    var halfH = height / 2;
 30350    var cornerRadius = getRoundPolygonRadius(width, height);
 30351  
 30352    if (context.beginPath) {
 30353      context.beginPath();
 30354    }
 30355  
 30356    for (var _i = 0; _i < points.length / 4; _i++) {
 30357      var sourceUv = void 0,
 30358          destUv = void 0;
 30359  
 30360      if (_i === 0) {
 30361        sourceUv = points.length - 2;
 30362      } else {
 30363        sourceUv = _i * 4 - 2;
 30364      }
 30365  
 30366      destUv = _i * 4 + 2;
 30367      var px = x + halfW * points[_i * 4];
 30368      var py = y + halfH * points[_i * 4 + 1];
 30369      var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
 30370      var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
 30371      var cp0x = px - offset * points[sourceUv];
 30372      var cp0y = py - offset * points[sourceUv + 1];
 30373      var cp1x = px + offset * points[destUv];
 30374      var cp1y = py + offset * points[destUv + 1];
 30375  
 30376      if (_i === 0) {
 30377        context.moveTo(cp0x, cp0y);
 30378      } else {
 30379        context.lineTo(cp0x, cp0y);
 30380      }
 30381  
 30382      context.arcTo(px, py, cp1x, cp1y, cornerRadius);
 30383    }
 30384  
 30385    context.closePath();
 30386  }; // Round rectangle drawing
 30387  
 30388  
 30389  CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
 30390    var halfWidth = width / 2;
 30391    var halfHeight = height / 2;
 30392    var cornerRadius = getRoundRectangleRadius(width, height);
 30393  
 30394    if (context.beginPath) {
 30395      context.beginPath();
 30396    } // Start at top middle
 30397  
 30398  
 30399    context.moveTo(x, y - halfHeight); // Arc from middle top to right side
 30400  
 30401    context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
 30402  
 30403    context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
 30404  
 30405    context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
 30406  
 30407    context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
 30408  
 30409    context.lineTo(x, y - halfHeight);
 30410    context.closePath();
 30411  };
 30412  
 30413  CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
 30414    var halfWidth = width / 2;
 30415    var halfHeight = height / 2;
 30416    var cornerRadius = getRoundRectangleRadius(width, height);
 30417  
 30418    if (context.beginPath) {
 30419      context.beginPath();
 30420    } // Start at top middle
 30421  
 30422  
 30423    context.moveTo(x, y - halfHeight);
 30424    context.lineTo(x + halfWidth, y - halfHeight);
 30425    context.lineTo(x + halfWidth, y);
 30426    context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
 30427    context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
 30428    context.lineTo(x - halfWidth, y - halfHeight);
 30429    context.lineTo(x, y - halfHeight);
 30430    context.closePath();
 30431  };
 30432  
 30433  CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
 30434    var halfWidth = width / 2;
 30435    var halfHeight = height / 2;
 30436    var cornerLength = getCutRectangleCornerLength();
 30437  
 30438    if (context.beginPath) {
 30439      context.beginPath();
 30440    }
 30441  
 30442    context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
 30443    context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
 30444    context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
 30445    context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
 30446    context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
 30447    context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
 30448    context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
 30449    context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
 30450    context.closePath();
 30451  };
 30452  
 30453  CRp$7.drawBarrelPath = function (context, x, y, width, height) {
 30454    var halfWidth = width / 2;
 30455    var halfHeight = height / 2;
 30456    var xBegin = x - halfWidth;
 30457    var xEnd = x + halfWidth;
 30458    var yBegin = y - halfHeight;
 30459    var yEnd = y + halfHeight;
 30460    var barrelCurveConstants = getBarrelCurveConstants(width, height);
 30461    var wOffset = barrelCurveConstants.widthOffset;
 30462    var hOffset = barrelCurveConstants.heightOffset;
 30463    var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
 30464  
 30465    if (context.beginPath) {
 30466      context.beginPath();
 30467    }
 30468  
 30469    context.moveTo(xBegin, yBegin + hOffset);
 30470    context.lineTo(xBegin, yEnd - hOffset);
 30471    context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
 30472    context.lineTo(xEnd - wOffset, yEnd);
 30473    context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
 30474    context.lineTo(xEnd, yBegin + hOffset);
 30475    context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
 30476    context.lineTo(xBegin + wOffset, yBegin);
 30477    context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
 30478    context.closePath();
 30479  };
 30480  
 30481  var sin0 = Math.sin(0);
 30482  var cos0 = Math.cos(0);
 30483  var sin = {};
 30484  var cos = {};
 30485  var ellipseStepSize = Math.PI / 40;
 30486  
 30487  for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
 30488    sin[i] = Math.sin(i);
 30489    cos[i] = Math.cos(i);
 30490  }
 30491  
 30492  CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
 30493    if (context.beginPath) {
 30494      context.beginPath();
 30495    }
 30496  
 30497    if (context.ellipse) {
 30498      context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
 30499    } else {
 30500      var xPos, yPos;
 30501      var rw = width / 2;
 30502      var rh = height / 2;
 30503  
 30504      for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
 30505        xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
 30506        yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
 30507  
 30508        if (i === 0) {
 30509          context.moveTo(xPos, yPos);
 30510        } else {
 30511          context.lineTo(xPos, yPos);
 30512        }
 30513      }
 30514    }
 30515  
 30516    context.closePath();
 30517  };
 30518  
 30519  /* global atob, ArrayBuffer, Uint8Array, Blob */
 30520  var CRp$8 = {};
 30521  
 30522  CRp$8.createBuffer = function (w, h) {
 30523    var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
 30524  
 30525    buffer.width = w;
 30526    buffer.height = h;
 30527    return [buffer, buffer.getContext('2d')];
 30528  };
 30529  
 30530  CRp$8.bufferCanvasImage = function (options) {
 30531    var cy = this.cy;
 30532    var eles = cy.mutableElements();
 30533    var bb = eles.boundingBox();
 30534    var ctrRect = this.findContainerClientCoords();
 30535    var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
 30536    var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
 30537    var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
 30538    var pxRatio = this.getPixelRatio();
 30539    var scale = 1;
 30540  
 30541    if (options.scale !== undefined) {
 30542      width *= options.scale;
 30543      height *= options.scale;
 30544      scale = options.scale;
 30545    } else if (specdMaxDims) {
 30546      var maxScaleW = Infinity;
 30547      var maxScaleH = Infinity;
 30548  
 30549      if (number(options.maxWidth)) {
 30550        maxScaleW = scale * options.maxWidth / width;
 30551      }
 30552  
 30553      if (number(options.maxHeight)) {
 30554        maxScaleH = scale * options.maxHeight / height;
 30555      }
 30556  
 30557      scale = Math.min(maxScaleW, maxScaleH);
 30558      width *= scale;
 30559      height *= scale;
 30560    }
 30561  
 30562    if (!specdMaxDims) {
 30563      width *= pxRatio;
 30564      height *= pxRatio;
 30565      scale *= pxRatio;
 30566    }
 30567  
 30568    var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
 30569  
 30570    buffCanvas.width = width;
 30571    buffCanvas.height = height;
 30572    buffCanvas.style.width = width + 'px';
 30573    buffCanvas.style.height = height + 'px';
 30574    var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
 30575  
 30576    if (width > 0 && height > 0) {
 30577      buffCxt.clearRect(0, 0, width, height);
 30578      buffCxt.globalCompositeOperation = 'source-over';
 30579      var zsortedEles = this.getCachedZSortedEles();
 30580  
 30581      if (options.full) {
 30582        // draw the full bounds of the graph
 30583        buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
 30584        buffCxt.scale(scale, scale);
 30585        this.drawElements(buffCxt, zsortedEles);
 30586        buffCxt.scale(1 / scale, 1 / scale);
 30587        buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
 30588      } else {
 30589        // draw the current view
 30590        var pan = cy.pan();
 30591        var translation = {
 30592          x: pan.x * scale,
 30593          y: pan.y * scale
 30594        };
 30595        scale *= cy.zoom();
 30596        buffCxt.translate(translation.x, translation.y);
 30597        buffCxt.scale(scale, scale);
 30598        this.drawElements(buffCxt, zsortedEles);
 30599        buffCxt.scale(1 / scale, 1 / scale);
 30600        buffCxt.translate(-translation.x, -translation.y);
 30601      } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
 30602  
 30603  
 30604      if (options.bg) {
 30605        buffCxt.globalCompositeOperation = 'destination-over';
 30606        buffCxt.fillStyle = options.bg;
 30607        buffCxt.rect(0, 0, width, height);
 30608        buffCxt.fill();
 30609      }
 30610    }
 30611  
 30612    return buffCanvas;
 30613  };
 30614  
 30615  function b64ToBlob(b64, mimeType) {
 30616    var bytes = atob(b64);
 30617    var buff = new ArrayBuffer(bytes.length);
 30618    var buffUint8 = new Uint8Array(buff);
 30619  
 30620    for (var i = 0; i < bytes.length; i++) {
 30621      buffUint8[i] = bytes.charCodeAt(i);
 30622    }
 30623  
 30624    return new Blob([buff], {
 30625      type: mimeType
 30626    });
 30627  }
 30628  
 30629  function b64UriToB64(b64uri) {
 30630    var i = b64uri.indexOf(',');
 30631    return b64uri.substr(i + 1);
 30632  }
 30633  
 30634  function output(options, canvas, mimeType) {
 30635    var getB64Uri = function getB64Uri() {
 30636      return canvas.toDataURL(mimeType, options.quality);
 30637    };
 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  
 30655      case 'blob':
 30656        return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
 30657  
 30658      case 'base64':
 30659        return b64UriToB64(getB64Uri());
 30660  
 30661      case 'base64uri':
 30662      default:
 30663        return getB64Uri();
 30664    }
 30665  }
 30666  
 30667  CRp$8.png = function (options) {
 30668    return output(options, this.bufferCanvasImage(options), 'image/png');
 30669  };
 30670  
 30671  CRp$8.jpg = function (options) {
 30672    return output(options, this.bufferCanvasImage(options), 'image/jpeg');
 30673  };
 30674  
 30675  var CRp$9 = {};
 30676  
 30677  CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
 30678    switch (name) {
 30679      case 'ellipse':
 30680        return this.drawEllipsePath(context, centerX, centerY, width, height);
 30681  
 30682      case 'polygon':
 30683        return this.drawPolygonPath(context, centerX, centerY, width, height, points);
 30684  
 30685      case 'round-polygon':
 30686        return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
 30687  
 30688      case 'roundrectangle':
 30689      case 'round-rectangle':
 30690        return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
 30691  
 30692      case 'cutrectangle':
 30693      case 'cut-rectangle':
 30694        return this.drawCutRectanglePath(context, centerX, centerY, width, height);
 30695  
 30696      case 'bottomroundrectangle':
 30697      case 'bottom-round-rectangle':
 30698        return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
 30699  
 30700      case 'barrel':
 30701        return this.drawBarrelPath(context, centerX, centerY, width, height);
 30702    }
 30703  };
 30704  
 30705  var CR = CanvasRenderer;
 30706  var CRp$a = CanvasRenderer.prototype;
 30707  CRp$a.CANVAS_LAYERS = 3; //
 30708  
 30709  CRp$a.SELECT_BOX = 0;
 30710  CRp$a.DRAG = 1;
 30711  CRp$a.NODE = 2;
 30712  CRp$a.BUFFER_COUNT = 3; //
 30713  
 30714  CRp$a.TEXTURE_BUFFER = 0;
 30715  CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
 30716  CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
 30717  
 30718  function CanvasRenderer(options) {
 30719    var r = this;
 30720    r.data = {
 30721      canvases: new Array(CRp$a.CANVAS_LAYERS),
 30722      contexts: new Array(CRp$a.CANVAS_LAYERS),
 30723      canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
 30724      bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
 30725      bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
 30726    };
 30727    var tapHlOffAttr = '-webkit-tap-highlight-color';
 30728    var tapHlOffStyle = 'rgba(0,0,0,0)';
 30729    r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
 30730  
 30731    var containerStyle = r.data.canvasContainer.style;
 30732    r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
 30733    containerStyle.position = 'relative';
 30734    containerStyle.zIndex = '0';
 30735    containerStyle.overflow = 'hidden';
 30736    var container = options.cy.container();
 30737    container.appendChild(r.data.canvasContainer);
 30738    container.style[tapHlOffAttr] = tapHlOffStyle;
 30739    var styleMap = {
 30740      '-webkit-user-select': 'none',
 30741      '-moz-user-select': '-moz-none',
 30742      'user-select': 'none',
 30743      '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
 30744      'outline-style': 'none'
 30745    };
 30746  
 30747    if (ms()) {
 30748      styleMap['-ms-touch-action'] = 'none';
 30749      styleMap['touch-action'] = 'none';
 30750    }
 30751  
 30752    for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
 30753      var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
 30754  
 30755      r.data.contexts[i] = canvas.getContext('2d');
 30756      Object.keys(styleMap).forEach(function (k) {
 30757        canvas.style[k] = styleMap[k];
 30758      });
 30759      canvas.style.position = 'absolute';
 30760      canvas.setAttribute('data-id', 'layer' + i);
 30761      canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
 30762      r.data.canvasContainer.appendChild(canvas);
 30763      r.data.canvasNeedsRedraw[i] = false;
 30764    }
 30765  
 30766    r.data.topCanvas = r.data.canvases[0];
 30767    r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
 30768    r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
 30769    r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
 30770  
 30771    for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
 30772      r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
 30773  
 30774      r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
 30775      r.data.bufferCanvases[i].style.position = 'absolute';
 30776      r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
 30777      r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
 30778      r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
 30779    }
 30780  
 30781    r.pathsEnabled = true;
 30782    var emptyBb = makeBoundingBox();
 30783  
 30784    var getBoxCenter = function getBoxCenter(bb) {
 30785      return {
 30786        x: (bb.x1 + bb.x2) / 2,
 30787        y: (bb.y1 + bb.y2) / 2
 30788      };
 30789    };
 30790  
 30791    var getCenterOffset = function getCenterOffset(bb) {
 30792      return {
 30793        x: -bb.w / 2,
 30794        y: -bb.h / 2
 30795      };
 30796    };
 30797  
 30798    var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
 30799      var _p = ele[0]._private;
 30800      var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
 30801      return !same;
 30802    };
 30803  
 30804    var getStyleKey = function getStyleKey(ele) {
 30805      return ele[0]._private.nodeKey;
 30806    };
 30807  
 30808    var getLabelKey = function getLabelKey(ele) {
 30809      return ele[0]._private.labelStyleKey;
 30810    };
 30811  
 30812    var getSourceLabelKey = function getSourceLabelKey(ele) {
 30813      return ele[0]._private.sourceLabelStyleKey;
 30814    };
 30815  
 30816    var getTargetLabelKey = function getTargetLabelKey(ele) {
 30817      return ele[0]._private.targetLabelStyleKey;
 30818    };
 30819  
 30820    var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
 30821      return r.drawElement(context, ele, bb, false, false, useEleOpacity);
 30822    };
 30823  
 30824    var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
 30825      return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
 30826    };
 30827  
 30828    var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
 30829      return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
 30830    };
 30831  
 30832    var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
 30833      return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
 30834    };
 30835  
 30836    var getElementBox = function getElementBox(ele) {
 30837      ele.boundingBox();
 30838      return ele[0]._private.bodyBounds;
 30839    };
 30840  
 30841    var getLabelBox = function getLabelBox(ele) {
 30842      ele.boundingBox();
 30843      return ele[0]._private.labelBounds.main || emptyBb;
 30844    };
 30845  
 30846    var getSourceLabelBox = function getSourceLabelBox(ele) {
 30847      ele.boundingBox();
 30848      return ele[0]._private.labelBounds.source || emptyBb;
 30849    };
 30850  
 30851    var getTargetLabelBox = function getTargetLabelBox(ele) {
 30852      ele.boundingBox();
 30853      return ele[0]._private.labelBounds.target || emptyBb;
 30854    };
 30855  
 30856    var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
 30857      return scaledLabelShown;
 30858    };
 30859  
 30860    var getElementRotationPoint = function getElementRotationPoint(ele) {
 30861      return getBoxCenter(getElementBox(ele));
 30862    };
 30863  
 30864    var addTextMargin = function addTextMargin(prefix, pt, ele) {
 30865      var pre = prefix ? prefix + '-' : '';
 30866      return {
 30867        x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
 30868        y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
 30869      };
 30870    };
 30871  
 30872    var getRsPt = function getRsPt(ele, x, y) {
 30873      var rs = ele[0]._private.rscratch;
 30874      return {
 30875        x: rs[x],
 30876        y: rs[y]
 30877      };
 30878    };
 30879  
 30880    var getLabelRotationPoint = function getLabelRotationPoint(ele) {
 30881      return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
 30882    };
 30883  
 30884    var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
 30885      return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
 30886    };
 30887  
 30888    var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
 30889      return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
 30890    };
 30891  
 30892    var getElementRotationOffset = function getElementRotationOffset(ele) {
 30893      return getCenterOffset(getElementBox(ele));
 30894    };
 30895  
 30896    var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
 30897      return getCenterOffset(getSourceLabelBox(ele));
 30898    };
 30899  
 30900    var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
 30901      return getCenterOffset(getTargetLabelBox(ele));
 30902    };
 30903  
 30904    var getLabelRotationOffset = function getLabelRotationOffset(ele) {
 30905      var bb = getLabelBox(ele);
 30906      var p = getCenterOffset(getLabelBox(ele));
 30907  
 30908      if (ele.isNode()) {
 30909        switch (ele.pstyle('text-halign').value) {
 30910          case 'left':
 30911            p.x = -bb.w;
 30912            break;
 30913  
 30914          case 'right':
 30915            p.x = 0;
 30916            break;
 30917        }
 30918  
 30919        switch (ele.pstyle('text-valign').value) {
 30920          case 'top':
 30921            p.y = -bb.h;
 30922            break;
 30923  
 30924          case 'bottom':
 30925            p.y = 0;
 30926            break;
 30927        }
 30928      }
 30929  
 30930      return p;
 30931    };
 30932  
 30933    var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
 30934      getKey: getStyleKey,
 30935      doesEleInvalidateKey: backgroundTimestampHasChanged,
 30936      drawElement: drawElement,
 30937      getBoundingBox: getElementBox,
 30938      getRotationPoint: getElementRotationPoint,
 30939      getRotationOffset: getElementRotationOffset,
 30940      allowEdgeTxrCaching: false,
 30941      allowParentTxrCaching: false
 30942    });
 30943    var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
 30944      getKey: getLabelKey,
 30945      drawElement: drawLabel,
 30946      getBoundingBox: getLabelBox,
 30947      getRotationPoint: getLabelRotationPoint,
 30948      getRotationOffset: getLabelRotationOffset,
 30949      isVisible: isLabelVisibleAtScale
 30950    });
 30951    var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
 30952      getKey: getSourceLabelKey,
 30953      drawElement: drawSourceLabel,
 30954      getBoundingBox: getSourceLabelBox,
 30955      getRotationPoint: getSourceLabelRotationPoint,
 30956      getRotationOffset: getSourceLabelRotationOffset,
 30957      isVisible: isLabelVisibleAtScale
 30958    });
 30959    var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
 30960      getKey: getTargetLabelKey,
 30961      drawElement: drawTargetLabel,
 30962      getBoundingBox: getTargetLabelBox,
 30963      getRotationPoint: getTargetLabelRotationPoint,
 30964      getRotationOffset: getTargetLabelRotationOffset,
 30965      isVisible: isLabelVisibleAtScale
 30966    });
 30967    var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
 30968    r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
 30969      // each cache should check for sub-key diff to see that the update affects that cache particularly
 30970      eleTxrCache.invalidateElements(eles);
 30971      lblTxrCache.invalidateElements(eles);
 30972      slbTxrCache.invalidateElements(eles);
 30973      tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
 30974  
 30975      lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
 30976  
 30977      for (var _i = 0; _i < eles.length; _i++) {
 30978        var _p = eles[_i]._private;
 30979        _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
 30980      }
 30981    });
 30982  
 30983    var refineInLayers = function refineInLayers(reqs) {
 30984      for (var i = 0; i < reqs.length; i++) {
 30985        lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
 30986      }
 30987    };
 30988  
 30989    eleTxrCache.onDequeue(refineInLayers);
 30990    lblTxrCache.onDequeue(refineInLayers);
 30991    slbTxrCache.onDequeue(refineInLayers);
 30992    tlbTxrCache.onDequeue(refineInLayers);
 30993  }
 30994  
 30995  CRp$a.redrawHint = function (group, bool) {
 30996    var r = this;
 30997  
 30998    switch (group) {
 30999      case 'eles':
 31000        r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
 31001        break;
 31002  
 31003      case 'drag':
 31004        r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
 31005        break;
 31006  
 31007      case 'select':
 31008        r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
 31009        break;
 31010    }
 31011  }; // whether to use Path2D caching for drawing
 31012  
 31013  
 31014  var pathsImpld = typeof Path2D !== 'undefined';
 31015  
 31016  CRp$a.path2dEnabled = function (on) {
 31017    if (on === undefined) {
 31018      return this.pathsEnabled;
 31019    }
 31020  
 31021    this.pathsEnabled = on ? true : false;
 31022  };
 31023  
 31024  CRp$a.usePaths = function () {
 31025    return pathsImpld && this.pathsEnabled;
 31026  };
 31027  
 31028  CRp$a.setImgSmoothing = function (context, bool) {
 31029    if (context.imageSmoothingEnabled != null) {
 31030      context.imageSmoothingEnabled = bool;
 31031    } else {
 31032      context.webkitImageSmoothingEnabled = bool;
 31033      context.mozImageSmoothingEnabled = bool;
 31034      context.msImageSmoothingEnabled = bool;
 31035    }
 31036  };
 31037  
 31038  CRp$a.getImgSmoothing = function (context) {
 31039    if (context.imageSmoothingEnabled != null) {
 31040      return context.imageSmoothingEnabled;
 31041    } else {
 31042      return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
 31043    }
 31044  };
 31045  
 31046  CRp$a.makeOffscreenCanvas = function (width, height) {
 31047    var canvas;
 31048  
 31049    if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
 31050      canvas = new OffscreenCanvas(width, height);
 31051    } else {
 31052      canvas = document.createElement('canvas'); // eslint-disable-line no-undef
 31053  
 31054      canvas.width = width;
 31055      canvas.height = height;
 31056    }
 31057  
 31058    return canvas;
 31059  };
 31060  
 31061  [CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
 31062    extend(CRp$a, props);
 31063  });
 31064  
 31065  var renderer = [{
 31066    name: 'null',
 31067    impl: NullRenderer
 31068  }, {
 31069    name: 'base',
 31070    impl: BR
 31071  }, {
 31072    name: 'canvas',
 31073    impl: CR
 31074  }];
 31075  
 31076  var incExts = [{
 31077    type: 'layout',
 31078    extensions: layout
 31079  }, {
 31080    type: 'renderer',
 31081    extensions: renderer
 31082  }];
 31083  
 31084  var extensions = {}; // registered modules for extensions, indexed by name
 31085  
 31086  var modules = {};
 31087  
 31088  function setExtension(type, name, registrant) {
 31089    var ext = registrant;
 31090  
 31091    var overrideErr = function overrideErr(field) {
 31092      error('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
 31093    };
 31094  
 31095    if (type === 'core') {
 31096      if (Core.prototype[name]) {
 31097        return overrideErr(name);
 31098      } else {
 31099        Core.prototype[name] = registrant;
 31100      }
 31101    } else if (type === 'collection') {
 31102      if (Collection.prototype[name]) {
 31103        return overrideErr(name);
 31104      } else {
 31105        Collection.prototype[name] = registrant;
 31106      }
 31107    } else if (type === 'layout') {
 31108      // fill in missing layout functions in the prototype
 31109      var Layout = function Layout(options) {
 31110        this.options = options;
 31111        registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
 31112  
 31113        if (!plainObject(this._private)) {
 31114          this._private = {};
 31115        }
 31116  
 31117        this._private.cy = options.cy;
 31118        this._private.listeners = [];
 31119        this.createEmitter();
 31120      };
 31121  
 31122      var layoutProto = Layout.prototype = Object.create(registrant.prototype);
 31123      var optLayoutFns = [];
 31124  
 31125      for (var i = 0; i < optLayoutFns.length; i++) {
 31126        var fnName = optLayoutFns[i];
 31127  
 31128        layoutProto[fnName] = layoutProto[fnName] || function () {
 31129          return this;
 31130        };
 31131      } // either .start() or .run() is defined, so autogen the other
 31132  
 31133  
 31134      if (layoutProto.start && !layoutProto.run) {
 31135        layoutProto.run = function () {
 31136          this.start();
 31137          return this;
 31138        };
 31139      } else if (!layoutProto.start && layoutProto.run) {
 31140        layoutProto.start = function () {
 31141          this.run();
 31142          return this;
 31143        };
 31144      }
 31145  
 31146      var regStop = registrant.prototype.stop;
 31147  
 31148      layoutProto.stop = function () {
 31149        var opts = this.options;
 31150  
 31151        if (opts && opts.animate) {
 31152          var anis = this.animations;
 31153  
 31154          if (anis) {
 31155            for (var _i = 0; _i < anis.length; _i++) {
 31156              anis[_i].stop();
 31157            }
 31158          }
 31159        }
 31160  
 31161        if (regStop) {
 31162          regStop.call(this);
 31163        } else {
 31164          this.emit('layoutstop');
 31165        }
 31166  
 31167        return this;
 31168      };
 31169  
 31170      if (!layoutProto.destroy) {
 31171        layoutProto.destroy = function () {
 31172          return this;
 31173        };
 31174      }
 31175  
 31176      layoutProto.cy = function () {
 31177        return this._private.cy;
 31178      };
 31179  
 31180      var getCy = function getCy(layout) {
 31181        return layout._private.cy;
 31182      };
 31183  
 31184      var emitterOpts = {
 31185        addEventFields: function addEventFields(layout, evt) {
 31186          evt.layout = layout;
 31187          evt.cy = getCy(layout);
 31188          evt.target = layout;
 31189        },
 31190        bubble: function bubble() {
 31191          return true;
 31192        },
 31193        parent: function parent(layout) {
 31194          return getCy(layout);
 31195        }
 31196      };
 31197      extend(layoutProto, {
 31198        createEmitter: function createEmitter() {
 31199          this._private.emitter = new Emitter(emitterOpts, this);
 31200          return this;
 31201        },
 31202        emitter: function emitter() {
 31203          return this._private.emitter;
 31204        },
 31205        on: function on(evt, cb) {
 31206          this.emitter().on(evt, cb);
 31207          return this;
 31208        },
 31209        one: function one(evt, cb) {
 31210          this.emitter().one(evt, cb);
 31211          return this;
 31212        },
 31213        once: function once(evt, cb) {
 31214          this.emitter().one(evt, cb);
 31215          return this;
 31216        },
 31217        removeListener: function removeListener(evt, cb) {
 31218          this.emitter().removeListener(evt, cb);
 31219          return this;
 31220        },
 31221        removeAllListeners: function removeAllListeners() {
 31222          this.emitter().removeAllListeners();
 31223          return this;
 31224        },
 31225        emit: function emit(evt, params) {
 31226          this.emitter().emit(evt, params);
 31227          return this;
 31228        }
 31229      });
 31230      define$3.eventAliasesOn(layoutProto);
 31231      ext = Layout; // replace with our wrapped layout
 31232    } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
 31233      // user registered renderers inherit from base
 31234      var BaseRenderer = getExtension('renderer', 'base');
 31235      var bProto = BaseRenderer.prototype;
 31236      var RegistrantRenderer = registrant;
 31237      var rProto = registrant.prototype;
 31238  
 31239      var Renderer = function Renderer() {
 31240        BaseRenderer.apply(this, arguments);
 31241        RegistrantRenderer.apply(this, arguments);
 31242      };
 31243  
 31244      var proto = Renderer.prototype;
 31245  
 31246      for (var pName in bProto) {
 31247        var pVal = bProto[pName];
 31248        var existsInR = rProto[pName] != null;
 31249  
 31250        if (existsInR) {
 31251          return overrideErr(pName);
 31252        }
 31253  
 31254        proto[pName] = pVal; // take impl from base
 31255      }
 31256  
 31257      for (var _pName in rProto) {
 31258        proto[_pName] = rProto[_pName]; // take impl from registrant
 31259      }
 31260  
 31261      bProto.clientFunctions.forEach(function (name) {
 31262        proto[name] = proto[name] || function () {
 31263          error('Renderer does not implement `renderer.' + name + '()` on its prototype');
 31264        };
 31265      });
 31266      ext = Renderer;
 31267    }
 31268  
 31269    return setMap({
 31270      map: extensions,
 31271      keys: [type, name],
 31272      value: ext
 31273    });
 31274  }
 31275  
 31276  function getExtension(type, name) {
 31277    return getMap({
 31278      map: extensions,
 31279      keys: [type, name]
 31280    });
 31281  }
 31282  
 31283  function setModule(type, name, moduleType, moduleName, registrant) {
 31284    return setMap({
 31285      map: modules,
 31286      keys: [type, name, moduleType, moduleName],
 31287      value: registrant
 31288    });
 31289  }
 31290  
 31291  function getModule(type, name, moduleType, moduleName) {
 31292    return getMap({
 31293      map: modules,
 31294      keys: [type, name, moduleType, moduleName]
 31295    });
 31296  }
 31297  
 31298  var extension = function extension() {
 31299    // e.g. extension('renderer', 'svg')
 31300    if (arguments.length === 2) {
 31301      return getExtension.apply(null, arguments);
 31302    } // e.g. extension('renderer', 'svg', { ... })
 31303    else if (arguments.length === 3) {
 31304        return setExtension.apply(null, arguments);
 31305      } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
 31306      else if (arguments.length === 4) {
 31307          return getModule.apply(null, arguments);
 31308        } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
 31309        else if (arguments.length === 5) {
 31310            return setModule.apply(null, arguments);
 31311          } else {
 31312            error('Invalid extension access syntax');
 31313          }
 31314  }; // allows a core instance to access extensions internally
 31315  
 31316  
 31317  Core.prototype.extension = extension; // included extensions
 31318  
 31319  incExts.forEach(function (group) {
 31320    group.extensions.forEach(function (ext) {
 31321      setExtension(group.type, ext.name, ext.impl);
 31322    });
 31323  });
 31324  
 31325  // (useful for init)
 31326  
 31327  var Stylesheet = function Stylesheet() {
 31328    if (!(this instanceof Stylesheet)) {
 31329      return new Stylesheet();
 31330    }
 31331  
 31332    this.length = 0;
 31333  };
 31334  
 31335  var sheetfn = Stylesheet.prototype;
 31336  
 31337  sheetfn.instanceString = function () {
 31338    return 'stylesheet';
 31339  }; // just store the selector to be parsed later
 31340  
 31341  
 31342  sheetfn.selector = function (selector) {
 31343    var i = this.length++;
 31344    this[i] = {
 31345      selector: selector,
 31346      properties: []
 31347    };
 31348    return this; // chaining
 31349  }; // just store the property to be parsed later
 31350  
 31351  
 31352  sheetfn.css = function (name, value) {
 31353    var i = this.length - 1;
 31354  
 31355    if (string(name)) {
 31356      this[i].properties.push({
 31357        name: name,
 31358        value: value
 31359      });
 31360    } else if (plainObject(name)) {
 31361      var map = name;
 31362      var propNames = Object.keys(map);
 31363  
 31364      for (var j = 0; j < propNames.length; j++) {
 31365        var key = propNames[j];
 31366        var mapVal = map[key];
 31367  
 31368        if (mapVal == null) {
 31369          continue;
 31370        }
 31371  
 31372        var prop = Style.properties[key] || Style.properties[dash2camel(key)];
 31373  
 31374        if (prop == null) {
 31375          continue;
 31376        }
 31377  
 31378        var _name = prop.name;
 31379        var _value = mapVal;
 31380        this[i].properties.push({
 31381          name: _name,
 31382          value: _value
 31383        });
 31384      }
 31385    }
 31386  
 31387    return this; // chaining
 31388  };
 31389  
 31390  sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
 31391  
 31392  sheetfn.generateStyle = function (cy) {
 31393    var style = new Style(cy);
 31394    return this.appendToStyle(style);
 31395  }; // append a dummy stylesheet object on a real style object
 31396  
 31397  
 31398  sheetfn.appendToStyle = function (style) {
 31399    for (var i = 0; i < this.length; i++) {
 31400      var context = this[i];
 31401      var selector = context.selector;
 31402      var props = context.properties;
 31403      style.selector(selector); // apply selector
 31404  
 31405      for (var j = 0; j < props.length; j++) {
 31406        var prop = props[j];
 31407        style.css(prop.name, prop.value); // apply property
 31408      }
 31409    }
 31410  
 31411    return style;
 31412  };
 31413  
 31414  var version = "3.13.3";
 31415  
 31416  var cytoscape = function cytoscape(options) {
 31417    // if no options specified, use default
 31418    if (options === undefined) {
 31419      options = {};
 31420    } // create instance
 31421  
 31422  
 31423    if (plainObject(options)) {
 31424      return new Core(options);
 31425    } // allow for registration of extensions
 31426    else if (string(options)) {
 31427        return extension.apply(extension, arguments);
 31428      }
 31429  }; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
 31430  
 31431  
 31432  cytoscape.use = function (ext) {
 31433    var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
 31434  
 31435    args.unshift(cytoscape); // cytoscape is first arg to ext
 31436  
 31437    ext.apply(null, args);
 31438    return this;
 31439  };
 31440  
 31441  cytoscape.warnings = function (bool) {
 31442    return warnings(bool);
 31443  }; // replaced by build system
 31444  
 31445  
 31446  cytoscape.version = version; // expose public apis (mostly for extensions)
 31447  
 31448  cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
 31449  
 31450  module.exports = cytoscape;