github.com/mweagle/Sparta@v1.15.0/resources/describe/cytoscape.js/dist/cytoscape.esm.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  import util from 'lodash.debounce';
    24  import Heap from 'heap';
    25  
    26  function _typeof(obj) {
    27    if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
    28      _typeof = function (obj) {
    29        return typeof obj;
    30      };
    31    } else {
    32      _typeof = function (obj) {
    33        return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
    34      };
    35    }
    36  
    37    return _typeof(obj);
    38  }
    39  
    40  function _classCallCheck(instance, Constructor) {
    41    if (!(instance instanceof Constructor)) {
    42      throw new TypeError("Cannot call a class as a function");
    43    }
    44  }
    45  
    46  function _defineProperties(target, props) {
    47    for (var i = 0; i < props.length; i++) {
    48      var descriptor = props[i];
    49      descriptor.enumerable = descriptor.enumerable || false;
    50      descriptor.configurable = true;
    51      if ("value" in descriptor) descriptor.writable = true;
    52      Object.defineProperty(target, descriptor.key, descriptor);
    53    }
    54  }
    55  
    56  function _createClass(Constructor, protoProps, staticProps) {
    57    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    58    if (staticProps) _defineProperties(Constructor, staticProps);
    59    return Constructor;
    60  }
    61  
    62  function _defineProperty(obj, key, value) {
    63    if (key in obj) {
    64      Object.defineProperty(obj, key, {
    65        value: value,
    66        enumerable: true,
    67        configurable: true,
    68        writable: true
    69      });
    70    } else {
    71      obj[key] = value;
    72    }
    73  
    74    return obj;
    75  }
    76  
    77  function _slicedToArray(arr, i) {
    78    return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
    79  }
    80  
    81  function _arrayWithHoles(arr) {
    82    if (Array.isArray(arr)) return arr;
    83  }
    84  
    85  function _iterableToArrayLimit(arr, i) {
    86    var _arr = [];
    87    var _n = true;
    88    var _d = false;
    89    var _e = undefined;
    90  
    91    try {
    92      for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
    93        _arr.push(_s.value);
    94  
    95        if (i && _arr.length === i) break;
    96      }
    97    } catch (err) {
    98      _d = true;
    99      _e = err;
   100    } finally {
   101      try {
   102        if (!_n && _i["return"] != null) _i["return"]();
   103      } finally {
   104        if (_d) throw _e;
   105      }
   106    }
   107  
   108    return _arr;
   109  }
   110  
   111  function _nonIterableRest() {
   112    throw new TypeError("Invalid attempt to destructure non-iterable instance");
   113  }
   114  
   115  var window$1 = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
   116  
   117  var navigator = window$1 ? window$1.navigator : null;
   118  var document$1 = window$1 ? window$1.document : null;
   119  
   120  var typeofstr = _typeof('');
   121  
   122  var typeofobj = _typeof({});
   123  
   124  var typeoffn = _typeof(function () {});
   125  
   126  var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
   127  
   128  var instanceStr = function instanceStr(obj) {
   129    return obj && obj.instanceString && fn(obj.instanceString) ? obj.instanceString() : null;
   130  };
   131  
   132  var string = function string(obj) {
   133    return obj != null && _typeof(obj) == typeofstr;
   134  };
   135  var fn = function fn(obj) {
   136    return obj != null && _typeof(obj) === typeoffn;
   137  };
   138  var array = function array(obj) {
   139    return Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array;
   140  };
   141  var plainObject = function plainObject(obj) {
   142    return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
   143  };
   144  var object = function object(obj) {
   145    return obj != null && _typeof(obj) === typeofobj;
   146  };
   147  var number = function number(obj) {
   148    return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
   149  };
   150  var integer = function integer(obj) {
   151    return number(obj) && Math.floor(obj) === obj;
   152  };
   153  var htmlElement = function htmlElement(obj) {
   154    if ('undefined' === typeofhtmlele) {
   155      return undefined;
   156    } else {
   157      return null != obj && obj instanceof HTMLElement;
   158    }
   159  };
   160  var elementOrCollection = function elementOrCollection(obj) {
   161    return element(obj) || collection(obj);
   162  };
   163  var element = function element(obj) {
   164    return instanceStr(obj) === 'collection' && obj._private.single;
   165  };
   166  var collection = function collection(obj) {
   167    return instanceStr(obj) === 'collection' && !obj._private.single;
   168  };
   169  var core = function core(obj) {
   170    return instanceStr(obj) === 'core';
   171  };
   172  var stylesheet = function stylesheet(obj) {
   173    return instanceStr(obj) === 'stylesheet';
   174  };
   175  var event = function event(obj) {
   176    return instanceStr(obj) === 'event';
   177  };
   178  var emptyString = function emptyString(obj) {
   179    if (obj === undefined || obj === null) {
   180      // null is empty
   181      return true;
   182    } else if (obj === '' || obj.match(/^\s+$/)) {
   183      return true; // empty string is empty
   184    }
   185  
   186    return false; // otherwise, we don't know what we've got
   187  };
   188  var domElement = function domElement(obj) {
   189    if (typeof HTMLElement === 'undefined') {
   190      return false; // we're not in a browser so it doesn't matter
   191    } else {
   192      return obj instanceof HTMLElement;
   193    }
   194  };
   195  var boundingBox = function boundingBox(obj) {
   196    return plainObject(obj) && number(obj.x1) && number(obj.x2) && number(obj.y1) && number(obj.y2);
   197  };
   198  var promise = function promise(obj) {
   199    return object(obj) && fn(obj.then);
   200  };
   201  var ms = function ms() {
   202    return navigator && navigator.userAgent.match(/msie|trident|edge/i);
   203  }; // probably a better way to detect this...
   204  
   205  var memoize = function memoize(fn, keyFn) {
   206    if (!keyFn) {
   207      keyFn = function keyFn() {
   208        if (arguments.length === 1) {
   209          return arguments[0];
   210        } else if (arguments.length === 0) {
   211          return 'undefined';
   212        }
   213  
   214        var args = [];
   215  
   216        for (var i = 0; i < arguments.length; i++) {
   217          args.push(arguments[i]);
   218        }
   219  
   220        return args.join('$');
   221      };
   222    }
   223  
   224    var memoizedFn = function memoizedFn() {
   225      var self = this;
   226      var args = arguments;
   227      var ret;
   228      var k = keyFn.apply(self, args);
   229      var cache = memoizedFn.cache;
   230  
   231      if (!(ret = cache[k])) {
   232        ret = cache[k] = fn.apply(self, args);
   233      }
   234  
   235      return ret;
   236    };
   237  
   238    memoizedFn.cache = {};
   239    return memoizedFn;
   240  };
   241  
   242  var camel2dash = memoize(function (str) {
   243    return str.replace(/([A-Z])/g, function (v) {
   244      return '-' + v.toLowerCase();
   245    });
   246  });
   247  var dash2camel = memoize(function (str) {
   248    return str.replace(/(-\w)/g, function (v) {
   249      return v[1].toUpperCase();
   250    });
   251  });
   252  var prependCamel = memoize(function (prefix, str) {
   253    return prefix + str[0].toUpperCase() + str.substring(1);
   254  }, function (prefix, str) {
   255    return prefix + '$' + str;
   256  });
   257  var capitalize = function capitalize(str) {
   258    if (emptyString(str)) {
   259      return str;
   260    }
   261  
   262    return str.charAt(0).toUpperCase() + str.substring(1);
   263  };
   264  
   265  var number$1 = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
   266  var rgba = 'rgb[a]?\\((' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)(?:\\s*,\\s*(' + number$1 + '))?\\)';
   267  var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
   268  var hsla = 'hsl[a]?\\((' + number$1 + ')\\s*,\\s*(' + number$1 + '[%])\\s*,\\s*(' + number$1 + '[%])(?:\\s*,\\s*(' + number$1 + '))?\\)';
   269  var hslaNoBackRefs = 'hsl[a]?\\((?:' + number$1 + ')\\s*,\\s*(?:' + number$1 + '[%])\\s*,\\s*(?:' + number$1 + '[%])(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
   270  var hex3 = '\\#[0-9a-fA-F]{3}';
   271  var hex6 = '\\#[0-9a-fA-F]{6}';
   272  
   273  var ascending = function ascending(a, b) {
   274    if (a < b) {
   275      return -1;
   276    } else if (a > b) {
   277      return 1;
   278    } else {
   279      return 0;
   280    }
   281  };
   282  var descending = function descending(a, b) {
   283    return -1 * ascending(a, b);
   284  };
   285  
   286  var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
   287    var args = arguments;
   288  
   289    for (var i = 1; i < args.length; i++) {
   290      var obj = args[i];
   291  
   292      if (obj == null) {
   293        continue;
   294      }
   295  
   296      var keys = Object.keys(obj);
   297  
   298      for (var j = 0; j < keys.length; j++) {
   299        var k = keys[j];
   300        tgt[k] = obj[k];
   301      }
   302    }
   303  
   304    return tgt;
   305  };
   306  
   307  var hex2tuple = function hex2tuple(hex) {
   308    if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
   309      return;
   310    }
   311  
   312    var shortHex = hex.length === 4;
   313    var r, g, b;
   314    var base = 16;
   315  
   316    if (shortHex) {
   317      r = parseInt(hex[1] + hex[1], base);
   318      g = parseInt(hex[2] + hex[2], base);
   319      b = parseInt(hex[3] + hex[3], base);
   320    } else {
   321      r = parseInt(hex[1] + hex[2], base);
   322      g = parseInt(hex[3] + hex[4], base);
   323      b = parseInt(hex[5] + hex[6], base);
   324    }
   325  
   326    return [r, g, b];
   327  }; // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
   328  
   329  var hsl2tuple = function hsl2tuple(hsl) {
   330    var ret;
   331    var h, s, l, a, r, g, b;
   332  
   333    function hue2rgb(p, q, t) {
   334      if (t < 0) t += 1;
   335      if (t > 1) t -= 1;
   336      if (t < 1 / 6) return p + (q - p) * 6 * t;
   337      if (t < 1 / 2) return q;
   338      if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
   339      return p;
   340    }
   341  
   342    var m = new RegExp('^' + hsla + '$').exec(hsl);
   343  
   344    if (m) {
   345      // get hue
   346      h = parseInt(m[1]);
   347  
   348      if (h < 0) {
   349        h = (360 - -1 * h % 360) % 360;
   350      } else if (h > 360) {
   351        h = h % 360;
   352      }
   353  
   354      h /= 360; // normalise on [0, 1]
   355  
   356      s = parseFloat(m[2]);
   357  
   358      if (s < 0 || s > 100) {
   359        return;
   360      } // saturation is [0, 100]
   361  
   362  
   363      s = s / 100; // normalise on [0, 1]
   364  
   365      l = parseFloat(m[3]);
   366  
   367      if (l < 0 || l > 100) {
   368        return;
   369      } // lightness is [0, 100]
   370  
   371  
   372      l = l / 100; // normalise on [0, 1]
   373  
   374      a = m[4];
   375  
   376      if (a !== undefined) {
   377        a = parseFloat(a);
   378  
   379        if (a < 0 || a > 1) {
   380          return;
   381        } // alpha is [0, 1]
   382  
   383      } // now, convert to rgb
   384      // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
   385  
   386  
   387      if (s === 0) {
   388        r = g = b = Math.round(l * 255); // achromatic
   389      } else {
   390        var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
   391        var p = 2 * l - q;
   392        r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
   393        g = Math.round(255 * hue2rgb(p, q, h));
   394        b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
   395      }
   396  
   397      ret = [r, g, b, a];
   398    }
   399  
   400    return ret;
   401  }; // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
   402  
   403  var rgb2tuple = function rgb2tuple(rgb) {
   404    var ret;
   405    var m = new RegExp('^' + rgba + '$').exec(rgb);
   406  
   407    if (m) {
   408      ret = [];
   409      var isPct = [];
   410  
   411      for (var i = 1; i <= 3; i++) {
   412        var channel = m[i];
   413  
   414        if (channel[channel.length - 1] === '%') {
   415          isPct[i] = true;
   416        }
   417  
   418        channel = parseFloat(channel);
   419  
   420        if (isPct[i]) {
   421          channel = channel / 100 * 255; // normalise to [0, 255]
   422        }
   423  
   424        if (channel < 0 || channel > 255) {
   425          return;
   426        } // invalid channel value
   427  
   428  
   429        ret.push(Math.floor(channel));
   430      }
   431  
   432      var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
   433      var allArePct = isPct[1] && isPct[2] && isPct[3];
   434  
   435      if (atLeastOneIsPct && !allArePct) {
   436        return;
   437      } // must all be percent values if one is
   438  
   439  
   440      var alpha = m[4];
   441  
   442      if (alpha !== undefined) {
   443        alpha = parseFloat(alpha);
   444  
   445        if (alpha < 0 || alpha > 1) {
   446          return;
   447        } // invalid alpha value
   448  
   449  
   450        ret.push(alpha);
   451      }
   452    }
   453  
   454    return ret;
   455  };
   456  var colorname2tuple = function colorname2tuple(color) {
   457    return colors[color.toLowerCase()];
   458  };
   459  var color2tuple = function color2tuple(color) {
   460    return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
   461  };
   462  var colors = {
   463    // special colour names
   464    transparent: [0, 0, 0, 0],
   465    // NB alpha === 0
   466    // regular colours
   467    aliceblue: [240, 248, 255],
   468    antiquewhite: [250, 235, 215],
   469    aqua: [0, 255, 255],
   470    aquamarine: [127, 255, 212],
   471    azure: [240, 255, 255],
   472    beige: [245, 245, 220],
   473    bisque: [255, 228, 196],
   474    black: [0, 0, 0],
   475    blanchedalmond: [255, 235, 205],
   476    blue: [0, 0, 255],
   477    blueviolet: [138, 43, 226],
   478    brown: [165, 42, 42],
   479    burlywood: [222, 184, 135],
   480    cadetblue: [95, 158, 160],
   481    chartreuse: [127, 255, 0],
   482    chocolate: [210, 105, 30],
   483    coral: [255, 127, 80],
   484    cornflowerblue: [100, 149, 237],
   485    cornsilk: [255, 248, 220],
   486    crimson: [220, 20, 60],
   487    cyan: [0, 255, 255],
   488    darkblue: [0, 0, 139],
   489    darkcyan: [0, 139, 139],
   490    darkgoldenrod: [184, 134, 11],
   491    darkgray: [169, 169, 169],
   492    darkgreen: [0, 100, 0],
   493    darkgrey: [169, 169, 169],
   494    darkkhaki: [189, 183, 107],
   495    darkmagenta: [139, 0, 139],
   496    darkolivegreen: [85, 107, 47],
   497    darkorange: [255, 140, 0],
   498    darkorchid: [153, 50, 204],
   499    darkred: [139, 0, 0],
   500    darksalmon: [233, 150, 122],
   501    darkseagreen: [143, 188, 143],
   502    darkslateblue: [72, 61, 139],
   503    darkslategray: [47, 79, 79],
   504    darkslategrey: [47, 79, 79],
   505    darkturquoise: [0, 206, 209],
   506    darkviolet: [148, 0, 211],
   507    deeppink: [255, 20, 147],
   508    deepskyblue: [0, 191, 255],
   509    dimgray: [105, 105, 105],
   510    dimgrey: [105, 105, 105],
   511    dodgerblue: [30, 144, 255],
   512    firebrick: [178, 34, 34],
   513    floralwhite: [255, 250, 240],
   514    forestgreen: [34, 139, 34],
   515    fuchsia: [255, 0, 255],
   516    gainsboro: [220, 220, 220],
   517    ghostwhite: [248, 248, 255],
   518    gold: [255, 215, 0],
   519    goldenrod: [218, 165, 32],
   520    gray: [128, 128, 128],
   521    grey: [128, 128, 128],
   522    green: [0, 128, 0],
   523    greenyellow: [173, 255, 47],
   524    honeydew: [240, 255, 240],
   525    hotpink: [255, 105, 180],
   526    indianred: [205, 92, 92],
   527    indigo: [75, 0, 130],
   528    ivory: [255, 255, 240],
   529    khaki: [240, 230, 140],
   530    lavender: [230, 230, 250],
   531    lavenderblush: [255, 240, 245],
   532    lawngreen: [124, 252, 0],
   533    lemonchiffon: [255, 250, 205],
   534    lightblue: [173, 216, 230],
   535    lightcoral: [240, 128, 128],
   536    lightcyan: [224, 255, 255],
   537    lightgoldenrodyellow: [250, 250, 210],
   538    lightgray: [211, 211, 211],
   539    lightgreen: [144, 238, 144],
   540    lightgrey: [211, 211, 211],
   541    lightpink: [255, 182, 193],
   542    lightsalmon: [255, 160, 122],
   543    lightseagreen: [32, 178, 170],
   544    lightskyblue: [135, 206, 250],
   545    lightslategray: [119, 136, 153],
   546    lightslategrey: [119, 136, 153],
   547    lightsteelblue: [176, 196, 222],
   548    lightyellow: [255, 255, 224],
   549    lime: [0, 255, 0],
   550    limegreen: [50, 205, 50],
   551    linen: [250, 240, 230],
   552    magenta: [255, 0, 255],
   553    maroon: [128, 0, 0],
   554    mediumaquamarine: [102, 205, 170],
   555    mediumblue: [0, 0, 205],
   556    mediumorchid: [186, 85, 211],
   557    mediumpurple: [147, 112, 219],
   558    mediumseagreen: [60, 179, 113],
   559    mediumslateblue: [123, 104, 238],
   560    mediumspringgreen: [0, 250, 154],
   561    mediumturquoise: [72, 209, 204],
   562    mediumvioletred: [199, 21, 133],
   563    midnightblue: [25, 25, 112],
   564    mintcream: [245, 255, 250],
   565    mistyrose: [255, 228, 225],
   566    moccasin: [255, 228, 181],
   567    navajowhite: [255, 222, 173],
   568    navy: [0, 0, 128],
   569    oldlace: [253, 245, 230],
   570    olive: [128, 128, 0],
   571    olivedrab: [107, 142, 35],
   572    orange: [255, 165, 0],
   573    orangered: [255, 69, 0],
   574    orchid: [218, 112, 214],
   575    palegoldenrod: [238, 232, 170],
   576    palegreen: [152, 251, 152],
   577    paleturquoise: [175, 238, 238],
   578    palevioletred: [219, 112, 147],
   579    papayawhip: [255, 239, 213],
   580    peachpuff: [255, 218, 185],
   581    peru: [205, 133, 63],
   582    pink: [255, 192, 203],
   583    plum: [221, 160, 221],
   584    powderblue: [176, 224, 230],
   585    purple: [128, 0, 128],
   586    red: [255, 0, 0],
   587    rosybrown: [188, 143, 143],
   588    royalblue: [65, 105, 225],
   589    saddlebrown: [139, 69, 19],
   590    salmon: [250, 128, 114],
   591    sandybrown: [244, 164, 96],
   592    seagreen: [46, 139, 87],
   593    seashell: [255, 245, 238],
   594    sienna: [160, 82, 45],
   595    silver: [192, 192, 192],
   596    skyblue: [135, 206, 235],
   597    slateblue: [106, 90, 205],
   598    slategray: [112, 128, 144],
   599    slategrey: [112, 128, 144],
   600    snow: [255, 250, 250],
   601    springgreen: [0, 255, 127],
   602    steelblue: [70, 130, 180],
   603    tan: [210, 180, 140],
   604    teal: [0, 128, 128],
   605    thistle: [216, 191, 216],
   606    tomato: [255, 99, 71],
   607    turquoise: [64, 224, 208],
   608    violet: [238, 130, 238],
   609    wheat: [245, 222, 179],
   610    white: [255, 255, 255],
   611    whitesmoke: [245, 245, 245],
   612    yellow: [255, 255, 0],
   613    yellowgreen: [154, 205, 50]
   614  };
   615  
   616  var setMap = function setMap(options) {
   617    var obj = options.map;
   618    var keys = options.keys;
   619    var l = keys.length;
   620  
   621    for (var i = 0; i < l; i++) {
   622      var key = keys[i];
   623  
   624      if (plainObject(key)) {
   625        throw Error('Tried to set map with object key');
   626      }
   627  
   628      if (i < keys.length - 1) {
   629        // extend the map if necessary
   630        if (obj[key] == null) {
   631          obj[key] = {};
   632        }
   633  
   634        obj = obj[key];
   635      } else {
   636        // set the value
   637        obj[key] = options.value;
   638      }
   639    }
   640  }; // gets the value in a map even if it's not built in places
   641  
   642  var getMap = function getMap(options) {
   643    var obj = options.map;
   644    var keys = options.keys;
   645    var l = keys.length;
   646  
   647    for (var i = 0; i < l; i++) {
   648      var key = keys[i];
   649  
   650      if (plainObject(key)) {
   651        throw Error('Tried to get map with object key');
   652      }
   653  
   654      obj = obj[key];
   655  
   656      if (obj == null) {
   657        return obj;
   658      }
   659    }
   660  
   661    return obj;
   662  }; // deletes the entry in the map
   663  
   664  var performance = window$1 ? window$1.performance : null;
   665  var pnow = performance && performance.now ? function () {
   666    return performance.now();
   667  } : function () {
   668    return Date.now();
   669  };
   670  
   671  var raf = function () {
   672    if (window$1) {
   673      if (window$1.requestAnimationFrame) {
   674        return function (fn) {
   675          window$1.requestAnimationFrame(fn);
   676        };
   677      } else if (window$1.mozRequestAnimationFrame) {
   678        return function (fn) {
   679          window$1.mozRequestAnimationFrame(fn);
   680        };
   681      } else if (window$1.webkitRequestAnimationFrame) {
   682        return function (fn) {
   683          window$1.webkitRequestAnimationFrame(fn);
   684        };
   685      } else if (window$1.msRequestAnimationFrame) {
   686        return function (fn) {
   687          window$1.msRequestAnimationFrame(fn);
   688        };
   689      }
   690    }
   691  
   692    return function (fn) {
   693      if (fn) {
   694        setTimeout(function () {
   695          fn(pnow());
   696        }, 1000 / 60);
   697      }
   698    };
   699  }();
   700  
   701  var requestAnimationFrame = function requestAnimationFrame(fn) {
   702    return raf(fn);
   703  };
   704  var performanceNow = pnow;
   705  
   706  var DEFAULT_SEED = 5381;
   707  var hashIterableInts = function hashIterableInts(iterator) {
   708    var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_SEED;
   709    // djb2/string-hash
   710    var hash = seed;
   711    var entry;
   712  
   713    for (;;) {
   714      entry = iterator.next();
   715  
   716      if (entry.done) {
   717        break;
   718      }
   719  
   720      hash = (hash << 5) + hash + entry.value | 0;
   721    }
   722  
   723    return hash;
   724  };
   725  var hashInt = function hashInt(num) {
   726    var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_SEED;
   727    // djb2/string-hash
   728    return (seed << 5) + seed + num | 0;
   729  };
   730  var hashIntsArray = function hashIntsArray(ints, seed) {
   731    var entry = {
   732      value: 0,
   733      done: false
   734    };
   735    var i = 0;
   736    var length = ints.length;
   737    var iterator = {
   738      next: function next() {
   739        if (i < length) {
   740          entry.value = ints[i++];
   741        } else {
   742          entry.done = true;
   743        }
   744  
   745        return entry;
   746      }
   747    };
   748    return hashIterableInts(iterator, seed);
   749  };
   750  var hashString = function hashString(str, seed) {
   751    var entry = {
   752      value: 0,
   753      done: false
   754    };
   755    var i = 0;
   756    var length = str.length;
   757    var iterator = {
   758      next: function next() {
   759        if (i < length) {
   760          entry.value = str.charCodeAt(i++);
   761        } else {
   762          entry.done = true;
   763        }
   764  
   765        return entry;
   766      }
   767    };
   768    return hashIterableInts(iterator, seed);
   769  };
   770  var hashStrings = function hashStrings() {
   771    return hashStringsArray(arguments);
   772  };
   773  var hashStringsArray = function hashStringsArray(strs) {
   774    var hash;
   775  
   776    for (var i = 0; i < strs.length; i++) {
   777      var str = strs[i];
   778  
   779      if (i === 0) {
   780        hash = hashString(str);
   781      } else {
   782        hash = hashString(str, hash);
   783      }
   784    }
   785  
   786    return hash;
   787  };
   788  
   789  /*global console */
   790  var warningsEnabled = true;
   791  var warnSupported = console.warn != null; // eslint-disable-line no-console
   792  
   793  var traceSupported = console.trace != null; // eslint-disable-line no-console
   794  
   795  var MAX_INT = Number.MAX_SAFE_INTEGER || 9007199254740991;
   796  var trueify = function trueify() {
   797    return true;
   798  };
   799  var falsify = function falsify() {
   800    return false;
   801  };
   802  var zeroify = function zeroify() {
   803    return 0;
   804  };
   805  var noop = function noop() {};
   806  var error = function error(msg) {
   807    throw new Error(msg);
   808  };
   809  var warnings = function warnings(enabled) {
   810    if (enabled !== undefined) {
   811      warningsEnabled = !!enabled;
   812    } else {
   813      return warningsEnabled;
   814    }
   815  };
   816  var warn = function warn(msg) {
   817    /* eslint-disable no-console */
   818    if (!warnings()) {
   819      return;
   820    }
   821  
   822    if (warnSupported) {
   823      console.warn(msg);
   824    } else {
   825      console.log(msg);
   826  
   827      if (traceSupported) {
   828        console.trace();
   829      }
   830    }
   831  };
   832  /* eslint-enable */
   833  
   834  var clone = function clone(obj) {
   835    return extend({}, obj);
   836  }; // gets a shallow copy of the argument
   837  
   838  var copy = function copy(obj) {
   839    if (obj == null) {
   840      return obj;
   841    }
   842  
   843    if (array(obj)) {
   844      return obj.slice();
   845    } else if (plainObject(obj)) {
   846      return clone(obj);
   847    } else {
   848      return obj;
   849    }
   850  };
   851  var copyArray = function copyArray(arr) {
   852    return arr.slice();
   853  };
   854  var uuid = function uuid(a, b
   855  /* placeholders */
   856  ) {
   857    for ( // loop :)
   858    b = a = ''; // b - result , a - numeric letiable
   859    a++ < 36; //
   860    b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
   861    ? //  return a random number or 4
   862    (a ^ 15 // if "a" is not 15
   863    ? // genetate a random number from 0 to 15
   864    8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
   865    : 4 //  otherwise 4
   866    ).toString(16) : '-' //  in other cases (if "a" is 9,14,19,24) insert "-"
   867    ) {
   868    }
   869  
   870    return b;
   871  };
   872  var _staticEmptyObject = {};
   873  var staticEmptyObject = function staticEmptyObject() {
   874    return _staticEmptyObject;
   875  };
   876  var defaults = function defaults(_defaults) {
   877    var keys = Object.keys(_defaults);
   878    return function (opts) {
   879      var filledOpts = {};
   880  
   881      for (var i = 0; i < keys.length; i++) {
   882        var key = keys[i];
   883        var optVal = opts == null ? undefined : opts[key];
   884        filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
   885      }
   886  
   887      return filledOpts;
   888    };
   889  };
   890  var removeFromArray = function removeFromArray(arr, ele, manyCopies) {
   891    for (var i = arr.length; i >= 0; i--) {
   892      if (arr[i] === ele) {
   893        arr.splice(i, 1);
   894  
   895        if (!manyCopies) {
   896          break;
   897        }
   898      }
   899    }
   900  };
   901  var clearArray = function clearArray(arr) {
   902    arr.splice(0, arr.length);
   903  };
   904  var push = function push(arr, otherArr) {
   905    for (var i = 0; i < otherArr.length; i++) {
   906      var el = otherArr[i];
   907      arr.push(el);
   908    }
   909  };
   910  var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
   911    if (prefix) {
   912      propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
   913    }
   914  
   915    return obj[propName];
   916  };
   917  var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
   918    if (prefix) {
   919      propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
   920    }
   921  
   922    obj[propName] = value;
   923  };
   924  
   925  /* global Map */
   926  var ObjectMap =
   927  /*#__PURE__*/
   928  function () {
   929    function ObjectMap() {
   930      _classCallCheck(this, ObjectMap);
   931  
   932      this._obj = {};
   933    }
   934  
   935    _createClass(ObjectMap, [{
   936      key: "set",
   937      value: function set(key, val) {
   938        this._obj[key] = val;
   939        return this;
   940      }
   941    }, {
   942      key: "delete",
   943      value: function _delete(key) {
   944        this._obj[key] = undefined;
   945        return this;
   946      }
   947    }, {
   948      key: "clear",
   949      value: function clear() {
   950        this._obj = {};
   951      }
   952    }, {
   953      key: "has",
   954      value: function has(key) {
   955        return this._obj[key] !== undefined;
   956      }
   957    }, {
   958      key: "get",
   959      value: function get(key) {
   960        return this._obj[key];
   961      }
   962    }]);
   963  
   964    return ObjectMap;
   965  }();
   966  
   967  var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
   968  
   969  /* global Set */
   970  var undef =  "undefined" ;
   971  
   972  var ObjectSet =
   973  /*#__PURE__*/
   974  function () {
   975    function ObjectSet(arrayOrObjectSet) {
   976      _classCallCheck(this, ObjectSet);
   977  
   978      this._obj = Object.create(null);
   979      this.size = 0;
   980  
   981      if (arrayOrObjectSet != null) {
   982        var arr;
   983  
   984        if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
   985          arr = arrayOrObjectSet.toArray();
   986        } else {
   987          arr = arrayOrObjectSet;
   988        }
   989  
   990        for (var i = 0; i < arr.length; i++) {
   991          this.add(arr[i]);
   992        }
   993      }
   994    }
   995  
   996    _createClass(ObjectSet, [{
   997      key: "instanceString",
   998      value: function instanceString() {
   999        return 'set';
  1000      }
  1001    }, {
  1002      key: "add",
  1003      value: function add(val) {
  1004        var o = this._obj;
  1005  
  1006        if (o[val] !== 1) {
  1007          o[val] = 1;
  1008          this.size++;
  1009        }
  1010      }
  1011    }, {
  1012      key: "delete",
  1013      value: function _delete(val) {
  1014        var o = this._obj;
  1015  
  1016        if (o[val] === 1) {
  1017          o[val] = 0;
  1018          this.size--;
  1019        }
  1020      }
  1021    }, {
  1022      key: "clear",
  1023      value: function clear() {
  1024        this._obj = Object.create(null);
  1025      }
  1026    }, {
  1027      key: "has",
  1028      value: function has(val) {
  1029        return this._obj[val] === 1;
  1030      }
  1031    }, {
  1032      key: "toArray",
  1033      value: function toArray() {
  1034        var _this = this;
  1035  
  1036        return Object.keys(this._obj).filter(function (key) {
  1037          return _this.has(key);
  1038        });
  1039      }
  1040    }, {
  1041      key: "forEach",
  1042      value: function forEach(callback, thisArg) {
  1043        return this.toArray().forEach(callback, thisArg);
  1044      }
  1045    }]);
  1046  
  1047    return ObjectSet;
  1048  }();
  1049  
  1050  var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
  1051  
  1052  var Element = function Element(cy, params, restore) {
  1053    restore = restore === undefined || restore ? true : false;
  1054  
  1055    if (cy === undefined || params === undefined || !core(cy)) {
  1056      error('An element must have a core reference and parameters set');
  1057      return;
  1058    }
  1059  
  1060    var group = params.group; // try to automatically infer the group if unspecified
  1061  
  1062    if (group == null) {
  1063      if (params.data && params.data.source != null && params.data.target != null) {
  1064        group = 'edges';
  1065      } else {
  1066        group = 'nodes';
  1067      }
  1068    } // validate group
  1069  
  1070  
  1071    if (group !== 'nodes' && group !== 'edges') {
  1072      error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
  1073      return;
  1074    } // make the element array-like, just like a collection
  1075  
  1076  
  1077    this.length = 1;
  1078    this[0] = this; // NOTE: when something is added here, add also to ele.json()
  1079  
  1080    var _p = this._private = {
  1081      cy: cy,
  1082      single: true,
  1083      // indicates this is an element
  1084      data: params.data || {},
  1085      // data object
  1086      position: params.position || {
  1087        x: 0,
  1088        y: 0
  1089      },
  1090      // (x, y) position pair
  1091      autoWidth: undefined,
  1092      // width and height of nodes calculated by the renderer when set to special 'auto' value
  1093      autoHeight: undefined,
  1094      autoPadding: undefined,
  1095      compoundBoundsClean: false,
  1096      // whether the compound dimensions need to be recalculated the next time dimensions are read
  1097      listeners: [],
  1098      // array of bound listeners
  1099      group: group,
  1100      // string; 'nodes' or 'edges'
  1101      style: {},
  1102      // properties as set by the style
  1103      rstyle: {},
  1104      // properties for style sent from the renderer to the core
  1105      styleCxts: [],
  1106      // applied style contexts from the styler
  1107      styleKeys: {},
  1108      // per-group keys of style property values
  1109      removed: true,
  1110      // whether it's inside the vis; true if removed (set true here since we call restore)
  1111      selected: params.selected ? true : false,
  1112      // whether it's selected
  1113      selectable: params.selectable === undefined ? true : params.selectable ? true : false,
  1114      // whether it's selectable
  1115      locked: params.locked ? true : false,
  1116      // whether the element is locked (cannot be moved)
  1117      grabbed: false,
  1118      // whether the element is grabbed by the mouse; renderer sets this privately
  1119      grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
  1120      // whether the element can be grabbed
  1121      pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
  1122      // whether the element has passthrough panning enabled
  1123      active: false,
  1124      // whether the element is active from user interaction
  1125      classes: new Set$1(),
  1126      // map ( className => true )
  1127      animation: {
  1128        // object for currently-running animations
  1129        current: [],
  1130        queue: []
  1131      },
  1132      rscratch: {},
  1133      // object in which the renderer can store information
  1134      scratch: params.scratch || {},
  1135      // scratch objects
  1136      edges: [],
  1137      // array of connected edges
  1138      children: [],
  1139      // array of children
  1140      parent: null,
  1141      // parent ref
  1142      traversalCache: {},
  1143      // cache of output of traversal functions
  1144      backgrounding: false,
  1145      // whether background images are loading
  1146      bbCache: null,
  1147      // cache of the current bounding box
  1148      bbCacheShift: {
  1149        x: 0,
  1150        y: 0
  1151      },
  1152      // shift applied to cached bb to be applied on next get
  1153      bodyBounds: null,
  1154      // bounds cache of element body, w/o overlay
  1155      overlayBounds: null,
  1156      // bounds cache of element body, including overlay
  1157      labelBounds: {
  1158        // bounds cache of labels
  1159        all: null,
  1160        source: null,
  1161        target: null,
  1162        main: null
  1163      },
  1164      arrowBounds: {
  1165        // bounds cache of edge arrows
  1166        source: null,
  1167        target: null,
  1168        'mid-source': null,
  1169        'mid-target': null
  1170      }
  1171    };
  1172  
  1173    if (_p.position.x == null) {
  1174      _p.position.x = 0;
  1175    }
  1176  
  1177    if (_p.position.y == null) {
  1178      _p.position.y = 0;
  1179    } // renderedPosition overrides if specified
  1180  
  1181  
  1182    if (params.renderedPosition) {
  1183      var rpos = params.renderedPosition;
  1184      var pan = cy.pan();
  1185      var zoom = cy.zoom();
  1186      _p.position = {
  1187        x: (rpos.x - pan.x) / zoom,
  1188        y: (rpos.y - pan.y) / zoom
  1189      };
  1190    }
  1191  
  1192    var classes = [];
  1193  
  1194    if (array(params.classes)) {
  1195      classes = params.classes;
  1196    } else if (string(params.classes)) {
  1197      classes = params.classes.split(/\s+/);
  1198    }
  1199  
  1200    for (var i = 0, l = classes.length; i < l; i++) {
  1201      var cls = classes[i];
  1202  
  1203      if (!cls || cls === '') {
  1204        continue;
  1205      }
  1206  
  1207      _p.classes.add(cls);
  1208    }
  1209  
  1210    this.createEmitter();
  1211    var bypass = params.style || params.css;
  1212  
  1213    if (bypass) {
  1214      warn('Setting a `style` bypass at element creation is deprecated');
  1215      this.style(bypass);
  1216    }
  1217  
  1218    if (restore === undefined || restore) {
  1219      this.restore();
  1220    }
  1221  };
  1222  
  1223  var defineSearch = function defineSearch(params) {
  1224    params = {
  1225      bfs: params.bfs || !params.dfs,
  1226      dfs: params.dfs || !params.bfs
  1227    }; // from pseudocode on wikipedia
  1228  
  1229    return function searchFn(roots, fn$1, directed) {
  1230      var options;
  1231  
  1232      if (plainObject(roots) && !elementOrCollection(roots)) {
  1233        options = roots;
  1234        roots = options.roots || options.root;
  1235        fn$1 = options.visit;
  1236        directed = options.directed;
  1237      }
  1238  
  1239      directed = arguments.length === 2 && !fn(fn$1) ? fn$1 : directed;
  1240      fn$1 = fn(fn$1) ? fn$1 : function () {};
  1241      var cy = this._private.cy;
  1242      var v = roots = string(roots) ? this.filter(roots) : roots;
  1243      var Q = [];
  1244      var connectedNodes = [];
  1245      var connectedBy = {};
  1246      var id2depth = {};
  1247      var V = {};
  1248      var j = 0;
  1249      var found;
  1250  
  1251      var _this$byGroup = this.byGroup(),
  1252          nodes = _this$byGroup.nodes,
  1253          edges = _this$byGroup.edges; // enqueue v
  1254  
  1255  
  1256      for (var i = 0; i < v.length; i++) {
  1257        var vi = v[i];
  1258        var viId = vi.id();
  1259  
  1260        if (vi.isNode()) {
  1261          Q.unshift(vi);
  1262  
  1263          if (params.bfs) {
  1264            V[viId] = true;
  1265            connectedNodes.push(vi);
  1266          }
  1267  
  1268          id2depth[viId] = 0;
  1269        }
  1270      }
  1271  
  1272      var _loop2 = function _loop2() {
  1273        var v = params.bfs ? Q.shift() : Q.pop();
  1274        var vId = v.id();
  1275  
  1276        if (params.dfs) {
  1277          if (V[vId]) {
  1278            return "continue";
  1279          }
  1280  
  1281          V[vId] = true;
  1282          connectedNodes.push(v);
  1283        }
  1284  
  1285        var depth = id2depth[vId];
  1286        var prevEdge = connectedBy[vId];
  1287        var src = prevEdge != null ? prevEdge.source() : null;
  1288        var tgt = prevEdge != null ? prevEdge.target() : null;
  1289        var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
  1290        var ret = void 0;
  1291        ret = fn$1(v, prevEdge, prevNode, j++, depth);
  1292  
  1293        if (ret === true) {
  1294          found = v;
  1295          return "break";
  1296        }
  1297  
  1298        if (ret === false) {
  1299          return "break";
  1300        }
  1301  
  1302        var vwEdges = v.connectedEdges().filter(function (e) {
  1303          return (!directed || e.source().same(v)) && edges.has(e);
  1304        });
  1305  
  1306        for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
  1307          var e = vwEdges[_i2];
  1308          var w = e.connectedNodes().filter(function (n) {
  1309            return !n.same(v) && nodes.has(n);
  1310          });
  1311          var wId = w.id();
  1312  
  1313          if (w.length !== 0 && !V[wId]) {
  1314            w = w[0];
  1315            Q.push(w);
  1316  
  1317            if (params.bfs) {
  1318              V[wId] = true;
  1319              connectedNodes.push(w);
  1320            }
  1321  
  1322            connectedBy[wId] = e;
  1323            id2depth[wId] = id2depth[vId] + 1;
  1324          }
  1325        }
  1326      };
  1327  
  1328      _loop: while (Q.length !== 0) {
  1329        var _ret = _loop2();
  1330  
  1331        switch (_ret) {
  1332          case "continue":
  1333            continue;
  1334  
  1335          case "break":
  1336            break _loop;
  1337        }
  1338      }
  1339  
  1340      var connectedEles = cy.collection();
  1341  
  1342      for (var _i = 0; _i < connectedNodes.length; _i++) {
  1343        var node = connectedNodes[_i];
  1344        var edge = connectedBy[node.id()];
  1345  
  1346        if (edge != null) {
  1347          connectedEles.merge(edge);
  1348        }
  1349  
  1350        connectedEles.merge(node);
  1351      }
  1352  
  1353      return {
  1354        path: cy.collection(connectedEles),
  1355        found: cy.collection(found)
  1356      };
  1357    };
  1358  }; // search, spanning trees, etc
  1359  
  1360  
  1361  var elesfn = {
  1362    breadthFirstSearch: defineSearch({
  1363      bfs: true
  1364    }),
  1365    depthFirstSearch: defineSearch({
  1366      dfs: true
  1367    })
  1368  }; // nice, short mathemathical alias
  1369  
  1370  elesfn.bfs = elesfn.breadthFirstSearch;
  1371  elesfn.dfs = elesfn.depthFirstSearch;
  1372  
  1373  var dijkstraDefaults = defaults({
  1374    root: null,
  1375    weight: function weight(edge) {
  1376      return 1;
  1377    },
  1378    directed: false
  1379  });
  1380  var elesfn$1 = {
  1381    dijkstra: function dijkstra(options) {
  1382      if (!plainObject(options)) {
  1383        var args = arguments;
  1384        options = {
  1385          root: args[0],
  1386          weight: args[1],
  1387          directed: args[2]
  1388        };
  1389      }
  1390  
  1391      var _dijkstraDefaults = dijkstraDefaults(options),
  1392          root = _dijkstraDefaults.root,
  1393          weight = _dijkstraDefaults.weight,
  1394          directed = _dijkstraDefaults.directed;
  1395  
  1396      var eles = this;
  1397      var weightFn = weight;
  1398      var source = string(root) ? this.filter(root)[0] : root[0];
  1399      var dist = {};
  1400      var prev = {};
  1401      var knownDist = {};
  1402  
  1403      var _this$byGroup = this.byGroup(),
  1404          nodes = _this$byGroup.nodes,
  1405          edges = _this$byGroup.edges;
  1406  
  1407      edges.unmergeBy(function (ele) {
  1408        return ele.isLoop();
  1409      });
  1410  
  1411      var getDist = function getDist(node) {
  1412        return dist[node.id()];
  1413      };
  1414  
  1415      var setDist = function setDist(node, d) {
  1416        dist[node.id()] = d;
  1417        Q.updateItem(node);
  1418      };
  1419  
  1420      var Q = new Heap(function (a, b) {
  1421        return getDist(a) - getDist(b);
  1422      });
  1423  
  1424      for (var i = 0; i < nodes.length; i++) {
  1425        var node = nodes[i];
  1426        dist[node.id()] = node.same(source) ? 0 : Infinity;
  1427        Q.push(node);
  1428      }
  1429  
  1430      var distBetween = function distBetween(u, v) {
  1431        var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
  1432        var smallestDistance = Infinity;
  1433        var smallestEdge;
  1434  
  1435        for (var _i = 0; _i < uvs.length; _i++) {
  1436          var edge = uvs[_i];
  1437  
  1438          var _weight = weightFn(edge);
  1439  
  1440          if (_weight < smallestDistance || !smallestEdge) {
  1441            smallestDistance = _weight;
  1442            smallestEdge = edge;
  1443          }
  1444        }
  1445  
  1446        return {
  1447          edge: smallestEdge,
  1448          dist: smallestDistance
  1449        };
  1450      };
  1451  
  1452      while (Q.size() > 0) {
  1453        var u = Q.pop();
  1454        var smalletsDist = getDist(u);
  1455        var uid = u.id();
  1456        knownDist[uid] = smalletsDist;
  1457  
  1458        if (smalletsDist === Infinity) {
  1459          continue;
  1460        }
  1461  
  1462        var neighbors = u.neighborhood().intersect(nodes);
  1463  
  1464        for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
  1465          var v = neighbors[_i2];
  1466          var vid = v.id();
  1467          var vDist = distBetween(u, v);
  1468          var alt = smalletsDist + vDist.dist;
  1469  
  1470          if (alt < getDist(v)) {
  1471            setDist(v, alt);
  1472            prev[vid] = {
  1473              node: u,
  1474              edge: vDist.edge
  1475            };
  1476          }
  1477        } // for
  1478  
  1479      } // while
  1480  
  1481  
  1482      return {
  1483        distanceTo: function distanceTo(node) {
  1484          var target = string(node) ? nodes.filter(node)[0] : node[0];
  1485          return knownDist[target.id()];
  1486        },
  1487        pathTo: function pathTo(node) {
  1488          var target = string(node) ? nodes.filter(node)[0] : node[0];
  1489          var S = [];
  1490          var u = target;
  1491          var uid = u.id();
  1492  
  1493          if (target.length > 0) {
  1494            S.unshift(target);
  1495  
  1496            while (prev[uid]) {
  1497              var p = prev[uid];
  1498              S.unshift(p.edge);
  1499              S.unshift(p.node);
  1500              u = p.node;
  1501              uid = u.id();
  1502            }
  1503          }
  1504  
  1505          return eles.spawn(S);
  1506        }
  1507      };
  1508    }
  1509  };
  1510  
  1511  var elesfn$2 = {
  1512    // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
  1513    // implemented from pseudocode from wikipedia
  1514    kruskal: function kruskal(weightFn) {
  1515      weightFn = weightFn || function (edge) {
  1516        return 1;
  1517      };
  1518  
  1519      var _this$byGroup = this.byGroup(),
  1520          nodes = _this$byGroup.nodes,
  1521          edges = _this$byGroup.edges;
  1522  
  1523      var numNodes = nodes.length;
  1524      var forest = new Array(numNodes);
  1525      var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
  1526  
  1527      var findSetIndex = function findSetIndex(ele) {
  1528        for (var i = 0; i < forest.length; i++) {
  1529          var eles = forest[i];
  1530  
  1531          if (eles.has(ele)) {
  1532            return i;
  1533          }
  1534        }
  1535      }; // start with one forest per node
  1536  
  1537  
  1538      for (var i = 0; i < numNodes; i++) {
  1539        forest[i] = this.spawn(nodes[i]);
  1540      }
  1541  
  1542      var S = edges.sort(function (a, b) {
  1543        return weightFn(a) - weightFn(b);
  1544      });
  1545  
  1546      for (var _i = 0; _i < S.length; _i++) {
  1547        var edge = S[_i];
  1548        var u = edge.source()[0];
  1549        var v = edge.target()[0];
  1550        var setUIndex = findSetIndex(u);
  1551        var setVIndex = findSetIndex(v);
  1552        var setU = forest[setUIndex];
  1553        var setV = forest[setVIndex];
  1554  
  1555        if (setUIndex !== setVIndex) {
  1556          A.merge(edge); // combine forests for u and v
  1557  
  1558          setU.merge(setV);
  1559          forest.splice(setVIndex, 1);
  1560        }
  1561      }
  1562  
  1563      return A;
  1564    }
  1565  };
  1566  
  1567  var aStarDefaults = defaults({
  1568    root: null,
  1569    goal: null,
  1570    weight: function weight(edge) {
  1571      return 1;
  1572    },
  1573    heuristic: function heuristic(edge) {
  1574      return 0;
  1575    },
  1576    directed: false
  1577  });
  1578  var elesfn$3 = {
  1579    // Implemented from pseudocode from wikipedia
  1580    aStar: function aStar(options) {
  1581      var cy = this.cy();
  1582  
  1583      var _aStarDefaults = aStarDefaults(options),
  1584          root = _aStarDefaults.root,
  1585          goal = _aStarDefaults.goal,
  1586          heuristic = _aStarDefaults.heuristic,
  1587          directed = _aStarDefaults.directed,
  1588          weight = _aStarDefaults.weight;
  1589  
  1590      root = cy.collection(root)[0];
  1591      goal = cy.collection(goal)[0];
  1592      var sid = root.id();
  1593      var tid = goal.id();
  1594      var gScore = {};
  1595      var fScore = {};
  1596      var closedSetIds = {};
  1597      var openSet = new Heap(function (a, b) {
  1598        return fScore[a.id()] - fScore[b.id()];
  1599      });
  1600      var openSetIds = new Set$1();
  1601      var cameFrom = {};
  1602      var cameFromEdge = {};
  1603  
  1604      var addToOpenSet = function addToOpenSet(ele, id) {
  1605        openSet.push(ele);
  1606        openSetIds.add(id);
  1607      };
  1608  
  1609      var cMin, cMinId;
  1610  
  1611      var popFromOpenSet = function popFromOpenSet() {
  1612        cMin = openSet.pop();
  1613        cMinId = cMin.id();
  1614        openSetIds["delete"](cMinId);
  1615      };
  1616  
  1617      var isInOpenSet = function isInOpenSet(id) {
  1618        return openSetIds.has(id);
  1619      };
  1620  
  1621      addToOpenSet(root, sid);
  1622      gScore[sid] = 0;
  1623      fScore[sid] = heuristic(root); // Counter
  1624  
  1625      var steps = 0; // Main loop
  1626  
  1627      while (openSet.size() > 0) {
  1628        popFromOpenSet();
  1629        steps++; // If we've found our goal, then we are done
  1630  
  1631        if (cMinId === tid) {
  1632          var path = [];
  1633          var pathNode = goal;
  1634          var pathNodeId = tid;
  1635          var pathEdge = cameFromEdge[pathNodeId];
  1636  
  1637          for (;;) {
  1638            path.unshift(pathNode);
  1639  
  1640            if (pathEdge != null) {
  1641              path.unshift(pathEdge);
  1642            }
  1643  
  1644            pathNode = cameFrom[pathNodeId];
  1645  
  1646            if (pathNode == null) {
  1647              break;
  1648            }
  1649  
  1650            pathNodeId = pathNode.id();
  1651            pathEdge = cameFromEdge[pathNodeId];
  1652          }
  1653  
  1654          return {
  1655            found: true,
  1656            distance: gScore[cMinId],
  1657            path: this.spawn(path),
  1658            steps: steps
  1659          };
  1660        } // Add cMin to processed nodes
  1661  
  1662  
  1663        closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
  1664        // Take into account if graph is directed or not
  1665  
  1666        var vwEdges = cMin._private.edges;
  1667  
  1668        for (var i = 0; i < vwEdges.length; i++) {
  1669          var e = vwEdges[i]; // edge must be in set of calling eles
  1670  
  1671          if (!this.hasElementWithId(e.id())) {
  1672            continue;
  1673          } // cMin must be the source of edge if directed
  1674  
  1675  
  1676          if (directed && e.data('source') !== cMinId) {
  1677            continue;
  1678          }
  1679  
  1680          var wSrc = e.source();
  1681          var wTgt = e.target();
  1682          var w = wSrc.id() !== cMinId ? wSrc : wTgt;
  1683          var wid = w.id(); // node must be in set of calling eles
  1684  
  1685          if (!this.hasElementWithId(wid)) {
  1686            continue;
  1687          } // if node is in closedSet, ignore it
  1688  
  1689  
  1690          if (closedSetIds[wid]) {
  1691            continue;
  1692          } // New tentative score for node w
  1693  
  1694  
  1695          var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
  1696          //   w not present in openSet
  1697          // OR
  1698          //   tentative gScore is less than previous value
  1699          // w not in openSet
  1700  
  1701          if (!isInOpenSet(wid)) {
  1702            gScore[wid] = tempScore;
  1703            fScore[wid] = tempScore + heuristic(w);
  1704            addToOpenSet(w, wid);
  1705            cameFrom[wid] = cMin;
  1706            cameFromEdge[wid] = e;
  1707            continue;
  1708          } // w already in openSet, but with greater gScore
  1709  
  1710  
  1711          if (tempScore < gScore[wid]) {
  1712            gScore[wid] = tempScore;
  1713            fScore[wid] = tempScore + heuristic(w);
  1714            cameFrom[wid] = cMin;
  1715          }
  1716        } // End of neighbors update
  1717  
  1718      } // End of main loop
  1719      // If we've reached here, then we've not reached our goal
  1720  
  1721  
  1722      return {
  1723        found: false,
  1724        distance: undefined,
  1725        path: undefined,
  1726        steps: steps
  1727      };
  1728    }
  1729  }; // elesfn
  1730  
  1731  var floydWarshallDefaults = defaults({
  1732    weight: function weight(edge) {
  1733      return 1;
  1734    },
  1735    directed: false
  1736  });
  1737  var elesfn$4 = {
  1738    // Implemented from pseudocode from wikipedia
  1739    floydWarshall: function floydWarshall(options) {
  1740      var cy = this.cy();
  1741  
  1742      var _floydWarshallDefault = floydWarshallDefaults(options),
  1743          weight = _floydWarshallDefault.weight,
  1744          directed = _floydWarshallDefault.directed;
  1745  
  1746      var weightFn = weight;
  1747  
  1748      var _this$byGroup = this.byGroup(),
  1749          nodes = _this$byGroup.nodes,
  1750          edges = _this$byGroup.edges;
  1751  
  1752      var N = nodes.length;
  1753      var Nsq = N * N;
  1754  
  1755      var indexOf = function indexOf(node) {
  1756        return nodes.indexOf(node);
  1757      };
  1758  
  1759      var atIndex = function atIndex(i) {
  1760        return nodes[i];
  1761      }; // Initialize distance matrix
  1762  
  1763  
  1764      var dist = new Array(Nsq);
  1765  
  1766      for (var n = 0; n < Nsq; n++) {
  1767        var j = n % N;
  1768        var i = (n - j) / N;
  1769  
  1770        if (i === j) {
  1771          dist[n] = 0;
  1772        } else {
  1773          dist[n] = Infinity;
  1774        }
  1775      } // Initialize matrix used for path reconstruction
  1776      // Initialize distance matrix
  1777  
  1778  
  1779      var next = new Array(Nsq);
  1780      var edgeNext = new Array(Nsq); // Process edges
  1781  
  1782      for (var _i = 0; _i < edges.length; _i++) {
  1783        var edge = edges[_i];
  1784        var src = edge.source()[0];
  1785        var tgt = edge.target()[0];
  1786  
  1787        if (src === tgt) {
  1788          continue;
  1789        } // exclude loops
  1790  
  1791  
  1792        var s = indexOf(src);
  1793        var t = indexOf(tgt);
  1794        var st = s * N + t; // source to target index
  1795  
  1796        var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
  1797  
  1798  
  1799        if (dist[st] > _weight) {
  1800          dist[st] = _weight;
  1801          next[st] = t;
  1802          edgeNext[st] = edge;
  1803        } // If undirected graph, process 'reversed' edge
  1804  
  1805  
  1806        if (!directed) {
  1807          var ts = t * N + s; // target to source index
  1808  
  1809          if (!directed && dist[ts] > _weight) {
  1810            dist[ts] = _weight;
  1811            next[ts] = s;
  1812            edgeNext[ts] = edge;
  1813          }
  1814        }
  1815      } // Main loop
  1816  
  1817  
  1818      for (var k = 0; k < N; k++) {
  1819        for (var _i2 = 0; _i2 < N; _i2++) {
  1820          var ik = _i2 * N + k;
  1821  
  1822          for (var _j = 0; _j < N; _j++) {
  1823            var ij = _i2 * N + _j;
  1824            var kj = k * N + _j;
  1825  
  1826            if (dist[ik] + dist[kj] < dist[ij]) {
  1827              dist[ij] = dist[ik] + dist[kj];
  1828              next[ij] = next[ik];
  1829            }
  1830          }
  1831        }
  1832      }
  1833  
  1834      var getArgEle = function getArgEle(ele) {
  1835        return (string(ele) ? cy.filter(ele) : ele)[0];
  1836      };
  1837  
  1838      var indexOfArgEle = function indexOfArgEle(ele) {
  1839        return indexOf(getArgEle(ele));
  1840      };
  1841  
  1842      var res = {
  1843        distance: function distance(from, to) {
  1844          var i = indexOfArgEle(from);
  1845          var j = indexOfArgEle(to);
  1846          return dist[i * N + j];
  1847        },
  1848        path: function path(from, to) {
  1849          var i = indexOfArgEle(from);
  1850          var j = indexOfArgEle(to);
  1851          var fromNode = atIndex(i);
  1852  
  1853          if (i === j) {
  1854            return fromNode.collection();
  1855          }
  1856  
  1857          if (next[i * N + j] == null) {
  1858            return cy.collection();
  1859          }
  1860  
  1861          var path = cy.collection();
  1862          var prev = i;
  1863          var edge;
  1864          path.merge(fromNode);
  1865  
  1866          while (i !== j) {
  1867            prev = i;
  1868            i = next[i * N + j];
  1869            edge = edgeNext[prev * N + i];
  1870            path.merge(edge);
  1871            path.merge(atIndex(i));
  1872          }
  1873  
  1874          return path;
  1875        }
  1876      };
  1877      return res;
  1878    } // floydWarshall
  1879  
  1880  }; // elesfn
  1881  
  1882  var bellmanFordDefaults = defaults({
  1883    weight: function weight(edge) {
  1884      return 1;
  1885    },
  1886    directed: false,
  1887    root: null
  1888  });
  1889  var elesfn$5 = {
  1890    // Implemented from pseudocode from wikipedia
  1891    bellmanFord: function bellmanFord(options) {
  1892      var _this = this;
  1893  
  1894      var _bellmanFordDefaults = bellmanFordDefaults(options),
  1895          weight = _bellmanFordDefaults.weight,
  1896          directed = _bellmanFordDefaults.directed,
  1897          root = _bellmanFordDefaults.root;
  1898  
  1899      var weightFn = weight;
  1900      var eles = this;
  1901      var cy = this.cy();
  1902  
  1903      var _this$byGroup = this.byGroup(),
  1904          edges = _this$byGroup.edges,
  1905          nodes = _this$byGroup.nodes;
  1906  
  1907      var numNodes = nodes.length;
  1908      var infoMap = new Map$1();
  1909      var hasNegativeWeightCycle = false;
  1910      var negativeWeightCycles = [];
  1911      root = cy.collection(root)[0]; // in case selector passed
  1912  
  1913      edges.unmergeBy(function (edge) {
  1914        return edge.isLoop();
  1915      });
  1916      var numEdges = edges.length;
  1917  
  1918      var getInfo = function getInfo(node) {
  1919        var obj = infoMap.get(node.id());
  1920  
  1921        if (!obj) {
  1922          obj = {};
  1923          infoMap.set(node.id(), obj);
  1924        }
  1925  
  1926        return obj;
  1927      };
  1928  
  1929      var getNodeFromTo = function getNodeFromTo(to) {
  1930        return (string(to) ? cy.$(to) : to)[0];
  1931      };
  1932  
  1933      var distanceTo = function distanceTo(to) {
  1934        return getInfo(getNodeFromTo(to)).dist;
  1935      };
  1936  
  1937      var pathTo = function pathTo(to) {
  1938        var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
  1939        var end = getNodeFromTo(to);
  1940        var path = [];
  1941        var node = end;
  1942  
  1943        for (;;) {
  1944          if (node == null) {
  1945            return _this.spawn();
  1946          }
  1947  
  1948          var _getInfo = getInfo(node),
  1949              edge = _getInfo.edge,
  1950              pred = _getInfo.pred;
  1951  
  1952          path.unshift(node[0]);
  1953  
  1954          if (node.same(thisStart) && path.length > 0) {
  1955            break;
  1956          }
  1957  
  1958          if (edge != null) {
  1959            path.unshift(edge);
  1960          }
  1961  
  1962          node = pred;
  1963        }
  1964  
  1965        return eles.spawn(path);
  1966      }; // Initializations { dist, pred, edge }
  1967  
  1968  
  1969      for (var i = 0; i < numNodes; i++) {
  1970        var node = nodes[i];
  1971        var info = getInfo(node);
  1972  
  1973        if (node.same(root)) {
  1974          info.dist = 0;
  1975        } else {
  1976          info.dist = Infinity;
  1977        }
  1978  
  1979        info.pred = null;
  1980        info.edge = null;
  1981      } // Edges relaxation
  1982  
  1983  
  1984      var replacedEdge = false;
  1985  
  1986      var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
  1987        var dist = info1.dist + weight;
  1988  
  1989        if (dist < info2.dist && !edge.same(info1.edge)) {
  1990          info2.dist = dist;
  1991          info2.pred = node1;
  1992          info2.edge = edge;
  1993          replacedEdge = true;
  1994        }
  1995      };
  1996  
  1997      for (var _i = 1; _i < numNodes; _i++) {
  1998        replacedEdge = false;
  1999  
  2000        for (var e = 0; e < numEdges; e++) {
  2001          var edge = edges[e];
  2002          var src = edge.source();
  2003          var tgt = edge.target();
  2004  
  2005          var _weight = weightFn(edge);
  2006  
  2007          var srcInfo = getInfo(src);
  2008          var tgtInfo = getInfo(tgt);
  2009          checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
  2010  
  2011          if (!directed) {
  2012            checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
  2013          }
  2014        }
  2015  
  2016        if (!replacedEdge) {
  2017          break;
  2018        }
  2019      }
  2020  
  2021      if (replacedEdge) {
  2022        // Check for negative weight cycles
  2023        for (var _e = 0; _e < numEdges; _e++) {
  2024          var _edge = edges[_e];
  2025  
  2026          var _src = _edge.source();
  2027  
  2028          var _tgt = _edge.target();
  2029  
  2030          var _weight2 = weightFn(_edge);
  2031  
  2032          var srcDist = getInfo(_src).dist;
  2033          var tgtDist = getInfo(_tgt).dist;
  2034  
  2035          if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
  2036            warn('Graph contains a negative weight cycle for Bellman-Ford');
  2037            hasNegativeWeightCycle = true;
  2038            break;
  2039          }
  2040        }
  2041      }
  2042  
  2043      return {
  2044        distanceTo: distanceTo,
  2045        pathTo: pathTo,
  2046        hasNegativeWeightCycle: hasNegativeWeightCycle,
  2047        negativeWeightCycles: negativeWeightCycles
  2048      };
  2049    } // bellmanFord
  2050  
  2051  }; // elesfn
  2052  
  2053  var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
  2054  // Updates the remaining edge lists
  2055  // Receives as a paramater the edge which causes the collapse
  2056  
  2057  var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
  2058    if (remainingEdges.length === 0) {
  2059      error("Karger-Stein must be run on a connected (sub)graph");
  2060    }
  2061  
  2062    var edgeInfo = remainingEdges[edgeIndex];
  2063    var sourceIn = edgeInfo[1];
  2064    var targetIn = edgeInfo[2];
  2065    var partition1 = nodeMap[sourceIn];
  2066    var partition2 = nodeMap[targetIn];
  2067    var newEdges = remainingEdges; // re-use array
  2068    // Delete all edges between partition1 and partition2
  2069  
  2070    for (var i = newEdges.length - 1; i >= 0; i--) {
  2071      var edge = newEdges[i];
  2072      var src = edge[1];
  2073      var tgt = edge[2];
  2074  
  2075      if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
  2076        newEdges.splice(i, 1);
  2077      }
  2078    } // All edges pointing to partition2 should now point to partition1
  2079  
  2080  
  2081    for (var _i = 0; _i < newEdges.length; _i++) {
  2082      var _edge = newEdges[_i];
  2083  
  2084      if (_edge[1] === partition2) {
  2085        // Check source
  2086        newEdges[_i] = _edge.slice(); // copy
  2087  
  2088        newEdges[_i][1] = partition1;
  2089      } else if (_edge[2] === partition2) {
  2090        // Check target
  2091        newEdges[_i] = _edge.slice(); // copy
  2092  
  2093        newEdges[_i][2] = partition1;
  2094      }
  2095    } // Move all nodes from partition2 to partition1
  2096  
  2097  
  2098    for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
  2099      if (nodeMap[_i2] === partition2) {
  2100        nodeMap[_i2] = partition1;
  2101      }
  2102    }
  2103  
  2104    return newEdges;
  2105  }; // Contracts a graph until we reach a certain number of meta nodes
  2106  
  2107  
  2108  var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
  2109    while (size > sizeLimit) {
  2110      // Choose an edge randomly
  2111      var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
  2112  
  2113      remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
  2114      size--;
  2115    }
  2116  
  2117    return remainingEdges;
  2118  };
  2119  
  2120  var elesfn$6 = {
  2121    // Computes the minimum cut of an undirected graph
  2122    // Returns the correct answer with high probability
  2123    kargerStein: function kargerStein() {
  2124      var _this = this;
  2125  
  2126      var _this$byGroup = this.byGroup(),
  2127          nodes = _this$byGroup.nodes,
  2128          edges = _this$byGroup.edges;
  2129  
  2130      edges.unmergeBy(function (edge) {
  2131        return edge.isLoop();
  2132      });
  2133      var numNodes = nodes.length;
  2134      var numEdges = edges.length;
  2135      var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
  2136      var stopSize = Math.floor(numNodes / sqrt2);
  2137  
  2138      if (numNodes < 2) {
  2139        error('At least 2 nodes are required for Karger-Stein algorithm');
  2140        return undefined;
  2141      } // Now store edge destination as indexes
  2142      // Format for each edge (edge index, source node index, target node index)
  2143  
  2144  
  2145      var edgeIndexes = [];
  2146  
  2147      for (var i = 0; i < numEdges; i++) {
  2148        var e = edges[i];
  2149        edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
  2150      } // We will store the best cut found here
  2151  
  2152  
  2153      var minCutSize = Infinity;
  2154      var minCutEdgeIndexes = [];
  2155      var minCutNodeMap = new Array(numNodes); // Initial meta node partition
  2156  
  2157      var metaNodeMap = new Array(numNodes);
  2158      var metaNodeMap2 = new Array(numNodes);
  2159  
  2160      var copyNodesMap = function copyNodesMap(from, to) {
  2161        for (var _i3 = 0; _i3 < numNodes; _i3++) {
  2162          to[_i3] = from[_i3];
  2163        }
  2164      }; // Main loop
  2165  
  2166  
  2167      for (var iter = 0; iter <= numIter; iter++) {
  2168        // Reset meta node partition
  2169        for (var _i4 = 0; _i4 < numNodes; _i4++) {
  2170          metaNodeMap[_i4] = _i4;
  2171        } // Contract until stop point (stopSize nodes)
  2172  
  2173  
  2174        var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
  2175        var edgesState2 = edgesState.slice(); // copy
  2176        // Create a copy of the colapsed nodes state
  2177  
  2178        copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
  2179  
  2180        var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
  2181        var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
  2182  
  2183        if (res1.length <= res2.length && res1.length < minCutSize) {
  2184          minCutSize = res1.length;
  2185          minCutEdgeIndexes = res1;
  2186          copyNodesMap(metaNodeMap, minCutNodeMap);
  2187        } else if (res2.length <= res1.length && res2.length < minCutSize) {
  2188          minCutSize = res2.length;
  2189          minCutEdgeIndexes = res2;
  2190          copyNodesMap(metaNodeMap2, minCutNodeMap);
  2191        }
  2192      } // end of main loop
  2193      // Construct result
  2194  
  2195  
  2196      var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
  2197        return edges[e[0]];
  2198      }));
  2199      var partition1 = this.spawn();
  2200      var partition2 = this.spawn(); // traverse metaNodeMap for best cut
  2201  
  2202      var witnessNodePartition = minCutNodeMap[0];
  2203  
  2204      for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
  2205        var partitionId = minCutNodeMap[_i5];
  2206        var node = nodes[_i5];
  2207  
  2208        if (partitionId === witnessNodePartition) {
  2209          partition1.merge(node);
  2210        } else {
  2211          partition2.merge(node);
  2212        }
  2213      } // construct components corresponding to each disjoint subset of nodes
  2214  
  2215  
  2216      var constructComponent = function constructComponent(subset) {
  2217        var component = _this.spawn();
  2218  
  2219        subset.forEach(function (node) {
  2220          component.merge(node);
  2221          node.connectedEdges().forEach(function (edge) {
  2222            // ensure edge is within calling collection and edge is not in cut
  2223            if (_this.contains(edge) && !cut.contains(edge)) {
  2224              component.merge(edge);
  2225            }
  2226          });
  2227        });
  2228        return component;
  2229      };
  2230  
  2231      var components = [constructComponent(partition1), constructComponent(partition2)];
  2232      var ret = {
  2233        cut: cut,
  2234        components: components,
  2235        // n.b. partitions are included to be compatible with the old api spec
  2236        // (could be removed in a future major version)
  2237        partition1: partition1,
  2238        partition2: partition2
  2239      };
  2240      return ret;
  2241    }
  2242  }; // elesfn
  2243  
  2244  var copyPosition = function copyPosition(p) {
  2245    return {
  2246      x: p.x,
  2247      y: p.y
  2248    };
  2249  };
  2250  var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
  2251    return {
  2252      x: p.x * zoom + pan.x,
  2253      y: p.y * zoom + pan.y
  2254    };
  2255  };
  2256  var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
  2257    return {
  2258      x: (p.x - pan.x) / zoom,
  2259      y: (p.y - pan.y) / zoom
  2260    };
  2261  };
  2262  var array2point = function array2point(arr) {
  2263    return {
  2264      x: arr[0],
  2265      y: arr[1]
  2266    };
  2267  };
  2268  var min = function min(arr) {
  2269    var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  2270    var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
  2271    var min = Infinity;
  2272  
  2273    for (var i = begin; i < end; i++) {
  2274      var val = arr[i];
  2275  
  2276      if (isFinite(val)) {
  2277        min = Math.min(val, min);
  2278      }
  2279    }
  2280  
  2281    return min;
  2282  };
  2283  var max = function max(arr) {
  2284    var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  2285    var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
  2286    var max = -Infinity;
  2287  
  2288    for (var i = begin; i < end; i++) {
  2289      var val = arr[i];
  2290  
  2291      if (isFinite(val)) {
  2292        max = Math.max(val, max);
  2293      }
  2294    }
  2295  
  2296    return max;
  2297  };
  2298  var mean = function mean(arr) {
  2299    var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  2300    var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
  2301    var total = 0;
  2302    var n = 0;
  2303  
  2304    for (var i = begin; i < end; i++) {
  2305      var val = arr[i];
  2306  
  2307      if (isFinite(val)) {
  2308        total += val;
  2309        n++;
  2310      }
  2311    }
  2312  
  2313    return total / n;
  2314  };
  2315  var median = function median(arr) {
  2316    var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  2317    var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
  2318    var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
  2319    var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
  2320    var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
  2321  
  2322    if (copy) {
  2323      arr = arr.slice(begin, end);
  2324    } else {
  2325      if (end < arr.length) {
  2326        arr.splice(end, arr.length - end);
  2327      }
  2328  
  2329      if (begin > 0) {
  2330        arr.splice(0, begin);
  2331      }
  2332    } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
  2333  
  2334  
  2335    var off = 0; // offset from non-finite values
  2336  
  2337    for (var i = arr.length - 1; i >= 0; i--) {
  2338      var v = arr[i];
  2339  
  2340      if (includeHoles) {
  2341        if (!isFinite(v)) {
  2342          arr[i] = -Infinity;
  2343          off++;
  2344        }
  2345      } else {
  2346        // just remove it if we don't want to consider holes
  2347        arr.splice(i, 1);
  2348      }
  2349    }
  2350  
  2351    if (sort) {
  2352      arr.sort(function (a, b) {
  2353        return a - b;
  2354      }); // requires copy = true if you don't want to change the orig
  2355    }
  2356  
  2357    var len = arr.length;
  2358    var mid = Math.floor(len / 2);
  2359  
  2360    if (len % 2 !== 0) {
  2361      return arr[mid + 1 + off];
  2362    } else {
  2363      return (arr[mid - 1 + off] + arr[mid + off]) / 2;
  2364    }
  2365  };
  2366  var deg2rad = function deg2rad(deg) {
  2367    return Math.PI * deg / 180;
  2368  };
  2369  var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
  2370    return Math.atan2(dispY, dispX) - Math.PI / 2;
  2371  };
  2372  var log2 = Math.log2 || function (n) {
  2373    return Math.log(n) / Math.log(2);
  2374  };
  2375  var signum = function signum(x) {
  2376    if (x > 0) {
  2377      return 1;
  2378    } else if (x < 0) {
  2379      return -1;
  2380    } else {
  2381      return 0;
  2382    }
  2383  };
  2384  var dist = function dist(p1, p2) {
  2385    return Math.sqrt(sqdist(p1, p2));
  2386  };
  2387  var sqdist = function sqdist(p1, p2) {
  2388    var dx = p2.x - p1.x;
  2389    var dy = p2.y - p1.y;
  2390    return dx * dx + dy * dy;
  2391  };
  2392  var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
  2393    var length = v.length; // First, get sum of all elements
  2394  
  2395    var total = 0;
  2396  
  2397    for (var i = 0; i < length; i++) {
  2398      total += v[i];
  2399    } // Now, divide each by the sum of all elements
  2400  
  2401  
  2402    for (var _i = 0; _i < length; _i++) {
  2403      v[_i] = v[_i] / total;
  2404    }
  2405  
  2406    return v;
  2407  };
  2408  
  2409  var qbezierAt = function qbezierAt(p0, p1, p2, t) {
  2410    return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
  2411  };
  2412  var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
  2413    return {
  2414      x: qbezierAt(p0.x, p1.x, p2.x, t),
  2415      y: qbezierAt(p0.y, p1.y, p2.y, t)
  2416    };
  2417  };
  2418  var lineAt = function lineAt(p0, p1, t, d) {
  2419    var vec = {
  2420      x: p1.x - p0.x,
  2421      y: p1.y - p0.y
  2422    };
  2423    var vecDist = dist(p0, p1);
  2424    var normVec = {
  2425      x: vec.x / vecDist,
  2426      y: vec.y / vecDist
  2427    };
  2428    t = t == null ? 0 : t;
  2429    d = d != null ? d : t * vecDist;
  2430    return {
  2431      x: p0.x + normVec.x * d,
  2432      y: p0.y + normVec.y * d
  2433    };
  2434  };
  2435  var bound = function bound(min, val, max) {
  2436    return Math.max(min, Math.min(max, val));
  2437  }; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
  2438  
  2439  var makeBoundingBox = function makeBoundingBox(bb) {
  2440    if (bb == null) {
  2441      return {
  2442        x1: Infinity,
  2443        y1: Infinity,
  2444        x2: -Infinity,
  2445        y2: -Infinity,
  2446        w: 0,
  2447        h: 0
  2448      };
  2449    } else if (bb.x1 != null && bb.y1 != null) {
  2450      if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
  2451        return {
  2452          x1: bb.x1,
  2453          y1: bb.y1,
  2454          x2: bb.x2,
  2455          y2: bb.y2,
  2456          w: bb.x2 - bb.x1,
  2457          h: bb.y2 - bb.y1
  2458        };
  2459      } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
  2460        return {
  2461          x1: bb.x1,
  2462          y1: bb.y1,
  2463          x2: bb.x1 + bb.w,
  2464          y2: bb.y1 + bb.h,
  2465          w: bb.w,
  2466          h: bb.h
  2467        };
  2468      }
  2469    }
  2470  };
  2471  var copyBoundingBox = function copyBoundingBox(bb) {
  2472    return {
  2473      x1: bb.x1,
  2474      x2: bb.x2,
  2475      w: bb.w,
  2476      y1: bb.y1,
  2477      y2: bb.y2,
  2478      h: bb.h
  2479    };
  2480  };
  2481  var clearBoundingBox = function clearBoundingBox(bb) {
  2482    bb.x1 = Infinity;
  2483    bb.y1 = Infinity;
  2484    bb.x2 = -Infinity;
  2485    bb.y2 = -Infinity;
  2486    bb.w = 0;
  2487    bb.h = 0;
  2488  };
  2489  var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
  2490    // update bb1 with bb2 bounds
  2491    bb1.x1 = Math.min(bb1.x1, bb2.x1);
  2492    bb1.x2 = Math.max(bb1.x2, bb2.x2);
  2493    bb1.w = bb1.x2 - bb1.x1;
  2494    bb1.y1 = Math.min(bb1.y1, bb2.y1);
  2495    bb1.y2 = Math.max(bb1.y2, bb2.y2);
  2496    bb1.h = bb1.y2 - bb1.y1;
  2497  };
  2498  var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
  2499    bb.x1 = Math.min(bb.x1, x);
  2500    bb.x2 = Math.max(bb.x2, x);
  2501    bb.w = bb.x2 - bb.x1;
  2502    bb.y1 = Math.min(bb.y1, y);
  2503    bb.y2 = Math.max(bb.y2, y);
  2504    bb.h = bb.y2 - bb.y1;
  2505  };
  2506  var expandBoundingBox = function expandBoundingBox(bb) {
  2507    var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  2508    bb.x1 -= padding;
  2509    bb.x2 += padding;
  2510    bb.y1 -= padding;
  2511    bb.y2 += padding;
  2512    bb.w = bb.x2 - bb.x1;
  2513    bb.h = bb.y2 - bb.y1;
  2514    return bb;
  2515  };
  2516  var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
  2517    var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
  2518    var top, right, bottom, left;
  2519  
  2520    if (padding.length === 1) {
  2521      top = right = bottom = left = padding[0];
  2522    } else if (padding.length === 2) {
  2523      top = bottom = padding[0];
  2524      left = right = padding[1];
  2525    } else if (padding.length === 4) {
  2526      var _padding = _slicedToArray(padding, 4);
  2527  
  2528      top = _padding[0];
  2529      right = _padding[1];
  2530      bottom = _padding[2];
  2531      left = _padding[3];
  2532    }
  2533  
  2534    bb.x1 -= left;
  2535    bb.x2 += right;
  2536    bb.y1 -= top;
  2537    bb.y2 += bottom;
  2538    bb.w = bb.x2 - bb.x1;
  2539    bb.h = bb.y2 - bb.y1;
  2540    return bb;
  2541  };
  2542  
  2543  var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
  2544    bb1.x1 = bb2.x1;
  2545    bb1.y1 = bb2.y1;
  2546    bb1.x2 = bb2.x2;
  2547    bb1.y2 = bb2.y2;
  2548    bb1.w = bb1.x2 - bb1.x1;
  2549    bb1.h = bb1.y2 - bb1.y1;
  2550  };
  2551  var assignShiftToBoundingBox = function assignShiftToBoundingBox(bb, delta) {
  2552    bb.x1 += delta.x;
  2553    bb.x2 += delta.x;
  2554    bb.y1 += delta.y;
  2555    bb.y2 += delta.y;
  2556  };
  2557  var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
  2558    // case: one bb to right of other
  2559    if (bb1.x1 > bb2.x2) {
  2560      return false;
  2561    }
  2562  
  2563    if (bb2.x1 > bb1.x2) {
  2564      return false;
  2565    } // case: one bb to left of other
  2566  
  2567  
  2568    if (bb1.x2 < bb2.x1) {
  2569      return false;
  2570    }
  2571  
  2572    if (bb2.x2 < bb1.x1) {
  2573      return false;
  2574    } // case: one bb above other
  2575  
  2576  
  2577    if (bb1.y2 < bb2.y1) {
  2578      return false;
  2579    }
  2580  
  2581    if (bb2.y2 < bb1.y1) {
  2582      return false;
  2583    } // case: one bb below other
  2584  
  2585  
  2586    if (bb1.y1 > bb2.y2) {
  2587      return false;
  2588    }
  2589  
  2590    if (bb2.y1 > bb1.y2) {
  2591      return false;
  2592    } // otherwise, must have some overlap
  2593  
  2594  
  2595    return true;
  2596  };
  2597  var inBoundingBox = function inBoundingBox(bb, x, y) {
  2598    return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
  2599  };
  2600  var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
  2601    return inBoundingBox(bb, pt.x, pt.y);
  2602  };
  2603  var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
  2604    return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
  2605  };
  2606  var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
  2607    var cornerRadius = getRoundRectangleRadius(width, height);
  2608    var halfWidth = width / 2;
  2609    var halfHeight = height / 2; // Check intersections with straight line segments
  2610  
  2611    var straightLineIntersections; // Top segment, left to right
  2612  
  2613    {
  2614      var topStartX = nodeX - halfWidth + cornerRadius - padding;
  2615      var topStartY = nodeY - halfHeight - padding;
  2616      var topEndX = nodeX + halfWidth - cornerRadius + padding;
  2617      var topEndY = topStartY;
  2618      straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
  2619  
  2620      if (straightLineIntersections.length > 0) {
  2621        return straightLineIntersections;
  2622      }
  2623    } // Right segment, top to bottom
  2624  
  2625    {
  2626      var rightStartX = nodeX + halfWidth + padding;
  2627      var rightStartY = nodeY - halfHeight + cornerRadius - padding;
  2628      var rightEndX = rightStartX;
  2629      var rightEndY = nodeY + halfHeight - cornerRadius + padding;
  2630      straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
  2631  
  2632      if (straightLineIntersections.length > 0) {
  2633        return straightLineIntersections;
  2634      }
  2635    } // Bottom segment, left to right
  2636  
  2637    {
  2638      var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
  2639      var bottomStartY = nodeY + halfHeight + padding;
  2640      var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
  2641      var bottomEndY = bottomStartY;
  2642      straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
  2643  
  2644      if (straightLineIntersections.length > 0) {
  2645        return straightLineIntersections;
  2646      }
  2647    } // Left segment, top to bottom
  2648  
  2649    {
  2650      var leftStartX = nodeX - halfWidth - padding;
  2651      var leftStartY = nodeY - halfHeight + cornerRadius - padding;
  2652      var leftEndX = leftStartX;
  2653      var leftEndY = nodeY + halfHeight - cornerRadius + padding;
  2654      straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
  2655  
  2656      if (straightLineIntersections.length > 0) {
  2657        return straightLineIntersections;
  2658      }
  2659    } // Check intersections with arc segments
  2660  
  2661    var arcIntersections; // Top Left
  2662  
  2663    {
  2664      var topLeftCenterX = nodeX - halfWidth + cornerRadius;
  2665      var topLeftCenterY = nodeY - halfHeight + cornerRadius;
  2666      arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
  2667  
  2668      if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
  2669        return [arcIntersections[0], arcIntersections[1]];
  2670      }
  2671    } // Top Right
  2672  
  2673    {
  2674      var topRightCenterX = nodeX + halfWidth - cornerRadius;
  2675      var topRightCenterY = nodeY - halfHeight + cornerRadius;
  2676      arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
  2677  
  2678      if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
  2679        return [arcIntersections[0], arcIntersections[1]];
  2680      }
  2681    } // Bottom Right
  2682  
  2683    {
  2684      var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
  2685      var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
  2686      arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
  2687  
  2688      if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
  2689        return [arcIntersections[0], arcIntersections[1]];
  2690      }
  2691    } // Bottom Left
  2692  
  2693    {
  2694      var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
  2695      var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
  2696      arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
  2697  
  2698      if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
  2699        return [arcIntersections[0], arcIntersections[1]];
  2700      }
  2701    }
  2702    return []; // if nothing
  2703  };
  2704  var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
  2705    var t = tolerance;
  2706    var x1 = Math.min(lx1, lx2);
  2707    var x2 = Math.max(lx1, lx2);
  2708    var y1 = Math.min(ly1, ly2);
  2709    var y2 = Math.max(ly1, ly2);
  2710    return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
  2711  };
  2712  var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
  2713    var bb = {
  2714      x1: Math.min(x1, x3, x2) - tolerance,
  2715      x2: Math.max(x1, x3, x2) + tolerance,
  2716      y1: Math.min(y1, y3, y2) - tolerance,
  2717      y2: Math.max(y1, y3, y2) + tolerance
  2718    }; // if outside the rough bounding box for the bezier, then it can't be a hit
  2719  
  2720    if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
  2721      // console.log('bezier out of rough bb')
  2722      return false;
  2723    } else {
  2724      // console.log('do more expensive check');
  2725      return true;
  2726    }
  2727  };
  2728  var solveQuadratic = function solveQuadratic(a, b, c, val) {
  2729    c -= val;
  2730    var r = b * b - 4 * a * c;
  2731  
  2732    if (r < 0) {
  2733      return [];
  2734    }
  2735  
  2736    var sqrtR = Math.sqrt(r);
  2737    var denom = 2 * a;
  2738    var root1 = (-b + sqrtR) / denom;
  2739    var root2 = (-b - sqrtR) / denom;
  2740    return [root1, root2];
  2741  };
  2742  var solveCubic = function solveCubic(a, b, c, d, result) {
  2743    // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
  2744    // r is the real component, i is the imaginary component
  2745    // An implementation of the Cardano method from the year 1545
  2746    // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
  2747    var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
  2748  
  2749    if (a === 0) {
  2750      a = epsilon;
  2751    }
  2752  
  2753    b /= a;
  2754    c /= a;
  2755    d /= a;
  2756    var discriminant, q, r, dum1, s, t, term1, r13;
  2757    q = (3.0 * c - b * b) / 9.0;
  2758    r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
  2759    r /= 54.0;
  2760    discriminant = q * q * q + r * r;
  2761    result[1] = 0;
  2762    term1 = b / 3.0;
  2763  
  2764    if (discriminant > 0) {
  2765      s = r + Math.sqrt(discriminant);
  2766      s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
  2767      t = r - Math.sqrt(discriminant);
  2768      t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
  2769      result[0] = -term1 + s + t;
  2770      term1 += (s + t) / 2.0;
  2771      result[4] = result[2] = -term1;
  2772      term1 = Math.sqrt(3.0) * (-t + s) / 2;
  2773      result[3] = term1;
  2774      result[5] = -term1;
  2775      return;
  2776    }
  2777  
  2778    result[5] = result[3] = 0;
  2779  
  2780    if (discriminant === 0) {
  2781      r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
  2782      result[0] = -term1 + 2.0 * r13;
  2783      result[4] = result[2] = -(r13 + term1);
  2784      return;
  2785    }
  2786  
  2787    q = -q;
  2788    dum1 = q * q * q;
  2789    dum1 = Math.acos(r / Math.sqrt(dum1));
  2790    r13 = 2.0 * Math.sqrt(q);
  2791    result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
  2792    result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
  2793    result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
  2794    return;
  2795  };
  2796  var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
  2797    // Find minimum distance by using the minimum of the distance
  2798    // function between the given point and the curve
  2799    // This gives the coefficients of the resulting cubic equation
  2800    // whose roots tell us where a possible minimum is
  2801    // (Coefficients are divided by 4)
  2802    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;
  2803    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;
  2804    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;
  2805    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);
  2806  
  2807    var roots = []; // Use the cubic solving algorithm
  2808  
  2809    solveCubic(a, b, c, d, roots);
  2810    var zeroThreshold = 0.0000001;
  2811    var params = [];
  2812  
  2813    for (var index = 0; index < 6; index += 2) {
  2814      if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
  2815        params.push(roots[index]);
  2816      }
  2817    }
  2818  
  2819    params.push(1.0);
  2820    params.push(0.0);
  2821    var minDistanceSquared = -1;
  2822    var curX, curY, distSquared;
  2823  
  2824    for (var i = 0; i < params.length; i++) {
  2825      curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
  2826      curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
  2827      distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
  2828  
  2829      if (minDistanceSquared >= 0) {
  2830        if (distSquared < minDistanceSquared) {
  2831          minDistanceSquared = distSquared;
  2832        }
  2833      } else {
  2834        minDistanceSquared = distSquared;
  2835      }
  2836    }
  2837  
  2838    return minDistanceSquared;
  2839  };
  2840  var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
  2841    var offset = [x - x1, y - y1];
  2842    var line = [x2 - x1, y2 - y1];
  2843    var lineSq = line[0] * line[0] + line[1] * line[1];
  2844    var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
  2845    var dotProduct = offset[0] * line[0] + offset[1] * line[1];
  2846    var adjSq = dotProduct * dotProduct / lineSq;
  2847  
  2848    if (dotProduct < 0) {
  2849      return hypSq;
  2850    }
  2851  
  2852    if (adjSq > lineSq) {
  2853      return (x - x2) * (x - x2) + (y - y2) * (y - y2);
  2854    }
  2855  
  2856    return hypSq - adjSq;
  2857  };
  2858  var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
  2859    var x1, y1, x2, y2;
  2860    var y3; // Intersect with vertical line through (x, y)
  2861  
  2862    var up = 0; // let down = 0;
  2863  
  2864    for (var i = 0; i < points.length / 2; i++) {
  2865      x1 = points[i * 2];
  2866      y1 = points[i * 2 + 1];
  2867  
  2868      if (i + 1 < points.length / 2) {
  2869        x2 = points[(i + 1) * 2];
  2870        y2 = points[(i + 1) * 2 + 1];
  2871      } else {
  2872        x2 = points[(i + 1 - points.length / 2) * 2];
  2873        y2 = points[(i + 1 - points.length / 2) * 2 + 1];
  2874      }
  2875  
  2876      if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
  2877        y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
  2878  
  2879        if (y3 > y) {
  2880          up++;
  2881        } // if( y3 < y ){
  2882        // down++;
  2883        // }
  2884  
  2885      } else {
  2886        continue;
  2887      }
  2888    }
  2889  
  2890    if (up % 2 === 0) {
  2891      return false;
  2892    } else {
  2893      return true;
  2894    }
  2895  };
  2896  var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
  2897    var transformedPoints = new Array(basePoints.length); // Gives negative angle
  2898  
  2899    var angle;
  2900  
  2901    if (direction[0] != null) {
  2902      angle = Math.atan(direction[1] / direction[0]);
  2903  
  2904      if (direction[0] < 0) {
  2905        angle = angle + Math.PI / 2;
  2906      } else {
  2907        angle = -angle - Math.PI / 2;
  2908      }
  2909    } else {
  2910      angle = direction;
  2911    }
  2912  
  2913    var cos = Math.cos(-angle);
  2914    var sin = Math.sin(-angle); //    console.log("base: " + basePoints);
  2915  
  2916    for (var i = 0; i < transformedPoints.length / 2; i++) {
  2917      transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
  2918      transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
  2919      transformedPoints[i * 2] += centerX;
  2920      transformedPoints[i * 2 + 1] += centerY;
  2921    }
  2922  
  2923    var points;
  2924  
  2925    if (padding > 0) {
  2926      var expandedLineSet = expandPolygon(transformedPoints, -padding);
  2927      points = joinLines(expandedLineSet);
  2928    } else {
  2929      points = transformedPoints;
  2930    }
  2931  
  2932    return pointInsidePolygonPoints(x, y, points);
  2933  };
  2934  var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
  2935    var cutPolygonPoints = new Array(basePoints.length);
  2936    var halfW = width / 2;
  2937    var halfH = height / 2;
  2938    var cornerRadius = getRoundPolygonRadius(width, height);
  2939    var squaredCornerRadius = cornerRadius * cornerRadius;
  2940  
  2941    for (var i = 0; i < basePoints.length / 4; i++) {
  2942      var sourceUv = void 0,
  2943          destUv = void 0;
  2944  
  2945      if (i === 0) {
  2946        sourceUv = basePoints.length - 2;
  2947      } else {
  2948        sourceUv = i * 4 - 2;
  2949      }
  2950  
  2951      destUv = i * 4 + 2;
  2952      var px = centerX + halfW * basePoints[i * 4];
  2953      var py = centerY + halfH * basePoints[i * 4 + 1];
  2954      var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
  2955      var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
  2956      var cp0x = px - offset * basePoints[sourceUv];
  2957      var cp0y = py - offset * basePoints[sourceUv + 1];
  2958      var cp1x = px + offset * basePoints[destUv];
  2959      var cp1y = py + offset * basePoints[destUv + 1];
  2960      cutPolygonPoints[i * 4] = cp0x;
  2961      cutPolygonPoints[i * 4 + 1] = cp0y;
  2962      cutPolygonPoints[i * 4 + 2] = cp1x;
  2963      cutPolygonPoints[i * 4 + 3] = cp1y;
  2964      var orthx = basePoints[sourceUv + 1];
  2965      var orthy = -basePoints[sourceUv];
  2966      var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
  2967  
  2968      if (cosAlpha < 0) {
  2969        orthx *= -1;
  2970        orthy *= -1;
  2971      }
  2972  
  2973      var cx = cp0x + orthx * cornerRadius;
  2974      var cy = cp0y + orthy * cornerRadius;
  2975      var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
  2976  
  2977      if (squaredDistance <= squaredCornerRadius) {
  2978        return true;
  2979      }
  2980    }
  2981  
  2982    return pointInsidePolygonPoints(x, y, cutPolygonPoints);
  2983  };
  2984  var joinLines = function joinLines(lineSet) {
  2985    var vertices = new Array(lineSet.length / 2);
  2986    var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
  2987    var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
  2988  
  2989    for (var i = 0; i < lineSet.length / 4; i++) {
  2990      currentLineStartX = lineSet[i * 4];
  2991      currentLineStartY = lineSet[i * 4 + 1];
  2992      currentLineEndX = lineSet[i * 4 + 2];
  2993      currentLineEndY = lineSet[i * 4 + 3];
  2994  
  2995      if (i < lineSet.length / 4 - 1) {
  2996        nextLineStartX = lineSet[(i + 1) * 4];
  2997        nextLineStartY = lineSet[(i + 1) * 4 + 1];
  2998        nextLineEndX = lineSet[(i + 1) * 4 + 2];
  2999        nextLineEndY = lineSet[(i + 1) * 4 + 3];
  3000      } else {
  3001        nextLineStartX = lineSet[0];
  3002        nextLineStartY = lineSet[1];
  3003        nextLineEndX = lineSet[2];
  3004        nextLineEndY = lineSet[3];
  3005      }
  3006  
  3007      var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
  3008      vertices[i * 2] = intersection[0];
  3009      vertices[i * 2 + 1] = intersection[1];
  3010    }
  3011  
  3012    return vertices;
  3013  };
  3014  var expandPolygon = function expandPolygon(points, pad) {
  3015    var expandedLineSet = new Array(points.length * 2);
  3016    var currentPointX, currentPointY, nextPointX, nextPointY;
  3017  
  3018    for (var i = 0; i < points.length / 2; i++) {
  3019      currentPointX = points[i * 2];
  3020      currentPointY = points[i * 2 + 1];
  3021  
  3022      if (i < points.length / 2 - 1) {
  3023        nextPointX = points[(i + 1) * 2];
  3024        nextPointY = points[(i + 1) * 2 + 1];
  3025      } else {
  3026        nextPointX = points[0];
  3027        nextPointY = points[1];
  3028      } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
  3029      // Assume CCW polygon winding
  3030  
  3031  
  3032      var offsetX = nextPointY - currentPointY;
  3033      var offsetY = -(nextPointX - currentPointX); // Normalize
  3034  
  3035      var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
  3036      var normalizedOffsetX = offsetX / offsetLength;
  3037      var normalizedOffsetY = offsetY / offsetLength;
  3038      expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
  3039      expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
  3040      expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
  3041      expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
  3042    }
  3043  
  3044    return expandedLineSet;
  3045  };
  3046  var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
  3047    var dispX = centerX - x;
  3048    var dispY = centerY - y;
  3049    dispX /= ellipseWradius;
  3050    dispY /= ellipseHradius;
  3051    var len = Math.sqrt(dispX * dispX + dispY * dispY);
  3052    var newLength = len - 1;
  3053  
  3054    if (newLength < 0) {
  3055      return [];
  3056    }
  3057  
  3058    var lenProportion = newLength / len;
  3059    return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
  3060  };
  3061  var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
  3062    x -= centerX;
  3063    y -= centerY;
  3064    x /= width / 2 + padding;
  3065    y /= height / 2 + padding;
  3066    return x * x + y * y <= 1;
  3067  }; // Returns intersections of increasing distance from line's start point
  3068  
  3069  var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
  3070    // Calculate d, direction vector of line
  3071    var d = [x2 - x1, y2 - y1]; // Direction vector of line
  3072  
  3073    var f = [x1 - centerX, y1 - centerY];
  3074    var a = d[0] * d[0] + d[1] * d[1];
  3075    var b = 2 * (f[0] * d[0] + f[1] * d[1]);
  3076    var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
  3077    var discriminant = b * b - 4 * a * c;
  3078  
  3079    if (discriminant < 0) {
  3080      return [];
  3081    }
  3082  
  3083    var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
  3084    var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
  3085    var tMin = Math.min(t1, t2);
  3086    var tMax = Math.max(t1, t2);
  3087    var inRangeParams = [];
  3088  
  3089    if (tMin >= 0 && tMin <= 1) {
  3090      inRangeParams.push(tMin);
  3091    }
  3092  
  3093    if (tMax >= 0 && tMax <= 1) {
  3094      inRangeParams.push(tMax);
  3095    }
  3096  
  3097    if (inRangeParams.length === 0) {
  3098      return [];
  3099    }
  3100  
  3101    var nearIntersectionX = inRangeParams[0] * d[0] + x1;
  3102    var nearIntersectionY = inRangeParams[0] * d[1] + y1;
  3103  
  3104    if (inRangeParams.length > 1) {
  3105      if (inRangeParams[0] == inRangeParams[1]) {
  3106        return [nearIntersectionX, nearIntersectionY];
  3107      } else {
  3108        var farIntersectionX = inRangeParams[1] * d[0] + x1;
  3109        var farIntersectionY = inRangeParams[1] * d[1] + y1;
  3110        return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
  3111      }
  3112    } else {
  3113      return [nearIntersectionX, nearIntersectionY];
  3114    }
  3115  };
  3116  var midOfThree = function midOfThree(a, b, c) {
  3117    if (b <= a && a <= c || c <= a && a <= b) {
  3118      return a;
  3119    } else if (a <= b && b <= c || c <= b && b <= a) {
  3120      return b;
  3121    } else {
  3122      return c;
  3123    }
  3124  }; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
  3125  
  3126  var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
  3127    var dx13 = x1 - x3;
  3128    var dx21 = x2 - x1;
  3129    var dx43 = x4 - x3;
  3130    var dy13 = y1 - y3;
  3131    var dy21 = y2 - y1;
  3132    var dy43 = y4 - y3;
  3133    var ua_t = dx43 * dy13 - dy43 * dx13;
  3134    var ub_t = dx21 * dy13 - dy21 * dx13;
  3135    var u_b = dy43 * dx21 - dx43 * dy21;
  3136  
  3137    if (u_b !== 0) {
  3138      var ua = ua_t / u_b;
  3139      var ub = ub_t / u_b;
  3140      var flptThreshold = 0.001;
  3141  
  3142      var _min = 0 - flptThreshold;
  3143  
  3144      var _max = 1 + flptThreshold;
  3145  
  3146      if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
  3147        return [x1 + ua * dx21, y1 + ua * dy21];
  3148      } else {
  3149        if (!infiniteLines) {
  3150          return [];
  3151        } else {
  3152          return [x1 + ua * dx21, y1 + ua * dy21];
  3153        }
  3154      }
  3155    } else {
  3156      if (ua_t === 0 || ub_t === 0) {
  3157        // Parallel, coincident lines. Check if overlap
  3158        // Check endpoint of second line
  3159        if (midOfThree(x1, x2, x4) === x4) {
  3160          return [x4, y4];
  3161        } // Check start point of second line
  3162  
  3163  
  3164        if (midOfThree(x1, x2, x3) === x3) {
  3165          return [x3, y3];
  3166        } // Endpoint of first line
  3167  
  3168  
  3169        if (midOfThree(x3, x4, x2) === x2) {
  3170          return [x2, y2];
  3171        }
  3172  
  3173        return [];
  3174      } else {
  3175        // Parallel, non-coincident
  3176        return [];
  3177      }
  3178    }
  3179  }; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
  3180  // intersect a node polygon (pts transformed)
  3181  //
  3182  // math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
  3183  // intersect the points (no transform)
  3184  
  3185  var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
  3186    var intersections = [];
  3187    var intersection;
  3188    var transformedPoints = new Array(basePoints.length);
  3189    var doTransform = true;
  3190  
  3191    if (width == null) {
  3192      doTransform = false;
  3193    }
  3194  
  3195    var points;
  3196  
  3197    if (doTransform) {
  3198      for (var i = 0; i < transformedPoints.length / 2; i++) {
  3199        transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
  3200        transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
  3201      }
  3202  
  3203      if (padding > 0) {
  3204        var expandedLineSet = expandPolygon(transformedPoints, -padding);
  3205        points = joinLines(expandedLineSet);
  3206      } else {
  3207        points = transformedPoints;
  3208      }
  3209    } else {
  3210      points = basePoints;
  3211    }
  3212  
  3213    var currentX, currentY, nextX, nextY;
  3214  
  3215    for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
  3216      currentX = points[_i2 * 2];
  3217      currentY = points[_i2 * 2 + 1];
  3218  
  3219      if (_i2 < points.length / 2 - 1) {
  3220        nextX = points[(_i2 + 1) * 2];
  3221        nextY = points[(_i2 + 1) * 2 + 1];
  3222      } else {
  3223        nextX = points[0];
  3224        nextY = points[1];
  3225      }
  3226  
  3227      intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
  3228  
  3229      if (intersection.length !== 0) {
  3230        intersections.push(intersection[0], intersection[1]);
  3231      }
  3232    }
  3233  
  3234    return intersections;
  3235  };
  3236  var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
  3237    var intersections = [];
  3238    var intersection;
  3239    var lines = new Array(basePoints.length);
  3240    var halfW = width / 2;
  3241    var halfH = height / 2;
  3242    var cornerRadius = getRoundPolygonRadius(width, height);
  3243  
  3244    for (var i = 0; i < basePoints.length / 4; i++) {
  3245      var sourceUv = void 0,
  3246          destUv = void 0;
  3247  
  3248      if (i === 0) {
  3249        sourceUv = basePoints.length - 2;
  3250      } else {
  3251        sourceUv = i * 4 - 2;
  3252      }
  3253  
  3254      destUv = i * 4 + 2;
  3255      var px = centerX + halfW * basePoints[i * 4];
  3256      var py = centerY + halfH * basePoints[i * 4 + 1];
  3257      var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
  3258      var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
  3259      var cp0x = px - offset * basePoints[sourceUv];
  3260      var cp0y = py - offset * basePoints[sourceUv + 1];
  3261      var cp1x = px + offset * basePoints[destUv];
  3262      var cp1y = py + offset * basePoints[destUv + 1];
  3263  
  3264      if (i === 0) {
  3265        lines[basePoints.length - 2] = cp0x;
  3266        lines[basePoints.length - 1] = cp0y;
  3267      } else {
  3268        lines[i * 4 - 2] = cp0x;
  3269        lines[i * 4 - 1] = cp0y;
  3270      }
  3271  
  3272      lines[i * 4] = cp1x;
  3273      lines[i * 4 + 1] = cp1y;
  3274      var orthx = basePoints[sourceUv + 1];
  3275      var orthy = -basePoints[sourceUv];
  3276      var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
  3277  
  3278      if (cosAlpha < 0) {
  3279        orthx *= -1;
  3280        orthy *= -1;
  3281      }
  3282  
  3283      var cx = cp0x + orthx * cornerRadius;
  3284      var cy = cp0y + orthy * cornerRadius;
  3285      intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
  3286  
  3287      if (intersection.length !== 0) {
  3288        intersections.push(intersection[0], intersection[1]);
  3289      }
  3290    }
  3291  
  3292    for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
  3293      intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
  3294  
  3295      if (intersection.length !== 0) {
  3296        intersections.push(intersection[0], intersection[1]);
  3297      }
  3298    }
  3299  
  3300    if (intersections.length > 2) {
  3301      var lowestIntersection = [intersections[0], intersections[1]];
  3302      var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
  3303  
  3304      for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
  3305        var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
  3306  
  3307        if (squaredDistance <= lowestSquaredDistance) {
  3308          lowestIntersection[0] = intersections[_i4 * 2];
  3309          lowestIntersection[1] = intersections[_i4 * 2 + 1];
  3310          lowestSquaredDistance = squaredDistance;
  3311        }
  3312      }
  3313  
  3314      return lowestIntersection;
  3315    }
  3316  
  3317    return intersections;
  3318  };
  3319  var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
  3320    var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
  3321    var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
  3322    var lenRatio = (length - amount) / length;
  3323  
  3324    if (lenRatio < 0) {
  3325      lenRatio = 0.00001;
  3326    }
  3327  
  3328    return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
  3329  };
  3330  var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
  3331    var points = generateUnitNgonPoints(sides, rotationRadians);
  3332    points = fitPolygonToSquare(points);
  3333    return points;
  3334  };
  3335  var fitPolygonToSquare = function fitPolygonToSquare(points) {
  3336    var x, y;
  3337    var sides = points.length / 2;
  3338    var minX = Infinity,
  3339        minY = Infinity,
  3340        maxX = -Infinity,
  3341        maxY = -Infinity;
  3342  
  3343    for (var i = 0; i < sides; i++) {
  3344      x = points[2 * i];
  3345      y = points[2 * i + 1];
  3346      minX = Math.min(minX, x);
  3347      maxX = Math.max(maxX, x);
  3348      minY = Math.min(minY, y);
  3349      maxY = Math.max(maxY, y);
  3350    } // stretch factors
  3351  
  3352  
  3353    var sx = 2 / (maxX - minX);
  3354    var sy = 2 / (maxY - minY);
  3355  
  3356    for (var _i5 = 0; _i5 < sides; _i5++) {
  3357      x = points[2 * _i5] = points[2 * _i5] * sx;
  3358      y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
  3359      minX = Math.min(minX, x);
  3360      maxX = Math.max(maxX, x);
  3361      minY = Math.min(minY, y);
  3362      maxY = Math.max(maxY, y);
  3363    }
  3364  
  3365    if (minY < -1) {
  3366      for (var _i6 = 0; _i6 < sides; _i6++) {
  3367        y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
  3368      }
  3369    }
  3370  
  3371    return points;
  3372  };
  3373  var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
  3374    var increment = 1.0 / sides * 2 * Math.PI;
  3375    var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
  3376    startAngle += rotationRadians;
  3377    var points = new Array(sides * 2);
  3378    var currentAngle;
  3379  
  3380    for (var i = 0; i < sides; i++) {
  3381      currentAngle = i * increment + startAngle;
  3382      points[2 * i] = Math.cos(currentAngle); // x
  3383  
  3384      points[2 * i + 1] = Math.sin(-currentAngle); // y
  3385    }
  3386  
  3387    return points;
  3388  }; // Set the default radius, unless half of width or height is smaller than default
  3389  
  3390  var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
  3391    return Math.min(width / 4, height / 4, 8);
  3392  }; // Set the default radius
  3393  
  3394  var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
  3395    return Math.min(width / 10, height / 10, 8);
  3396  };
  3397  var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
  3398    return 8;
  3399  };
  3400  var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
  3401    return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
  3402  }; // get curve width, height, and control point position offsets as a percentage of node height / width
  3403  
  3404  var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
  3405    return {
  3406      heightOffset: Math.min(15, 0.05 * height),
  3407      widthOffset: Math.min(100, 0.25 * width),
  3408      ctrlPtOffsetPct: 0.05
  3409    };
  3410  };
  3411  
  3412  var pageRankDefaults = defaults({
  3413    dampingFactor: 0.8,
  3414    precision: 0.000001,
  3415    iterations: 200,
  3416    weight: function weight(edge) {
  3417      return 1;
  3418    }
  3419  });
  3420  var elesfn$7 = {
  3421    pageRank: function pageRank(options) {
  3422      var _pageRankDefaults = pageRankDefaults(options),
  3423          dampingFactor = _pageRankDefaults.dampingFactor,
  3424          precision = _pageRankDefaults.precision,
  3425          iterations = _pageRankDefaults.iterations,
  3426          weight = _pageRankDefaults.weight;
  3427  
  3428      var cy = this._private.cy;
  3429  
  3430      var _this$byGroup = this.byGroup(),
  3431          nodes = _this$byGroup.nodes,
  3432          edges = _this$byGroup.edges;
  3433  
  3434      var numNodes = nodes.length;
  3435      var numNodesSqd = numNodes * numNodes;
  3436      var numEdges = edges.length; // Construct transposed adjacency matrix
  3437      // First lets have a zeroed matrix of the right size
  3438      // We'll also keep track of the sum of each column
  3439  
  3440      var matrix = new Array(numNodesSqd);
  3441      var columnSum = new Array(numNodes);
  3442      var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
  3443  
  3444      for (var i = 0; i < numNodes; i++) {
  3445        for (var j = 0; j < numNodes; j++) {
  3446          var n = i * numNodes + j;
  3447          matrix[n] = 0;
  3448        }
  3449  
  3450        columnSum[i] = 0;
  3451      } // Now, process edges
  3452  
  3453  
  3454      for (var _i = 0; _i < numEdges; _i++) {
  3455        var edge = edges[_i];
  3456        var srcId = edge.data('source');
  3457        var tgtId = edge.data('target'); // Don't include loops in the matrix
  3458  
  3459        if (srcId === tgtId) {
  3460          continue;
  3461        }
  3462  
  3463        var s = nodes.indexOfId(srcId);
  3464        var t = nodes.indexOfId(tgtId);
  3465        var w = weight(edge);
  3466  
  3467        var _n = t * numNodes + s; // Update matrix
  3468  
  3469  
  3470        matrix[_n] += w; // Update column sum
  3471  
  3472        columnSum[s] += w;
  3473      } // Add additional probability based on damping factor
  3474      // Also, take into account columns that have sum = 0
  3475  
  3476  
  3477      var p = 1.0 / numNodes + additionalProb; // Shorthand
  3478      // Traverse matrix, column by column
  3479  
  3480      for (var _j = 0; _j < numNodes; _j++) {
  3481        if (columnSum[_j] === 0) {
  3482          // No 'links' out from node jth, assume equal probability for each possible node
  3483          for (var _i2 = 0; _i2 < numNodes; _i2++) {
  3484            var _n2 = _i2 * numNodes + _j;
  3485  
  3486            matrix[_n2] = p;
  3487          }
  3488        } else {
  3489          // Node jth has outgoing link, compute normalized probabilities
  3490          for (var _i3 = 0; _i3 < numNodes; _i3++) {
  3491            var _n3 = _i3 * numNodes + _j;
  3492  
  3493            matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
  3494          }
  3495        }
  3496      } // Compute dominant eigenvector using power method
  3497  
  3498  
  3499      var eigenvector = new Array(numNodes);
  3500      var temp = new Array(numNodes);
  3501      var previous; // Start with a vector of all 1's
  3502      // Also, initialize a null vector which will be used as shorthand
  3503  
  3504      for (var _i4 = 0; _i4 < numNodes; _i4++) {
  3505        eigenvector[_i4] = 1;
  3506      }
  3507  
  3508      for (var iter = 0; iter < iterations; iter++) {
  3509        // Temp array with all 0's
  3510        for (var _i5 = 0; _i5 < numNodes; _i5++) {
  3511          temp[_i5] = 0;
  3512        } // Multiply matrix with previous result
  3513  
  3514  
  3515        for (var _i6 = 0; _i6 < numNodes; _i6++) {
  3516          for (var _j2 = 0; _j2 < numNodes; _j2++) {
  3517            var _n4 = _i6 * numNodes + _j2;
  3518  
  3519            temp[_i6] += matrix[_n4] * eigenvector[_j2];
  3520          }
  3521        }
  3522  
  3523        inPlaceSumNormalize(temp);
  3524        previous = eigenvector;
  3525        eigenvector = temp;
  3526        temp = previous;
  3527        var diff = 0; // Compute difference (squared module) of both vectors
  3528  
  3529        for (var _i7 = 0; _i7 < numNodes; _i7++) {
  3530          var delta = previous[_i7] - eigenvector[_i7];
  3531          diff += delta * delta;
  3532        } // If difference is less than the desired threshold, stop iterating
  3533  
  3534  
  3535        if (diff < precision) {
  3536          break;
  3537        }
  3538      } // Construct result
  3539  
  3540  
  3541      var res = {
  3542        rank: function rank(node) {
  3543          node = cy.collection(node)[0];
  3544          return eigenvector[nodes.indexOf(node)];
  3545        }
  3546      };
  3547      return res;
  3548    } // pageRank
  3549  
  3550  }; // elesfn
  3551  
  3552  var defaults$1 = defaults({
  3553    root: null,
  3554    weight: function weight(edge) {
  3555      return 1;
  3556    },
  3557    directed: false,
  3558    alpha: 0
  3559  });
  3560  var elesfn$8 = {
  3561    degreeCentralityNormalized: function degreeCentralityNormalized(options) {
  3562      options = defaults$1(options);
  3563      var cy = this.cy();
  3564      var nodes = this.nodes();
  3565      var numNodes = nodes.length;
  3566  
  3567      if (!options.directed) {
  3568        var degrees = {};
  3569        var maxDegree = 0;
  3570  
  3571        for (var i = 0; i < numNodes; i++) {
  3572          var node = nodes[i]; // add current node to the current options object and call degreeCentrality
  3573  
  3574          options.root = node;
  3575          var currDegree = this.degreeCentrality(options);
  3576  
  3577          if (maxDegree < currDegree.degree) {
  3578            maxDegree = currDegree.degree;
  3579          }
  3580  
  3581          degrees[node.id()] = currDegree.degree;
  3582        }
  3583  
  3584        return {
  3585          degree: function degree(node) {
  3586            if (maxDegree === 0) {
  3587              return 0;
  3588            }
  3589  
  3590            if (string(node)) {
  3591              // from is a selector string
  3592              node = cy.filter(node);
  3593            }
  3594  
  3595            return degrees[node.id()] / maxDegree;
  3596          }
  3597        };
  3598      } else {
  3599        var indegrees = {};
  3600        var outdegrees = {};
  3601        var maxIndegree = 0;
  3602        var maxOutdegree = 0;
  3603  
  3604        for (var _i = 0; _i < numNodes; _i++) {
  3605          var _node = nodes[_i];
  3606  
  3607          var id = _node.id(); // add current node to the current options object and call degreeCentrality
  3608  
  3609  
  3610          options.root = _node;
  3611  
  3612          var _currDegree = this.degreeCentrality(options);
  3613  
  3614          if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
  3615          if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
  3616          indegrees[id] = _currDegree.indegree;
  3617          outdegrees[id] = _currDegree.outdegree;
  3618        }
  3619  
  3620        return {
  3621          indegree: function indegree(node) {
  3622            if (maxIndegree == 0) {
  3623              return 0;
  3624            }
  3625  
  3626            if (string(node)) {
  3627              // from is a selector string
  3628              node = cy.filter(node);
  3629            }
  3630  
  3631            return indegrees[node.id()] / maxIndegree;
  3632          },
  3633          outdegree: function outdegree(node) {
  3634            if (maxOutdegree === 0) {
  3635              return 0;
  3636            }
  3637  
  3638            if (string(node)) {
  3639              // from is a selector string
  3640              node = cy.filter(node);
  3641            }
  3642  
  3643            return outdegrees[node.id()] / maxOutdegree;
  3644          }
  3645        };
  3646      }
  3647    },
  3648    // degreeCentralityNormalized
  3649    // Implemented from the algorithm in Opsahl's paper
  3650    // "Node centrality in weighted networks: Generalizing degree and shortest paths"
  3651    // check the heading 2 "Degree"
  3652    degreeCentrality: function degreeCentrality(options) {
  3653      options = defaults$1(options);
  3654      var cy = this.cy();
  3655      var callingEles = this;
  3656      var _options = options,
  3657          root = _options.root,
  3658          weight = _options.weight,
  3659          directed = _options.directed,
  3660          alpha = _options.alpha;
  3661      root = cy.collection(root)[0];
  3662  
  3663      if (!directed) {
  3664        var connEdges = root.connectedEdges().intersection(callingEles);
  3665        var k = connEdges.length;
  3666        var s = 0; // Now, sum edge weights
  3667  
  3668        for (var i = 0; i < connEdges.length; i++) {
  3669          s += weight(connEdges[i]);
  3670        }
  3671  
  3672        return {
  3673          degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
  3674        };
  3675      } else {
  3676        var edges = root.connectedEdges();
  3677        var incoming = edges.filter(function (edge) {
  3678          return edge.target().same(root) && callingEles.has(edge);
  3679        });
  3680        var outgoing = edges.filter(function (edge) {
  3681          return edge.source().same(root) && callingEles.has(edge);
  3682        });
  3683        var k_in = incoming.length;
  3684        var k_out = outgoing.length;
  3685        var s_in = 0;
  3686        var s_out = 0; // Now, sum incoming edge weights
  3687  
  3688        for (var _i2 = 0; _i2 < incoming.length; _i2++) {
  3689          s_in += weight(incoming[_i2]);
  3690        } // Now, sum outgoing edge weights
  3691  
  3692  
  3693        for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
  3694          s_out += weight(outgoing[_i3]);
  3695        }
  3696  
  3697        return {
  3698          indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
  3699          outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
  3700        };
  3701      }
  3702    } // degreeCentrality
  3703  
  3704  }; // elesfn
  3705  // nice, short mathemathical alias
  3706  
  3707  elesfn$8.dc = elesfn$8.degreeCentrality;
  3708  elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
  3709  
  3710  var defaults$2 = defaults({
  3711    harmonic: true,
  3712    weight: function weight() {
  3713      return 1;
  3714    },
  3715    directed: false,
  3716    root: null
  3717  });
  3718  var elesfn$9 = {
  3719    closenessCentralityNormalized: function closenessCentralityNormalized(options) {
  3720      var _defaults = defaults$2(options),
  3721          harmonic = _defaults.harmonic,
  3722          weight = _defaults.weight,
  3723          directed = _defaults.directed;
  3724  
  3725      var cy = this.cy();
  3726      var closenesses = {};
  3727      var maxCloseness = 0;
  3728      var nodes = this.nodes();
  3729      var fw = this.floydWarshall({
  3730        weight: weight,
  3731        directed: directed
  3732      }); // Compute closeness for every node and find the maximum closeness
  3733  
  3734      for (var i = 0; i < nodes.length; i++) {
  3735        var currCloseness = 0;
  3736        var node_i = nodes[i];
  3737  
  3738        for (var j = 0; j < nodes.length; j++) {
  3739          if (i !== j) {
  3740            var d = fw.distance(node_i, nodes[j]);
  3741  
  3742            if (harmonic) {
  3743              currCloseness += 1 / d;
  3744            } else {
  3745              currCloseness += d;
  3746            }
  3747          }
  3748        }
  3749  
  3750        if (!harmonic) {
  3751          currCloseness = 1 / currCloseness;
  3752        }
  3753  
  3754        if (maxCloseness < currCloseness) {
  3755          maxCloseness = currCloseness;
  3756        }
  3757  
  3758        closenesses[node_i.id()] = currCloseness;
  3759      }
  3760  
  3761      return {
  3762        closeness: function closeness(node) {
  3763          if (maxCloseness == 0) {
  3764            return 0;
  3765          }
  3766  
  3767          if (string(node)) {
  3768            // from is a selector string
  3769            node = cy.filter(node)[0].id();
  3770          } else {
  3771            // from is a node
  3772            node = node.id();
  3773          }
  3774  
  3775          return closenesses[node] / maxCloseness;
  3776        }
  3777      };
  3778    },
  3779    // Implemented from pseudocode from wikipedia
  3780    closenessCentrality: function closenessCentrality(options) {
  3781      var _defaults2 = defaults$2(options),
  3782          root = _defaults2.root,
  3783          weight = _defaults2.weight,
  3784          directed = _defaults2.directed,
  3785          harmonic = _defaults2.harmonic;
  3786  
  3787      root = this.filter(root)[0]; // we need distance from this node to every other node
  3788  
  3789      var dijkstra = this.dijkstra({
  3790        root: root,
  3791        weight: weight,
  3792        directed: directed
  3793      });
  3794      var totalDistance = 0;
  3795      var nodes = this.nodes();
  3796  
  3797      for (var i = 0; i < nodes.length; i++) {
  3798        var n = nodes[i];
  3799  
  3800        if (!n.same(root)) {
  3801          var d = dijkstra.distanceTo(n);
  3802  
  3803          if (harmonic) {
  3804            totalDistance += 1 / d;
  3805          } else {
  3806            totalDistance += d;
  3807          }
  3808        }
  3809      }
  3810  
  3811      return harmonic ? totalDistance : 1 / totalDistance;
  3812    } // closenessCentrality
  3813  
  3814  }; // elesfn
  3815  // nice, short mathemathical alias
  3816  
  3817  elesfn$9.cc = elesfn$9.closenessCentrality;
  3818  elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
  3819  
  3820  var defaults$3 = defaults({
  3821    weight: null,
  3822    directed: false
  3823  });
  3824  var elesfn$a = {
  3825    // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
  3826    betweennessCentrality: function betweennessCentrality(options) {
  3827      var _defaults = defaults$3(options),
  3828          directed = _defaults.directed,
  3829          weight = _defaults.weight;
  3830  
  3831      var weighted = weight != null;
  3832      var cy = this.cy(); // starting
  3833  
  3834      var V = this.nodes();
  3835      var A = {};
  3836      var _C = {};
  3837      var max = 0;
  3838      var C = {
  3839        set: function set(key, val) {
  3840          _C[key] = val;
  3841  
  3842          if (val > max) {
  3843            max = val;
  3844          }
  3845        },
  3846        get: function get(key) {
  3847          return _C[key];
  3848        }
  3849      }; // A contains the neighborhoods of every node
  3850  
  3851      for (var i = 0; i < V.length; i++) {
  3852        var v = V[i];
  3853        var vid = v.id();
  3854  
  3855        if (directed) {
  3856          A[vid] = v.outgoers().nodes(); // get outgoers of every node
  3857        } else {
  3858          A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
  3859        }
  3860  
  3861        C.set(vid, 0);
  3862      }
  3863  
  3864      var _loop = function _loop(s) {
  3865        var sid = V[s].id();
  3866        var S = []; // stack
  3867  
  3868        var P = {};
  3869        var g = {};
  3870        var d = {};
  3871        var Q = new Heap(function (a, b) {
  3872          return d[a] - d[b];
  3873        }); // queue
  3874        // init dictionaries
  3875  
  3876        for (var _i = 0; _i < V.length; _i++) {
  3877          var _vid = V[_i].id();
  3878  
  3879          P[_vid] = [];
  3880          g[_vid] = 0;
  3881          d[_vid] = Infinity;
  3882        }
  3883  
  3884        g[sid] = 1; // sigma
  3885  
  3886        d[sid] = 0; // distance to s
  3887  
  3888        Q.push(sid);
  3889  
  3890        while (!Q.empty()) {
  3891          var _v = Q.pop();
  3892  
  3893          S.push(_v);
  3894  
  3895          if (weighted) {
  3896            for (var j = 0; j < A[_v].length; j++) {
  3897              var w = A[_v][j];
  3898              var vEle = cy.getElementById(_v);
  3899              var edge = void 0;
  3900  
  3901              if (vEle.edgesTo(w).length > 0) {
  3902                edge = vEle.edgesTo(w)[0];
  3903              } else {
  3904                edge = w.edgesTo(vEle)[0];
  3905              }
  3906  
  3907              var edgeWeight = weight(edge);
  3908              w = w.id();
  3909  
  3910              if (d[w] > d[_v] + edgeWeight) {
  3911                d[w] = d[_v] + edgeWeight;
  3912  
  3913                if (Q.nodes.indexOf(w) < 0) {
  3914                  //if w is not in Q
  3915                  Q.push(w);
  3916                } else {
  3917                  // update position if w is in Q
  3918                  Q.updateItem(w);
  3919                }
  3920  
  3921                g[w] = 0;
  3922                P[w] = [];
  3923              }
  3924  
  3925              if (d[w] == d[_v] + edgeWeight) {
  3926                g[w] = g[w] + g[_v];
  3927                P[w].push(_v);
  3928              }
  3929            }
  3930          } else {
  3931            for (var _j = 0; _j < A[_v].length; _j++) {
  3932              var _w = A[_v][_j].id();
  3933  
  3934              if (d[_w] == Infinity) {
  3935                Q.push(_w);
  3936                d[_w] = d[_v] + 1;
  3937              }
  3938  
  3939              if (d[_w] == d[_v] + 1) {
  3940                g[_w] = g[_w] + g[_v];
  3941  
  3942                P[_w].push(_v);
  3943              }
  3944            }
  3945          }
  3946        }
  3947  
  3948        var e = {};
  3949  
  3950        for (var _i2 = 0; _i2 < V.length; _i2++) {
  3951          e[V[_i2].id()] = 0;
  3952        }
  3953  
  3954        while (S.length > 0) {
  3955          var _w2 = S.pop();
  3956  
  3957          for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
  3958            var _v2 = P[_w2][_j2];
  3959            e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
  3960  
  3961            if (_w2 != V[s].id()) {
  3962              C.set(_w2, C.get(_w2) + e[_w2]);
  3963            }
  3964          }
  3965        }
  3966      };
  3967  
  3968      for (var s = 0; s < V.length; s++) {
  3969        _loop(s);
  3970      }
  3971  
  3972      var ret = {
  3973        betweenness: function betweenness(node) {
  3974          var id = cy.collection(node).id();
  3975          return C.get(id);
  3976        },
  3977        betweennessNormalized: function betweennessNormalized(node) {
  3978          if (max == 0) {
  3979            return 0;
  3980          }
  3981  
  3982          var id = cy.collection(node).id();
  3983          return C.get(id) / max;
  3984        }
  3985      }; // alias
  3986  
  3987      ret.betweennessNormalised = ret.betweennessNormalized;
  3988      return ret;
  3989    } // betweennessCentrality
  3990  
  3991  }; // elesfn
  3992  // nice, short mathemathical alias
  3993  
  3994  elesfn$a.bc = elesfn$a.betweennessCentrality;
  3995  
  3996  // Implemented by Zoe Xi @zoexi for GSOC 2016
  3997  /* eslint-disable no-unused-vars */
  3998  
  3999  var defaults$4 = defaults({
  4000    expandFactor: 2,
  4001    // affects time of computation and cluster granularity to some extent: M * M
  4002    inflateFactor: 2,
  4003    // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
  4004    multFactor: 1,
  4005    // optional self loops for each node. Use a neutral value to improve cluster computations.
  4006    maxIterations: 20,
  4007    // maximum number of iterations of the MCL algorithm in a single run
  4008    attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
  4009    function (edge) {
  4010      return 1;
  4011    }]
  4012  });
  4013  /* eslint-enable */
  4014  
  4015  var setOptions = function setOptions(options) {
  4016    return defaults$4(options);
  4017  };
  4018  /* eslint-enable */
  4019  
  4020  
  4021  var getSimilarity = function getSimilarity(edge, attributes) {
  4022    var total = 0;
  4023  
  4024    for (var i = 0; i < attributes.length; i++) {
  4025      total += attributes[i](edge);
  4026    }
  4027  
  4028    return total;
  4029  };
  4030  
  4031  var addLoops = function addLoops(M, n, val) {
  4032    for (var i = 0; i < n; i++) {
  4033      M[i * n + i] = val;
  4034    }
  4035  };
  4036  
  4037  var normalize = function normalize(M, n) {
  4038    var sum;
  4039  
  4040    for (var col = 0; col < n; col++) {
  4041      sum = 0;
  4042  
  4043      for (var row = 0; row < n; row++) {
  4044        sum += M[row * n + col];
  4045      }
  4046  
  4047      for (var _row = 0; _row < n; _row++) {
  4048        M[_row * n + col] = M[_row * n + col] / sum;
  4049      }
  4050    }
  4051  }; // TODO: blocked matrix multiplication?
  4052  
  4053  
  4054  var mmult = function mmult(A, B, n) {
  4055    var C = new Array(n * n);
  4056  
  4057    for (var i = 0; i < n; i++) {
  4058      for (var j = 0; j < n; j++) {
  4059        C[i * n + j] = 0;
  4060      }
  4061  
  4062      for (var k = 0; k < n; k++) {
  4063        for (var _j = 0; _j < n; _j++) {
  4064          C[i * n + _j] += A[i * n + k] * B[k * n + _j];
  4065        }
  4066      }
  4067    }
  4068  
  4069    return C;
  4070  };
  4071  
  4072  var expand = function expand(M, n, expandFactor
  4073  /** power **/
  4074  ) {
  4075    var _M = M.slice(0);
  4076  
  4077    for (var p = 1; p < expandFactor; p++) {
  4078      M = mmult(M, _M, n);
  4079    }
  4080  
  4081    return M;
  4082  };
  4083  
  4084  var inflate = function inflate(M, n, inflateFactor
  4085  /** r **/
  4086  ) {
  4087    var _M = new Array(n * n); // M(i,j) ^ inflatePower
  4088  
  4089  
  4090    for (var i = 0; i < n * n; i++) {
  4091      _M[i] = Math.pow(M[i], inflateFactor);
  4092    }
  4093  
  4094    normalize(_M, n);
  4095    return _M;
  4096  };
  4097  
  4098  var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
  4099    // Check that both matrices have the same elements (i,j)
  4100    for (var i = 0; i < n2; i++) {
  4101      var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
  4102  
  4103      var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
  4104  
  4105      if (v1 !== v2) {
  4106        return false;
  4107      }
  4108    }
  4109  
  4110    return true;
  4111  };
  4112  
  4113  var assign = function assign(M, n, nodes, cy) {
  4114    var clusters = [];
  4115  
  4116    for (var i = 0; i < n; i++) {
  4117      var cluster = [];
  4118  
  4119      for (var j = 0; j < n; j++) {
  4120        // Row-wise attractors and elements that they attract belong in same cluster
  4121        if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
  4122          cluster.push(nodes[j]);
  4123        }
  4124      }
  4125  
  4126      if (cluster.length !== 0) {
  4127        clusters.push(cy.collection(cluster));
  4128      }
  4129    }
  4130  
  4131    return clusters;
  4132  };
  4133  
  4134  var isDuplicate = function isDuplicate(c1, c2) {
  4135    for (var i = 0; i < c1.length; i++) {
  4136      if (!c2[i] || c1[i].id() !== c2[i].id()) {
  4137        return false;
  4138      }
  4139    }
  4140  
  4141    return true;
  4142  };
  4143  
  4144  var removeDuplicates = function removeDuplicates(clusters) {
  4145    for (var i = 0; i < clusters.length; i++) {
  4146      for (var j = 0; j < clusters.length; j++) {
  4147        if (i != j && isDuplicate(clusters[i], clusters[j])) {
  4148          clusters.splice(j, 1);
  4149        }
  4150      }
  4151    }
  4152  
  4153    return clusters;
  4154  };
  4155  
  4156  var markovClustering = function markovClustering(options) {
  4157    var nodes = this.nodes();
  4158    var edges = this.edges();
  4159    var cy = this.cy(); // Set parameters of algorithm:
  4160  
  4161    var opts = setOptions(options); // Map each node to its position in node array
  4162  
  4163    var id2position = {};
  4164  
  4165    for (var i = 0; i < nodes.length; i++) {
  4166      id2position[nodes[i].id()] = i;
  4167    } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
  4168  
  4169  
  4170    var n = nodes.length,
  4171        n2 = n * n;
  4172  
  4173    var M = new Array(n2),
  4174        _M;
  4175  
  4176    for (var _i = 0; _i < n2; _i++) {
  4177      M[_i] = 0;
  4178    }
  4179  
  4180    for (var e = 0; e < edges.length; e++) {
  4181      var edge = edges[e];
  4182      var _i2 = id2position[edge.source().id()];
  4183      var j = id2position[edge.target().id()];
  4184      var sim = getSimilarity(edge, opts.attributes);
  4185      M[_i2 * n + j] += sim; // G should be symmetric and undirected
  4186  
  4187      M[j * n + _i2] += sim;
  4188    } // Begin Markov cluster algorithm
  4189    // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
  4190  
  4191  
  4192    addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
  4193  
  4194    normalize(M, n);
  4195    var isStillMoving = true;
  4196    var iterations = 0;
  4197  
  4198    while (isStillMoving && iterations < opts.maxIterations) {
  4199      isStillMoving = false; // Step 3:
  4200  
  4201      _M = expand(M, n, opts.expandFactor); // Step 4:
  4202  
  4203      M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
  4204  
  4205      if (!hasConverged(M, _M, n2, 4)) {
  4206        isStillMoving = true;
  4207      }
  4208  
  4209      iterations++;
  4210    } // Build clusters from matrix
  4211  
  4212  
  4213    var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
  4214  
  4215    clusters = removeDuplicates(clusters);
  4216    return clusters;
  4217  };
  4218  
  4219  var markovClustering$1 = {
  4220    markovClustering: markovClustering,
  4221    mcl: markovClustering
  4222  };
  4223  
  4224  // Common distance metrics for clustering algorithms
  4225  
  4226  var identity = function identity(x) {
  4227    return x;
  4228  };
  4229  
  4230  var absDiff = function absDiff(p, q) {
  4231    return Math.abs(q - p);
  4232  };
  4233  
  4234  var addAbsDiff = function addAbsDiff(total, p, q) {
  4235    return total + absDiff(p, q);
  4236  };
  4237  
  4238  var addSquaredDiff = function addSquaredDiff(total, p, q) {
  4239    return total + Math.pow(q - p, 2);
  4240  };
  4241  
  4242  var sqrt = function sqrt(x) {
  4243    return Math.sqrt(x);
  4244  };
  4245  
  4246  var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
  4247    return Math.max(currentMax, absDiff(p, q));
  4248  };
  4249  
  4250  var getDistance = function getDistance(length, getP, getQ, init, visit) {
  4251    var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
  4252    var ret = init;
  4253    var p, q;
  4254  
  4255    for (var dim = 0; dim < length; dim++) {
  4256      p = getP(dim);
  4257      q = getQ(dim);
  4258      ret = visit(ret, p, q);
  4259    }
  4260  
  4261    return post(ret);
  4262  };
  4263  
  4264  var distances = {
  4265    euclidean: function euclidean(length, getP, getQ) {
  4266      if (length >= 2) {
  4267        return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
  4268      } else {
  4269        // for single attr case, more efficient to avoid sqrt
  4270        return getDistance(length, getP, getQ, 0, addAbsDiff);
  4271      }
  4272    },
  4273    squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
  4274      return getDistance(length, getP, getQ, 0, addSquaredDiff);
  4275    },
  4276    manhattan: function manhattan(length, getP, getQ) {
  4277      return getDistance(length, getP, getQ, 0, addAbsDiff);
  4278    },
  4279    max: function max(length, getP, getQ) {
  4280      return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
  4281    }
  4282  }; // in case the user accidentally doesn't use camel case
  4283  
  4284  distances['squared-euclidean'] = distances['squaredEuclidean'];
  4285  distances['squaredeuclidean'] = distances['squaredEuclidean'];
  4286  function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
  4287    var impl;
  4288  
  4289    if (fn(method)) {
  4290      impl = method;
  4291    } else {
  4292      impl = distances[method] || distances.euclidean;
  4293    }
  4294  
  4295    if (length === 0 && fn(method)) {
  4296      return impl(nodeP, nodeQ);
  4297    } else {
  4298      return impl(length, getP, getQ, nodeP, nodeQ);
  4299    }
  4300  }
  4301  
  4302  var defaults$5 = defaults({
  4303    k: 2,
  4304    m: 2,
  4305    sensitivityThreshold: 0.0001,
  4306    distance: 'euclidean',
  4307    maxIterations: 10,
  4308    attributes: [],
  4309    testMode: false,
  4310    testCentroids: null
  4311  });
  4312  
  4313  var setOptions$1 = function setOptions(options) {
  4314    return defaults$5(options);
  4315  };
  4316  /* eslint-enable */
  4317  
  4318  
  4319  var getDist = function getDist(type, node, centroid, attributes, mode) {
  4320    var noNodeP = mode !== 'kMedoids';
  4321    var getP = noNodeP ? function (i) {
  4322      return centroid[i];
  4323    } : function (i) {
  4324      return attributes[i](centroid);
  4325    };
  4326  
  4327    var getQ = function getQ(i) {
  4328      return attributes[i](node);
  4329    };
  4330  
  4331    var nodeP = centroid;
  4332    var nodeQ = node;
  4333    return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
  4334  };
  4335  
  4336  var randomCentroids = function randomCentroids(nodes, k, attributes) {
  4337    var ndim = attributes.length;
  4338    var min = new Array(ndim);
  4339    var max = new Array(ndim);
  4340    var centroids = new Array(k);
  4341    var centroid = null; // Find min, max values for each attribute dimension
  4342  
  4343    for (var i = 0; i < ndim; i++) {
  4344      min[i] = nodes.min(attributes[i]).value;
  4345      max[i] = nodes.max(attributes[i]).value;
  4346    } // Build k centroids, each represented as an n-dim feature vector
  4347  
  4348  
  4349    for (var c = 0; c < k; c++) {
  4350      centroid = [];
  4351  
  4352      for (var _i = 0; _i < ndim; _i++) {
  4353        centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
  4354      }
  4355  
  4356      centroids[c] = centroid;
  4357    }
  4358  
  4359    return centroids;
  4360  };
  4361  
  4362  var classify = function classify(node, centroids, distance, attributes, type) {
  4363    var min = Infinity;
  4364    var index = 0;
  4365  
  4366    for (var i = 0; i < centroids.length; i++) {
  4367      var dist = getDist(distance, node, centroids[i], attributes, type);
  4368  
  4369      if (dist < min) {
  4370        min = dist;
  4371        index = i;
  4372      }
  4373    }
  4374  
  4375    return index;
  4376  };
  4377  
  4378  var buildCluster = function buildCluster(centroid, nodes, assignment) {
  4379    var cluster = [];
  4380    var node = null;
  4381  
  4382    for (var n = 0; n < nodes.length; n++) {
  4383      node = nodes[n];
  4384  
  4385      if (assignment[node.id()] === centroid) {
  4386        //console.log("Node " + node.id() + " is associated with medoid #: " + m);
  4387        cluster.push(node);
  4388      }
  4389    }
  4390  
  4391    return cluster;
  4392  };
  4393  
  4394  var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
  4395    return Math.abs(v2 - v1) <= sensitivityThreshold;
  4396  };
  4397  
  4398  var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
  4399    for (var i = 0; i < v1.length; i++) {
  4400      for (var j = 0; j < v1[i].length; j++) {
  4401        var diff = Math.abs(v1[i][j] - v2[i][j]);
  4402  
  4403        if (diff > sensitivityThreshold) {
  4404          return false;
  4405        }
  4406      }
  4407    }
  4408  
  4409    return true;
  4410  };
  4411  
  4412  var seenBefore = function seenBefore(node, medoids, n) {
  4413    for (var i = 0; i < n; i++) {
  4414      if (node === medoids[i]) return true;
  4415    }
  4416  
  4417    return false;
  4418  };
  4419  
  4420  var randomMedoids = function randomMedoids(nodes, k) {
  4421    var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
  4422    // so we need to check to see if we've already seen or chose this node before.
  4423  
  4424    if (nodes.length < 50) {
  4425      // Randomly select k medoids from the n nodes
  4426      for (var i = 0; i < k; i++) {
  4427        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).
  4428        // Instead choose a different random node.
  4429  
  4430        while (seenBefore(node, medoids, i)) {
  4431          node = nodes[Math.floor(Math.random() * nodes.length)];
  4432        }
  4433  
  4434        medoids[i] = node;
  4435      }
  4436    } else {
  4437      // Relatively large data set, so pretty safe to not check and just select random nodes
  4438      for (var _i2 = 0; _i2 < k; _i2++) {
  4439        medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
  4440      }
  4441    }
  4442  
  4443    return medoids;
  4444  };
  4445  
  4446  var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
  4447    var cost = 0;
  4448  
  4449    for (var n = 0; n < cluster.length; n++) {
  4450      cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
  4451    }
  4452  
  4453    return cost;
  4454  };
  4455  
  4456  var kMeans = function kMeans(options) {
  4457    var cy = this.cy();
  4458    var nodes = this.nodes();
  4459    var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
  4460  
  4461    var opts = setOptions$1(options); // Begin k-means algorithm
  4462  
  4463    var clusters = new Array(opts.k);
  4464    var assignment = {};
  4465    var centroids; // Step 1: Initialize centroid positions
  4466  
  4467    if (opts.testMode) {
  4468      if (typeof opts.testCentroids === 'number') {
  4469        centroids = randomCentroids(nodes, opts.k, opts.attributes);
  4470      } else if (_typeof(opts.testCentroids) === 'object') {
  4471        centroids = opts.testCentroids;
  4472      } else {
  4473        centroids = randomCentroids(nodes, opts.k, opts.attributes);
  4474      }
  4475    } else {
  4476      centroids = randomCentroids(nodes, opts.k, opts.attributes);
  4477    }
  4478  
  4479    var isStillMoving = true;
  4480    var iterations = 0;
  4481  
  4482    while (isStillMoving && iterations < opts.maxIterations) {
  4483      // Step 2: Assign nodes to the nearest centroid
  4484      for (var n = 0; n < nodes.length; n++) {
  4485        node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
  4486  
  4487        assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
  4488      } // Step 3: For each of the k clusters, update its centroid
  4489  
  4490  
  4491      isStillMoving = false;
  4492  
  4493      for (var c = 0; c < opts.k; c++) {
  4494        // Get all nodes that belong to this cluster
  4495        var cluster = buildCluster(c, nodes, assignment);
  4496  
  4497        if (cluster.length === 0) {
  4498          // If cluster is empty, break out early & move to next cluster
  4499          continue;
  4500        } // Update centroids by calculating avg of all nodes within the cluster.
  4501  
  4502  
  4503        var ndim = opts.attributes.length;
  4504        var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
  4505  
  4506        var newCentroid = new Array(ndim);
  4507        var sum = new Array(ndim);
  4508  
  4509        for (var d = 0; d < ndim; d++) {
  4510          sum[d] = 0.0;
  4511  
  4512          for (var i = 0; i < cluster.length; i++) {
  4513            node = cluster[i];
  4514            sum[d] += opts.attributes[d](node);
  4515          }
  4516  
  4517          newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
  4518  
  4519          if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
  4520            isStillMoving = true;
  4521          }
  4522        }
  4523  
  4524        centroids[c] = newCentroid;
  4525        clusters[c] = cy.collection(cluster);
  4526      }
  4527  
  4528      iterations++;
  4529    }
  4530  
  4531    return clusters;
  4532  };
  4533  
  4534  var kMedoids = function kMedoids(options) {
  4535    var cy = this.cy();
  4536    var nodes = this.nodes();
  4537    var node = null;
  4538    var opts = setOptions$1(options); // Begin k-medoids algorithm
  4539  
  4540    var clusters = new Array(opts.k);
  4541    var medoids;
  4542    var assignment = {};
  4543    var curCost;
  4544    var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
  4545    // Step 1: Initialize k medoids
  4546  
  4547    if (opts.testMode) {
  4548      if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
  4549        medoids = opts.testCentroids;
  4550      } else {
  4551        medoids = randomMedoids(nodes, opts.k);
  4552      }
  4553    } else {
  4554      medoids = randomMedoids(nodes, opts.k);
  4555    }
  4556  
  4557    var isStillMoving = true;
  4558    var iterations = 0;
  4559  
  4560    while (isStillMoving && iterations < opts.maxIterations) {
  4561      // Step 2: Assign nodes to the nearest medoid
  4562      for (var n = 0; n < nodes.length; n++) {
  4563        node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
  4564  
  4565        assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
  4566      }
  4567  
  4568      isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
  4569      // select the node with the lowest configuration cost as new medoid.
  4570  
  4571      for (var m = 0; m < medoids.length; m++) {
  4572        // Get all nodes that belong to this medoid
  4573        var cluster = buildCluster(m, nodes, assignment);
  4574  
  4575        if (cluster.length === 0) {
  4576          // If cluster is empty, break out early & move to next cluster
  4577          continue;
  4578        }
  4579  
  4580        minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
  4581        // Select different medoid if its configuration has the lowest cost
  4582  
  4583        for (var _n = 0; _n < cluster.length; _n++) {
  4584          curCost = findCost(cluster[_n], cluster, opts.attributes);
  4585  
  4586          if (curCost < minCosts[m]) {
  4587            minCosts[m] = curCost;
  4588            medoids[m] = cluster[_n];
  4589            isStillMoving = true;
  4590          }
  4591        }
  4592  
  4593        clusters[m] = cy.collection(cluster);
  4594      }
  4595  
  4596      iterations++;
  4597    }
  4598  
  4599    return clusters;
  4600  };
  4601  
  4602  var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
  4603    var numerator, denominator;
  4604  
  4605    for (var n = 0; n < nodes.length; n++) {
  4606      for (var c = 0; c < centroids.length; c++) {
  4607        weight[n][c] = Math.pow(U[n][c], opts.m);
  4608      }
  4609    }
  4610  
  4611    for (var _c = 0; _c < centroids.length; _c++) {
  4612      for (var dim = 0; dim < opts.attributes.length; dim++) {
  4613        numerator = 0;
  4614        denominator = 0;
  4615  
  4616        for (var _n2 = 0; _n2 < nodes.length; _n2++) {
  4617          numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
  4618          denominator += weight[_n2][_c];
  4619        }
  4620  
  4621        centroids[_c][dim] = numerator / denominator;
  4622      }
  4623    }
  4624  };
  4625  
  4626  var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
  4627    // Save previous step
  4628    for (var i = 0; i < U.length; i++) {
  4629      _U[i] = U[i].slice();
  4630    }
  4631  
  4632    var sum, numerator, denominator;
  4633    var pow = 2 / (opts.m - 1);
  4634  
  4635    for (var c = 0; c < centroids.length; c++) {
  4636      for (var n = 0; n < nodes.length; n++) {
  4637        sum = 0;
  4638  
  4639        for (var k = 0; k < centroids.length; k++) {
  4640          // against all other centroids
  4641          numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
  4642          denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
  4643          sum += Math.pow(numerator / denominator, pow);
  4644        }
  4645  
  4646        U[n][c] = 1 / sum;
  4647      }
  4648    }
  4649  };
  4650  
  4651  var assign$1 = function assign(nodes, U, opts, cy) {
  4652    var clusters = new Array(opts.k);
  4653  
  4654    for (var c = 0; c < clusters.length; c++) {
  4655      clusters[c] = [];
  4656    }
  4657  
  4658    var max;
  4659    var index;
  4660  
  4661    for (var n = 0; n < U.length; n++) {
  4662      // for each node (U is N x C matrix)
  4663      max = -Infinity;
  4664      index = -1; // Determine which cluster the node is most likely to belong in
  4665  
  4666      for (var _c2 = 0; _c2 < U[0].length; _c2++) {
  4667        if (U[n][_c2] > max) {
  4668          max = U[n][_c2];
  4669          index = _c2;
  4670        }
  4671      }
  4672  
  4673      clusters[index].push(nodes[n]);
  4674    } // Turn every array into a collection of nodes
  4675  
  4676  
  4677    for (var _c3 = 0; _c3 < clusters.length; _c3++) {
  4678      clusters[_c3] = cy.collection(clusters[_c3]);
  4679    }
  4680  
  4681    return clusters;
  4682  };
  4683  
  4684  var fuzzyCMeans = function fuzzyCMeans(options) {
  4685    var cy = this.cy();
  4686    var nodes = this.nodes();
  4687    var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
  4688  
  4689    var clusters;
  4690    var centroids;
  4691    var U;
  4692  
  4693    var _U;
  4694  
  4695    var weight; // Step 1: Initialize letiables.
  4696  
  4697    _U = new Array(nodes.length);
  4698  
  4699    for (var i = 0; i < nodes.length; i++) {
  4700      // N x C matrix
  4701      _U[i] = new Array(opts.k);
  4702    }
  4703  
  4704    U = new Array(nodes.length);
  4705  
  4706    for (var _i3 = 0; _i3 < nodes.length; _i3++) {
  4707      // N x C matrix
  4708      U[_i3] = new Array(opts.k);
  4709    }
  4710  
  4711    for (var _i4 = 0; _i4 < nodes.length; _i4++) {
  4712      var total = 0;
  4713  
  4714      for (var j = 0; j < opts.k; j++) {
  4715        U[_i4][j] = Math.random();
  4716        total += U[_i4][j];
  4717      }
  4718  
  4719      for (var _j = 0; _j < opts.k; _j++) {
  4720        U[_i4][_j] = U[_i4][_j] / total;
  4721      }
  4722    }
  4723  
  4724    centroids = new Array(opts.k);
  4725  
  4726    for (var _i5 = 0; _i5 < opts.k; _i5++) {
  4727      centroids[_i5] = new Array(opts.attributes.length);
  4728    }
  4729  
  4730    weight = new Array(nodes.length);
  4731  
  4732    for (var _i6 = 0; _i6 < nodes.length; _i6++) {
  4733      // N x C matrix
  4734      weight[_i6] = new Array(opts.k);
  4735    } // end init FCM
  4736  
  4737  
  4738    var isStillMoving = true;
  4739    var iterations = 0;
  4740  
  4741    while (isStillMoving && iterations < opts.maxIterations) {
  4742      isStillMoving = false; // Step 2: Calculate the centroids for each step.
  4743  
  4744      updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
  4745  
  4746      updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
  4747  
  4748      if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
  4749        isStillMoving = true;
  4750      }
  4751  
  4752      iterations++;
  4753    } // Assign nodes to clusters with highest probability.
  4754  
  4755  
  4756    clusters = assign$1(nodes, U, opts, cy);
  4757    return {
  4758      clusters: clusters,
  4759      degreeOfMembership: U
  4760    };
  4761  };
  4762  
  4763  var kClustering = {
  4764    kMeans: kMeans,
  4765    kMedoids: kMedoids,
  4766    fuzzyCMeans: fuzzyCMeans,
  4767    fcm: fuzzyCMeans
  4768  };
  4769  
  4770  // Implemented by Zoe Xi @zoexi for GSOC 2016
  4771  var defaults$6 = defaults({
  4772    distance: 'euclidean',
  4773    // distance metric to compare nodes
  4774    linkage: 'min',
  4775    // linkage criterion : how to determine the distance between clusters of nodes
  4776    mode: 'threshold',
  4777    // mode:'threshold' => clusters must be threshold distance apart
  4778    threshold: Infinity,
  4779    // the distance threshold
  4780    // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
  4781    addDendrogram: false,
  4782    // whether to add the dendrogram to the graph for viz
  4783    dendrogramDepth: 0,
  4784    // depth at which dendrogram branches are merged into the returned clusters
  4785    attributes: [] // array of attr functions
  4786  
  4787  });
  4788  var linkageAliases = {
  4789    'single': 'min',
  4790    'complete': 'max'
  4791  };
  4792  
  4793  var setOptions$2 = function setOptions(options) {
  4794    var opts = defaults$6(options);
  4795    var preferredAlias = linkageAliases[opts.linkage];
  4796  
  4797    if (preferredAlias != null) {
  4798      opts.linkage = preferredAlias;
  4799    }
  4800  
  4801    return opts;
  4802  };
  4803  
  4804  var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
  4805    // Find two closest clusters from cached mins
  4806    var minKey = 0;
  4807    var min = Infinity;
  4808    var dist;
  4809    var attrs = opts.attributes;
  4810  
  4811    var getDist = function getDist(n1, n2) {
  4812      return clusteringDistance(opts.distance, attrs.length, function (i) {
  4813        return attrs[i](n1);
  4814      }, function (i) {
  4815        return attrs[i](n2);
  4816      }, n1, n2);
  4817    };
  4818  
  4819    for (var i = 0; i < clusters.length; i++) {
  4820      var key = clusters[i].key;
  4821      var _dist = dists[key][mins[key]];
  4822  
  4823      if (_dist < min) {
  4824        minKey = key;
  4825        min = _dist;
  4826      }
  4827    }
  4828  
  4829    if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
  4830      return false;
  4831    }
  4832  
  4833    var c1 = index[minKey];
  4834    var c2 = index[mins[minKey]];
  4835    var merged; // Merge two closest clusters
  4836  
  4837    if (opts.mode === 'dendrogram') {
  4838      merged = {
  4839        left: c1,
  4840        right: c2,
  4841        key: c1.key
  4842      };
  4843    } else {
  4844      merged = {
  4845        value: c1.value.concat(c2.value),
  4846        key: c1.key
  4847      };
  4848    }
  4849  
  4850    clusters[c1.index] = merged;
  4851    clusters.splice(c2.index, 1);
  4852    index[c1.key] = merged; // Update distances with new merged cluster
  4853  
  4854    for (var _i = 0; _i < clusters.length; _i++) {
  4855      var cur = clusters[_i];
  4856  
  4857      if (c1.key === cur.key) {
  4858        dist = Infinity;
  4859      } else if (opts.linkage === 'min') {
  4860        dist = dists[c1.key][cur.key];
  4861  
  4862        if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
  4863          dist = dists[c2.key][cur.key];
  4864        }
  4865      } else if (opts.linkage === 'max') {
  4866        dist = dists[c1.key][cur.key];
  4867  
  4868        if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
  4869          dist = dists[c2.key][cur.key];
  4870        }
  4871      } else if (opts.linkage === 'mean') {
  4872        dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
  4873      } else {
  4874        if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
  4875      }
  4876  
  4877      dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
  4878    } // Update cached mins
  4879  
  4880  
  4881    for (var _i2 = 0; _i2 < clusters.length; _i2++) {
  4882      var key1 = clusters[_i2].key;
  4883  
  4884      if (mins[key1] === c1.key || mins[key1] === c2.key) {
  4885        var _min = key1;
  4886  
  4887        for (var j = 0; j < clusters.length; j++) {
  4888          var key2 = clusters[j].key;
  4889  
  4890          if (dists[key1][key2] < dists[key1][_min]) {
  4891            _min = key2;
  4892          }
  4893        }
  4894  
  4895        mins[key1] = _min;
  4896      }
  4897  
  4898      clusters[_i2].index = _i2;
  4899    } // Clean up meta data used for clustering
  4900  
  4901  
  4902    c1.key = c2.key = c1.index = c2.index = null;
  4903    return true;
  4904  };
  4905  
  4906  var getAllChildren = function getAllChildren(root, arr, cy) {
  4907    if (!root) return;
  4908  
  4909    if (root.value) {
  4910      arr.push(root.value);
  4911    } else {
  4912      if (root.left) getAllChildren(root.left, arr);
  4913      if (root.right) getAllChildren(root.right, arr);
  4914    }
  4915  };
  4916  
  4917  var buildDendrogram = function buildDendrogram(root, cy) {
  4918    if (!root) return '';
  4919  
  4920    if (root.left && root.right) {
  4921      var leftStr = buildDendrogram(root.left, cy);
  4922      var rightStr = buildDendrogram(root.right, cy);
  4923      var node = cy.add({
  4924        group: 'nodes',
  4925        data: {
  4926          id: leftStr + ',' + rightStr
  4927        }
  4928      });
  4929      cy.add({
  4930        group: 'edges',
  4931        data: {
  4932          source: leftStr,
  4933          target: node.id()
  4934        }
  4935      });
  4936      cy.add({
  4937        group: 'edges',
  4938        data: {
  4939          source: rightStr,
  4940          target: node.id()
  4941        }
  4942      });
  4943      return node.id();
  4944    } else if (root.value) {
  4945      return root.value.id();
  4946    }
  4947  };
  4948  
  4949  var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
  4950    if (!root) return [];
  4951    var left = [],
  4952        right = [],
  4953        leaves = [];
  4954  
  4955    if (k === 0) {
  4956      // don't cut tree, simply return all nodes as 1 single cluster
  4957      if (root.left) getAllChildren(root.left, left);
  4958      if (root.right) getAllChildren(root.right, right);
  4959      leaves = left.concat(right);
  4960      return [cy.collection(leaves)];
  4961    } else if (k === 1) {
  4962      // cut at root
  4963      if (root.value) {
  4964        // leaf node
  4965        return [cy.collection(root.value)];
  4966      } else {
  4967        if (root.left) getAllChildren(root.left, left);
  4968        if (root.right) getAllChildren(root.right, right);
  4969        return [cy.collection(left), cy.collection(right)];
  4970      }
  4971    } else {
  4972      if (root.value) {
  4973        return [cy.collection(root.value)];
  4974      } else {
  4975        if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
  4976        if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
  4977        return left.concat(right);
  4978      }
  4979    }
  4980  };
  4981  /* eslint-enable */
  4982  
  4983  
  4984  var hierarchicalClustering = function hierarchicalClustering(options) {
  4985    var cy = this.cy();
  4986    var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
  4987  
  4988    var opts = setOptions$2(options);
  4989    var attrs = opts.attributes;
  4990  
  4991    var getDist = function getDist(n1, n2) {
  4992      return clusteringDistance(opts.distance, attrs.length, function (i) {
  4993        return attrs[i](n1);
  4994      }, function (i) {
  4995        return attrs[i](n2);
  4996      }, n1, n2);
  4997    }; // Begin hierarchical algorithm
  4998  
  4999  
  5000    var clusters = [];
  5001    var dists = []; // distances between each pair of clusters
  5002  
  5003    var mins = []; // closest cluster for each cluster
  5004  
  5005    var index = []; // hash of all clusters by key
  5006    // In agglomerative (bottom-up) clustering, each node starts as its own cluster
  5007  
  5008    for (var n = 0; n < nodes.length; n++) {
  5009      var cluster = {
  5010        value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
  5011        key: n,
  5012        index: n
  5013      };
  5014      clusters[n] = cluster;
  5015      index[n] = cluster;
  5016      dists[n] = [];
  5017      mins[n] = 0;
  5018    } // Calculate the distance between each pair of clusters
  5019  
  5020  
  5021    for (var i = 0; i < clusters.length; i++) {
  5022      for (var j = 0; j <= i; j++) {
  5023        var dist = void 0;
  5024  
  5025        if (opts.mode === 'dendrogram') {
  5026          // modes store cluster values differently
  5027          dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
  5028        } else {
  5029          dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
  5030        }
  5031  
  5032        dists[i][j] = dist;
  5033        dists[j][i] = dist;
  5034  
  5035        if (dist < dists[i][mins[i]]) {
  5036          mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
  5037        }
  5038      }
  5039    } // Find the closest pair of clusters and merge them into a single cluster.
  5040    // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
  5041  
  5042  
  5043    var merged = mergeClosest(clusters, index, dists, mins, opts);
  5044  
  5045    while (merged) {
  5046      merged = mergeClosest(clusters, index, dists, mins, opts);
  5047    }
  5048  
  5049    var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
  5050    // in addition to returning the clusters.
  5051  
  5052    if (opts.mode === 'dendrogram') {
  5053      retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
  5054      if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
  5055    } else {
  5056      // Regular mode simply returns the clusters
  5057      retClusters = new Array(clusters.length);
  5058      clusters.forEach(function (cluster, i) {
  5059        // Clean up meta data used for clustering
  5060        cluster.key = cluster.index = null;
  5061        retClusters[i] = cy.collection(cluster.value);
  5062      });
  5063    }
  5064  
  5065    return retClusters;
  5066  };
  5067  
  5068  var hierarchicalClustering$1 = {
  5069    hierarchicalClustering: hierarchicalClustering,
  5070    hca: hierarchicalClustering
  5071  };
  5072  
  5073  // Implemented by Zoe Xi @zoexi for GSOC 2016
  5074  var defaults$7 = defaults({
  5075    distance: 'euclidean',
  5076    // distance metric to compare attributes between two nodes
  5077    preference: 'median',
  5078    // suitability of a data point to serve as an exemplar
  5079    damping: 0.8,
  5080    // damping factor between [0.5, 1)
  5081    maxIterations: 1000,
  5082    // max number of iterations to run
  5083    minIterations: 100,
  5084    // min number of iterations to run in order for clustering to stop
  5085    attributes: [// functions to quantify the similarity between any two points
  5086      // e.g. node => node.data('weight')
  5087    ]
  5088  });
  5089  
  5090  var setOptions$3 = function setOptions(options) {
  5091    var dmp = options.damping;
  5092    var pref = options.preference;
  5093  
  5094    if (!(0.5 <= dmp && dmp < 1)) {
  5095      error("Damping must range on [0.5, 1).  Got: ".concat(dmp));
  5096    }
  5097  
  5098    var validPrefs = ['median', 'mean', 'min', 'max'];
  5099  
  5100    if (!(validPrefs.some(function (v) {
  5101      return v === pref;
  5102    }) || number(pref))) {
  5103      error("Preference must be one of [".concat(validPrefs.map(function (p) {
  5104        return "'".concat(p, "'");
  5105      }).join(', '), "] or a number.  Got: ").concat(pref));
  5106    }
  5107  
  5108    return defaults$7(options);
  5109  };
  5110  /* eslint-enable */
  5111  
  5112  
  5113  var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
  5114    var attr = function attr(n, i) {
  5115      return attributes[i](n);
  5116    }; // nb negative because similarity should have an inverse relationship to distance
  5117  
  5118  
  5119    return -clusteringDistance(type, attributes.length, function (i) {
  5120      return attr(n1, i);
  5121    }, function (i) {
  5122      return attr(n2, i);
  5123    }, n1, n2);
  5124  };
  5125  
  5126  var getPreference = function getPreference(S, preference) {
  5127    // larger preference = greater # of clusters
  5128    var p = null;
  5129  
  5130    if (preference === 'median') {
  5131      p = median(S);
  5132    } else if (preference === 'mean') {
  5133      p = mean(S);
  5134    } else if (preference === 'min') {
  5135      p = min(S);
  5136    } else if (preference === 'max') {
  5137      p = max(S);
  5138    } else {
  5139      // Custom preference number, as set by user
  5140      p = preference;
  5141    }
  5142  
  5143    return p;
  5144  };
  5145  
  5146  var findExemplars = function findExemplars(n, R, A) {
  5147    var indices = [];
  5148  
  5149    for (var i = 0; i < n; i++) {
  5150      if (R[i * n + i] + A[i * n + i] > 0) {
  5151        indices.push(i);
  5152      }
  5153    }
  5154  
  5155    return indices;
  5156  };
  5157  
  5158  var assignClusters = function assignClusters(n, S, exemplars) {
  5159    var clusters = [];
  5160  
  5161    for (var i = 0; i < n; i++) {
  5162      var index = -1;
  5163      var max = -Infinity;
  5164  
  5165      for (var ei = 0; ei < exemplars.length; ei++) {
  5166        var e = exemplars[ei];
  5167  
  5168        if (S[i * n + e] > max) {
  5169          index = e;
  5170          max = S[i * n + e];
  5171        }
  5172      }
  5173  
  5174      if (index > 0) {
  5175        clusters.push(index);
  5176      }
  5177    }
  5178  
  5179    for (var _ei = 0; _ei < exemplars.length; _ei++) {
  5180      clusters[exemplars[_ei]] = exemplars[_ei];
  5181    }
  5182  
  5183    return clusters;
  5184  };
  5185  
  5186  var assign$2 = function assign(n, S, exemplars) {
  5187    var clusters = assignClusters(n, S, exemplars);
  5188  
  5189    for (var ei = 0; ei < exemplars.length; ei++) {
  5190      var ii = [];
  5191  
  5192      for (var c = 0; c < clusters.length; c++) {
  5193        if (clusters[c] === exemplars[ei]) {
  5194          ii.push(c);
  5195        }
  5196      }
  5197  
  5198      var maxI = -1;
  5199      var maxSum = -Infinity;
  5200  
  5201      for (var i = 0; i < ii.length; i++) {
  5202        var sum = 0;
  5203  
  5204        for (var j = 0; j < ii.length; j++) {
  5205          sum += S[ii[j] * n + ii[i]];
  5206        }
  5207  
  5208        if (sum > maxSum) {
  5209          maxI = i;
  5210          maxSum = sum;
  5211        }
  5212      }
  5213  
  5214      exemplars[ei] = ii[maxI];
  5215    }
  5216  
  5217    clusters = assignClusters(n, S, exemplars);
  5218    return clusters;
  5219  };
  5220  
  5221  var affinityPropagation = function affinityPropagation(options) {
  5222    var cy = this.cy();
  5223    var nodes = this.nodes();
  5224    var opts = setOptions$3(options); // Map each node to its position in node array
  5225  
  5226    var id2position = {};
  5227  
  5228    for (var i = 0; i < nodes.length; i++) {
  5229      id2position[nodes[i].id()] = i;
  5230    } // Begin affinity propagation algorithm
  5231  
  5232  
  5233    var n; // number of data points
  5234  
  5235    var n2; // size of matrices
  5236  
  5237    var S; // similarity matrix (1D array)
  5238  
  5239    var p; // preference/suitability of a data point to serve as an exemplar
  5240  
  5241    var R; // responsibility matrix (1D array)
  5242  
  5243    var A; // availability matrix (1D array)
  5244  
  5245    n = nodes.length;
  5246    n2 = n * n; // Initialize and build S similarity matrix
  5247  
  5248    S = new Array(n2);
  5249  
  5250    for (var _i = 0; _i < n2; _i++) {
  5251      S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
  5252    }
  5253  
  5254    for (var _i2 = 0; _i2 < n; _i2++) {
  5255      for (var j = 0; j < n; j++) {
  5256        if (_i2 !== j) {
  5257          S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
  5258        }
  5259      }
  5260    } // Place preferences on the diagonal of S
  5261  
  5262  
  5263    p = getPreference(S, opts.preference);
  5264  
  5265    for (var _i3 = 0; _i3 < n; _i3++) {
  5266      S[_i3 * n + _i3] = p;
  5267    } // Initialize R responsibility matrix
  5268  
  5269  
  5270    R = new Array(n2);
  5271  
  5272    for (var _i4 = 0; _i4 < n2; _i4++) {
  5273      R[_i4] = 0.0;
  5274    } // Initialize A availability matrix
  5275  
  5276  
  5277    A = new Array(n2);
  5278  
  5279    for (var _i5 = 0; _i5 < n2; _i5++) {
  5280      A[_i5] = 0.0;
  5281    }
  5282  
  5283    var old = new Array(n);
  5284    var Rp = new Array(n);
  5285    var se = new Array(n);
  5286  
  5287    for (var _i6 = 0; _i6 < n; _i6++) {
  5288      old[_i6] = 0.0;
  5289      Rp[_i6] = 0.0;
  5290      se[_i6] = 0;
  5291    }
  5292  
  5293    var e = new Array(n * opts.minIterations);
  5294  
  5295    for (var _i7 = 0; _i7 < e.length; _i7++) {
  5296      e[_i7] = 0;
  5297    }
  5298  
  5299    var iter;
  5300  
  5301    for (iter = 0; iter < opts.maxIterations; iter++) {
  5302      // main algorithmic loop
  5303      // Update R responsibility matrix
  5304      for (var _i8 = 0; _i8 < n; _i8++) {
  5305        var max = -Infinity,
  5306            max2 = -Infinity,
  5307            maxI = -1,
  5308            AS = 0.0;
  5309  
  5310        for (var _j = 0; _j < n; _j++) {
  5311          old[_j] = R[_i8 * n + _j];
  5312          AS = A[_i8 * n + _j] + S[_i8 * n + _j];
  5313  
  5314          if (AS >= max) {
  5315            max2 = max;
  5316            max = AS;
  5317            maxI = _j;
  5318          } else if (AS > max2) {
  5319            max2 = AS;
  5320          }
  5321        }
  5322  
  5323        for (var _j2 = 0; _j2 < n; _j2++) {
  5324          R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
  5325        }
  5326  
  5327        R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
  5328      } // Update A availability matrix
  5329  
  5330  
  5331      for (var _i9 = 0; _i9 < n; _i9++) {
  5332        var sum = 0;
  5333  
  5334        for (var _j3 = 0; _j3 < n; _j3++) {
  5335          old[_j3] = A[_j3 * n + _i9];
  5336          Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
  5337          sum += Rp[_j3];
  5338        }
  5339  
  5340        sum -= Rp[_i9];
  5341        Rp[_i9] = R[_i9 * n + _i9];
  5342        sum += Rp[_i9];
  5343  
  5344        for (var _j4 = 0; _j4 < n; _j4++) {
  5345          A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
  5346        }
  5347  
  5348        A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
  5349      } // Check for convergence
  5350  
  5351  
  5352      var K = 0;
  5353  
  5354      for (var _i10 = 0; _i10 < n; _i10++) {
  5355        var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
  5356        e[iter % opts.minIterations * n + _i10] = E;
  5357        K += E;
  5358      }
  5359  
  5360      if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
  5361        var _sum = 0;
  5362  
  5363        for (var _i11 = 0; _i11 < n; _i11++) {
  5364          se[_i11] = 0;
  5365  
  5366          for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
  5367            se[_i11] += e[_j5 * n + _i11];
  5368          }
  5369  
  5370          if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
  5371            _sum++;
  5372          }
  5373        }
  5374  
  5375        if (_sum === n) {
  5376          // then we have convergence
  5377          break;
  5378        }
  5379      }
  5380    } // Identify exemplars (cluster centers)
  5381  
  5382  
  5383    var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
  5384  
  5385    var clusterIndices = assign$2(n, S, exemplarsIndices);
  5386    var clusters = {};
  5387  
  5388    for (var c = 0; c < exemplarsIndices.length; c++) {
  5389      clusters[exemplarsIndices[c]] = [];
  5390    }
  5391  
  5392    for (var _i12 = 0; _i12 < nodes.length; _i12++) {
  5393      var pos = id2position[nodes[_i12].id()];
  5394  
  5395      var clusterIndex = clusterIndices[pos];
  5396  
  5397      if (clusterIndex != null) {
  5398        // the node may have not been assigned a cluster if no valid attributes were specified
  5399        clusters[clusterIndex].push(nodes[_i12]);
  5400      }
  5401    }
  5402  
  5403    var retClusters = new Array(exemplarsIndices.length);
  5404  
  5405    for (var _c = 0; _c < exemplarsIndices.length; _c++) {
  5406      retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
  5407    }
  5408  
  5409    return retClusters;
  5410  };
  5411  
  5412  var affinityPropagation$1 = {
  5413    affinityPropagation: affinityPropagation,
  5414    ap: affinityPropagation
  5415  };
  5416  
  5417  var hierholzerDefaults = defaults({
  5418    root: undefined,
  5419    directed: false
  5420  });
  5421  var elesfn$b = {
  5422    hierholzer: function hierholzer(options) {
  5423      if (!plainObject(options)) {
  5424        var args = arguments;
  5425        options = {
  5426          root: args[0],
  5427          directed: args[1]
  5428        };
  5429      }
  5430  
  5431      var _hierholzerDefaults = hierholzerDefaults(options),
  5432          root = _hierholzerDefaults.root,
  5433          directed = _hierholzerDefaults.directed;
  5434  
  5435      var eles = this;
  5436      var dflag = false;
  5437      var oddIn;
  5438      var oddOut;
  5439      var startVertex;
  5440      if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
  5441      var nodes = {};
  5442      var edges = {};
  5443  
  5444      if (directed) {
  5445        eles.forEach(function (ele) {
  5446          var id = ele.id();
  5447  
  5448          if (ele.isNode()) {
  5449            var ind = ele.indegree(true);
  5450            var outd = ele.outdegree(true);
  5451            var d1 = ind - outd;
  5452            var d2 = outd - ind;
  5453  
  5454            if (d1 == 1) {
  5455              if (oddIn) dflag = true;else oddIn = id;
  5456            } else if (d2 == 1) {
  5457              if (oddOut) dflag = true;else oddOut = id;
  5458            } else if (d2 > 1 || d1 > 1) {
  5459              dflag = true;
  5460            }
  5461  
  5462            nodes[id] = [];
  5463            ele.outgoers().forEach(function (e) {
  5464              if (e.isEdge()) nodes[id].push(e.id());
  5465            });
  5466          } else {
  5467            edges[id] = [undefined, ele.target().id()];
  5468          }
  5469        });
  5470      } else {
  5471        eles.forEach(function (ele) {
  5472          var id = ele.id();
  5473  
  5474          if (ele.isNode()) {
  5475            var d = ele.degree(true);
  5476  
  5477            if (d % 2) {
  5478              if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
  5479            }
  5480  
  5481            nodes[id] = [];
  5482            ele.connectedEdges().forEach(function (e) {
  5483              return nodes[id].push(e.id());
  5484            });
  5485          } else {
  5486            edges[id] = [ele.source().id(), ele.target().id()];
  5487          }
  5488        });
  5489      }
  5490  
  5491      var result = {
  5492        found: false,
  5493        trail: undefined
  5494      };
  5495      if (dflag) return result;else if (oddOut && oddIn) {
  5496        if (directed) {
  5497          if (startVertex && oddOut != startVertex) {
  5498            return result;
  5499          }
  5500  
  5501          startVertex = oddOut;
  5502        } else {
  5503          if (startVertex && oddOut != startVertex && oddIn != startVertex) {
  5504            return result;
  5505          } else if (!startVertex) {
  5506            startVertex = oddOut;
  5507          }
  5508        }
  5509      } else {
  5510        if (!startVertex) startVertex = eles[0].id();
  5511      }
  5512  
  5513      var walk = function walk(v) {
  5514        var currentNode = v;
  5515        var subtour = [v];
  5516        var adj, adjTail, adjHead;
  5517  
  5518        while (nodes[currentNode].length) {
  5519          adj = nodes[currentNode].shift();
  5520          adjTail = edges[adj][0];
  5521          adjHead = edges[adj][1];
  5522  
  5523          if (currentNode != adjHead) {
  5524            nodes[adjHead] = nodes[adjHead].filter(function (e) {
  5525              return e != adj;
  5526            });
  5527            currentNode = adjHead;
  5528          } else if (!directed && currentNode != adjTail) {
  5529            nodes[adjTail] = nodes[adjTail].filter(function (e) {
  5530              return e != adj;
  5531            });
  5532            currentNode = adjTail;
  5533          }
  5534  
  5535          subtour.unshift(adj);
  5536          subtour.unshift(currentNode);
  5537        }
  5538  
  5539        return subtour;
  5540      };
  5541  
  5542      var trail = [];
  5543      var subtour = [];
  5544      subtour = walk(startVertex);
  5545  
  5546      while (subtour.length != 1) {
  5547        if (nodes[subtour[0]].length == 0) {
  5548          trail.unshift(eles.getElementById(subtour.shift()));
  5549          trail.unshift(eles.getElementById(subtour.shift()));
  5550        } else {
  5551          subtour = walk(subtour.shift()).concat(subtour);
  5552        }
  5553      }
  5554  
  5555      trail.unshift(eles.getElementById(subtour.shift())); // final node
  5556  
  5557      for (var d in nodes) {
  5558        if (nodes[d].length) {
  5559          return result;
  5560        }
  5561      }
  5562  
  5563      result.found = true;
  5564      result.trail = this.spawn(trail);
  5565      return result;
  5566    }
  5567  };
  5568  
  5569  var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
  5570    var eles = this;
  5571    var nodes = {};
  5572    var id = 0;
  5573    var edgeCount = 0;
  5574    var components = [];
  5575    var stack = [];
  5576    var visitedEdges = {};
  5577  
  5578    var buildComponent = function buildComponent(x, y) {
  5579      var i = stack.length - 1;
  5580      var cutset = [];
  5581      var component = eles.spawn();
  5582  
  5583      while (stack[i].x != x || stack[i].y != y) {
  5584        cutset.push(stack.pop().edge);
  5585        i--;
  5586      }
  5587  
  5588      cutset.push(stack.pop().edge);
  5589      cutset.forEach(function (edge) {
  5590        var connectedNodes = edge.connectedNodes().intersection(eles);
  5591        component.merge(edge);
  5592        connectedNodes.forEach(function (node) {
  5593          var nodeId = node.id();
  5594          var connectedEdges = node.connectedEdges().intersection(eles);
  5595          component.merge(node);
  5596  
  5597          if (!nodes[nodeId].cutVertex) {
  5598            component.merge(connectedEdges);
  5599          } else {
  5600            component.merge(connectedEdges.filter(function (edge) {
  5601              return edge.isLoop();
  5602            }));
  5603          }
  5604        });
  5605      });
  5606      components.push(component);
  5607    };
  5608  
  5609    var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
  5610      if (root === parent) edgeCount += 1;
  5611      nodes[currentNode] = {
  5612        id: id,
  5613        low: id++,
  5614        cutVertex: false
  5615      };
  5616      var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
  5617  
  5618      if (edges.size() === 0) {
  5619        components.push(eles.spawn(eles.getElementById(currentNode)));
  5620      } else {
  5621        var sourceId, targetId, otherNodeId, edgeId;
  5622        edges.forEach(function (edge) {
  5623          sourceId = edge.source().id();
  5624          targetId = edge.target().id();
  5625          otherNodeId = sourceId === currentNode ? targetId : sourceId;
  5626  
  5627          if (otherNodeId !== parent) {
  5628            edgeId = edge.id();
  5629  
  5630            if (!visitedEdges[edgeId]) {
  5631              visitedEdges[edgeId] = true;
  5632              stack.push({
  5633                x: currentNode,
  5634                y: otherNodeId,
  5635                edge: edge
  5636              });
  5637            }
  5638  
  5639            if (!(otherNodeId in nodes)) {
  5640              biconnectedSearch(root, otherNodeId, currentNode);
  5641              nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
  5642  
  5643              if (nodes[currentNode].id <= nodes[otherNodeId].low) {
  5644                nodes[currentNode].cutVertex = true;
  5645                buildComponent(currentNode, otherNodeId);
  5646              }
  5647            } else {
  5648              nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
  5649            }
  5650          }
  5651        });
  5652      }
  5653    };
  5654  
  5655    eles.forEach(function (ele) {
  5656      if (ele.isNode()) {
  5657        var nodeId = ele.id();
  5658  
  5659        if (!(nodeId in nodes)) {
  5660          edgeCount = 0;
  5661          biconnectedSearch(nodeId, nodeId);
  5662          nodes[nodeId].cutVertex = edgeCount > 1;
  5663        }
  5664      }
  5665    });
  5666    var cutVertices = Object.keys(nodes).filter(function (id) {
  5667      return nodes[id].cutVertex;
  5668    }).map(function (id) {
  5669      return eles.getElementById(id);
  5670    });
  5671    return {
  5672      cut: eles.spawn(cutVertices),
  5673      components: components
  5674    };
  5675  };
  5676  
  5677  var hopcroftTarjanBiconnected$1 = {
  5678    hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
  5679    htbc: hopcroftTarjanBiconnected,
  5680    htb: hopcroftTarjanBiconnected,
  5681    hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
  5682  };
  5683  
  5684  var elesfn$c = {};
  5685  [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) {
  5686    extend(elesfn$c, props);
  5687  });
  5688  
  5689  /*!
  5690  Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
  5691  Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
  5692  Licensed under The MIT License (http://opensource.org/licenses/MIT)
  5693  */
  5694  
  5695  /*  promise states [Promises/A+ 2.1]  */
  5696  var STATE_PENDING = 0;
  5697  /*  [Promises/A+ 2.1.1]  */
  5698  
  5699  var STATE_FULFILLED = 1;
  5700  /*  [Promises/A+ 2.1.2]  */
  5701  
  5702  var STATE_REJECTED = 2;
  5703  /*  [Promises/A+ 2.1.3]  */
  5704  
  5705  /*  promise object constructor  */
  5706  
  5707  var api = function api(executor) {
  5708    /*  optionally support non-constructor/plain-function call  */
  5709    if (!(this instanceof api)) return new api(executor);
  5710    /*  initialize object  */
  5711  
  5712    this.id = 'Thenable/1.0.7';
  5713    this.state = STATE_PENDING;
  5714    /*  initial state  */
  5715  
  5716    this.fulfillValue = undefined;
  5717    /*  initial value  */
  5718  
  5719    /*  [Promises/A+ 1.3, 2.1.2.2]  */
  5720  
  5721    this.rejectReason = undefined;
  5722    /*  initial reason */
  5723  
  5724    /*  [Promises/A+ 1.5, 2.1.3.2]  */
  5725  
  5726    this.onFulfilled = [];
  5727    /*  initial handlers  */
  5728  
  5729    this.onRejected = [];
  5730    /*  initial handlers  */
  5731  
  5732    /*  provide optional information-hiding proxy  */
  5733  
  5734    this.proxy = {
  5735      then: this.then.bind(this)
  5736    };
  5737    /*  support optional executor function  */
  5738  
  5739    if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
  5740  };
  5741  /*  promise API methods  */
  5742  
  5743  
  5744  api.prototype = {
  5745    /*  promise resolving methods  */
  5746    fulfill: function fulfill(value) {
  5747      return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
  5748    },
  5749    reject: function reject(value) {
  5750      return deliver(this, STATE_REJECTED, 'rejectReason', value);
  5751    },
  5752  
  5753    /*  "The then Method" [Promises/A+ 1.1, 1.2, 2.2]  */
  5754    then: function then(onFulfilled, onRejected) {
  5755      var curr = this;
  5756      var next = new api();
  5757      /*  [Promises/A+ 2.2.7]  */
  5758  
  5759      curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
  5760      /*  [Promises/A+ 2.2.2/2.2.6]  */
  5761  
  5762      curr.onRejected.push(resolver(onRejected, next, 'reject'));
  5763      /*  [Promises/A+ 2.2.3/2.2.6]  */
  5764  
  5765      execute(curr);
  5766      return next.proxy;
  5767      /*  [Promises/A+ 2.2.7, 3.3]  */
  5768    }
  5769  };
  5770  /*  deliver an action  */
  5771  
  5772  var deliver = function deliver(curr, state, name, value) {
  5773    if (curr.state === STATE_PENDING) {
  5774      curr.state = state;
  5775      /*  [Promises/A+ 2.1.2.1, 2.1.3.1]  */
  5776  
  5777      curr[name] = value;
  5778      /*  [Promises/A+ 2.1.2.2, 2.1.3.2]  */
  5779  
  5780      execute(curr);
  5781    }
  5782  
  5783    return curr;
  5784  };
  5785  /*  execute all handlers  */
  5786  
  5787  
  5788  var execute = function execute(curr) {
  5789    if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
  5790  };
  5791  /*  execute particular set of handlers  */
  5792  
  5793  
  5794  var execute_handlers = function execute_handlers(curr, name, value) {
  5795    /* global setImmediate: true */
  5796  
  5797    /* global setTimeout: true */
  5798  
  5799    /*  short-circuit processing  */
  5800    if (curr[name].length === 0) return;
  5801    /*  iterate over all handlers, exactly once  */
  5802  
  5803    var handlers = curr[name];
  5804    curr[name] = [];
  5805    /*  [Promises/A+ 2.2.2.3, 2.2.3.3]  */
  5806  
  5807    var func = function func() {
  5808      for (var i = 0; i < handlers.length; i++) {
  5809        handlers[i](value);
  5810      }
  5811      /*  [Promises/A+ 2.2.5]  */
  5812  
  5813    };
  5814    /*  execute procedure asynchronously  */
  5815  
  5816    /*  [Promises/A+ 2.2.4, 3.1]  */
  5817  
  5818  
  5819    if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
  5820  };
  5821  /*  generate a resolver function  */
  5822  
  5823  
  5824  var resolver = function resolver(cb, next, method) {
  5825    return function (value) {
  5826      if (typeof cb !== 'function')
  5827        /*  [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4]  */
  5828        next[method].call(next, value);
  5829        /*  [Promises/A+ 2.2.7.3, 2.2.7.4]  */
  5830      else {
  5831          var result;
  5832  
  5833          try {
  5834            result = cb(value);
  5835          }
  5836          /*  [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2]  */
  5837          catch (e) {
  5838            next.reject(e);
  5839            /*  [Promises/A+ 2.2.7.2]  */
  5840  
  5841            return;
  5842          }
  5843  
  5844          resolve(next, result);
  5845          /*  [Promises/A+ 2.2.7.1]  */
  5846        }
  5847    };
  5848  };
  5849  /*  "Promise Resolution Procedure"  */
  5850  
  5851  /*  [Promises/A+ 2.3]  */
  5852  
  5853  
  5854  var resolve = function resolve(promise, x) {
  5855    /*  sanity check arguments  */
  5856  
  5857    /*  [Promises/A+ 2.3.1]  */
  5858    if (promise === x || promise.proxy === x) {
  5859      promise.reject(new TypeError('cannot resolve promise with itself'));
  5860      return;
  5861    }
  5862    /*  surgically check for a "then" method
  5863      (mainly to just call the "getter" of "then" only once)  */
  5864  
  5865  
  5866    var then;
  5867  
  5868    if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
  5869      try {
  5870        then = x.then;
  5871      }
  5872      /*  [Promises/A+ 2.3.3.1, 3.5]  */
  5873      catch (e) {
  5874        promise.reject(e);
  5875        /*  [Promises/A+ 2.3.3.2]  */
  5876  
  5877        return;
  5878      }
  5879    }
  5880    /*  handle own Thenables    [Promises/A+ 2.3.2]
  5881      and similar "thenables" [Promises/A+ 2.3.3]  */
  5882  
  5883  
  5884    if (typeof then === 'function') {
  5885      var resolved = false;
  5886  
  5887      try {
  5888        /*  call retrieved "then" method */
  5889  
  5890        /*  [Promises/A+ 2.3.3.3]  */
  5891        then.call(x,
  5892        /*  resolvePromise  */
  5893  
  5894        /*  [Promises/A+ 2.3.3.3.1]  */
  5895        function (y) {
  5896          if (resolved) return;
  5897          resolved = true;
  5898          /*  [Promises/A+ 2.3.3.3.3]  */
  5899  
  5900          if (y === x)
  5901            /*  [Promises/A+ 3.6]  */
  5902            promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
  5903        },
  5904        /*  rejectPromise  */
  5905  
  5906        /*  [Promises/A+ 2.3.3.3.2]  */
  5907        function (r) {
  5908          if (resolved) return;
  5909          resolved = true;
  5910          /*  [Promises/A+ 2.3.3.3.3]  */
  5911  
  5912          promise.reject(r);
  5913        });
  5914      } catch (e) {
  5915        if (!resolved)
  5916          /*  [Promises/A+ 2.3.3.3.3]  */
  5917          promise.reject(e);
  5918        /*  [Promises/A+ 2.3.3.3.4]  */
  5919      }
  5920  
  5921      return;
  5922    }
  5923    /*  handle other values  */
  5924  
  5925  
  5926    promise.fulfill(x);
  5927    /*  [Promises/A+ 2.3.4, 2.3.3.4]  */
  5928  }; // so we always have Promise.all()
  5929  
  5930  
  5931  api.all = function (ps) {
  5932    return new api(function (resolveAll, rejectAll) {
  5933      var vals = new Array(ps.length);
  5934      var doneCount = 0;
  5935  
  5936      var fulfill = function fulfill(i, val) {
  5937        vals[i] = val;
  5938        doneCount++;
  5939  
  5940        if (doneCount === ps.length) {
  5941          resolveAll(vals);
  5942        }
  5943      };
  5944  
  5945      for (var i = 0; i < ps.length; i++) {
  5946        (function (i) {
  5947          var p = ps[i];
  5948          var isPromise = p != null && p.then != null;
  5949  
  5950          if (isPromise) {
  5951            p.then(function (val) {
  5952              fulfill(i, val);
  5953            }, function (err) {
  5954              rejectAll(err);
  5955            });
  5956          } else {
  5957            var val = p;
  5958            fulfill(i, val);
  5959          }
  5960        })(i);
  5961      }
  5962    });
  5963  };
  5964  
  5965  api.resolve = function (val) {
  5966    return new api(function (resolve, reject) {
  5967      resolve(val);
  5968    });
  5969  };
  5970  
  5971  api.reject = function (val) {
  5972    return new api(function (resolve, reject) {
  5973      reject(val);
  5974    });
  5975  };
  5976  
  5977  var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
  5978  
  5979  var Animation = function Animation(target, opts, opts2) {
  5980    var isCore = core(target);
  5981    var isEle = !isCore;
  5982  
  5983    var _p = this._private = extend({
  5984      duration: 1000
  5985    }, opts, opts2);
  5986  
  5987    _p.target = target;
  5988    _p.style = _p.style || _p.css;
  5989    _p.started = false;
  5990    _p.playing = false;
  5991    _p.hooked = false;
  5992    _p.applying = false;
  5993    _p.progress = 0;
  5994    _p.completes = [];
  5995    _p.frames = [];
  5996  
  5997    if (_p.complete && fn(_p.complete)) {
  5998      _p.completes.push(_p.complete);
  5999    }
  6000  
  6001    if (isEle) {
  6002      var pos = target.position();
  6003      _p.startPosition = _p.startPosition || {
  6004        x: pos.x,
  6005        y: pos.y
  6006      };
  6007      _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
  6008    }
  6009  
  6010    if (isCore) {
  6011      var pan = target.pan();
  6012      _p.startPan = {
  6013        x: pan.x,
  6014        y: pan.y
  6015      };
  6016      _p.startZoom = target.zoom();
  6017    } // for future timeline/animations impl
  6018  
  6019  
  6020    this.length = 1;
  6021    this[0] = this;
  6022  };
  6023  
  6024  var anifn = Animation.prototype;
  6025  extend(anifn, {
  6026    instanceString: function instanceString() {
  6027      return 'animation';
  6028    },
  6029    hook: function hook() {
  6030      var _p = this._private;
  6031  
  6032      if (!_p.hooked) {
  6033        // add to target's animation queue
  6034        var q;
  6035        var tAni = _p.target._private.animation;
  6036  
  6037        if (_p.queue) {
  6038          q = tAni.queue;
  6039        } else {
  6040          q = tAni.current;
  6041        }
  6042  
  6043        q.push(this); // add to the animation loop pool
  6044  
  6045        if (elementOrCollection(_p.target)) {
  6046          _p.target.cy().addToAnimationPool(_p.target);
  6047        }
  6048  
  6049        _p.hooked = true;
  6050      }
  6051  
  6052      return this;
  6053    },
  6054    play: function play() {
  6055      var _p = this._private; // autorewind
  6056  
  6057      if (_p.progress === 1) {
  6058        _p.progress = 0;
  6059      }
  6060  
  6061      _p.playing = true;
  6062      _p.started = false; // needs to be started by animation loop
  6063  
  6064      _p.stopped = false;
  6065      this.hook(); // the animation loop will start the animation...
  6066  
  6067      return this;
  6068    },
  6069    playing: function playing() {
  6070      return this._private.playing;
  6071    },
  6072    apply: function apply() {
  6073      var _p = this._private;
  6074      _p.applying = true;
  6075      _p.started = false; // needs to be started by animation loop
  6076  
  6077      _p.stopped = false;
  6078      this.hook(); // the animation loop will apply the animation at this progress
  6079  
  6080      return this;
  6081    },
  6082    applying: function applying() {
  6083      return this._private.applying;
  6084    },
  6085    pause: function pause() {
  6086      var _p = this._private;
  6087      _p.playing = false;
  6088      _p.started = false;
  6089      return this;
  6090    },
  6091    stop: function stop() {
  6092      var _p = this._private;
  6093      _p.playing = false;
  6094      _p.started = false;
  6095      _p.stopped = true; // to be removed from animation queues
  6096  
  6097      return this;
  6098    },
  6099    rewind: function rewind() {
  6100      return this.progress(0);
  6101    },
  6102    fastforward: function fastforward() {
  6103      return this.progress(1);
  6104    },
  6105    time: function time(t) {
  6106      var _p = this._private;
  6107  
  6108      if (t === undefined) {
  6109        return _p.progress * _p.duration;
  6110      } else {
  6111        return this.progress(t / _p.duration);
  6112      }
  6113    },
  6114    progress: function progress(p) {
  6115      var _p = this._private;
  6116      var wasPlaying = _p.playing;
  6117  
  6118      if (p === undefined) {
  6119        return _p.progress;
  6120      } else {
  6121        if (wasPlaying) {
  6122          this.pause();
  6123        }
  6124  
  6125        _p.progress = p;
  6126        _p.started = false;
  6127  
  6128        if (wasPlaying) {
  6129          this.play();
  6130        }
  6131      }
  6132  
  6133      return this;
  6134    },
  6135    completed: function completed() {
  6136      return this._private.progress === 1;
  6137    },
  6138    reverse: function reverse() {
  6139      var _p = this._private;
  6140      var wasPlaying = _p.playing;
  6141  
  6142      if (wasPlaying) {
  6143        this.pause();
  6144      }
  6145  
  6146      _p.progress = 1 - _p.progress;
  6147      _p.started = false;
  6148  
  6149      var swap = function swap(a, b) {
  6150        var _pa = _p[a];
  6151  
  6152        if (_pa == null) {
  6153          return;
  6154        }
  6155  
  6156        _p[a] = _p[b];
  6157        _p[b] = _pa;
  6158      };
  6159  
  6160      swap('zoom', 'startZoom');
  6161      swap('pan', 'startPan');
  6162      swap('position', 'startPosition'); // swap styles
  6163  
  6164      if (_p.style) {
  6165        for (var i = 0; i < _p.style.length; i++) {
  6166          var prop = _p.style[i];
  6167          var name = prop.name;
  6168          var startStyleProp = _p.startStyle[name];
  6169          _p.startStyle[name] = prop;
  6170          _p.style[i] = startStyleProp;
  6171        }
  6172      }
  6173  
  6174      if (wasPlaying) {
  6175        this.play();
  6176      }
  6177  
  6178      return this;
  6179    },
  6180    promise: function promise(type) {
  6181      var _p = this._private;
  6182      var arr;
  6183  
  6184      switch (type) {
  6185        case 'frame':
  6186          arr = _p.frames;
  6187          break;
  6188  
  6189        default:
  6190        case 'complete':
  6191        case 'completed':
  6192          arr = _p.completes;
  6193      }
  6194  
  6195      return new Promise$1(function (resolve, reject) {
  6196        arr.push(function () {
  6197          resolve();
  6198        });
  6199      });
  6200    }
  6201  });
  6202  anifn.complete = anifn.completed;
  6203  anifn.run = anifn.play;
  6204  anifn.running = anifn.playing;
  6205  
  6206  var define = {
  6207    animated: function animated() {
  6208      return function animatedImpl() {
  6209        var self = this;
  6210        var selfIsArrayLike = self.length !== undefined;
  6211        var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6212  
  6213        var cy = this._private.cy || this;
  6214  
  6215        if (!cy.styleEnabled()) {
  6216          return false;
  6217        }
  6218  
  6219        var ele = all[0];
  6220  
  6221        if (ele) {
  6222          return ele._private.animation.current.length > 0;
  6223        }
  6224      };
  6225    },
  6226    // animated
  6227    clearQueue: function clearQueue() {
  6228      return function clearQueueImpl() {
  6229        var self = this;
  6230        var selfIsArrayLike = self.length !== undefined;
  6231        var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6232  
  6233        var cy = this._private.cy || this;
  6234  
  6235        if (!cy.styleEnabled()) {
  6236          return this;
  6237        }
  6238  
  6239        for (var i = 0; i < all.length; i++) {
  6240          var ele = all[i];
  6241          ele._private.animation.queue = [];
  6242        }
  6243  
  6244        return this;
  6245      };
  6246    },
  6247    // clearQueue
  6248    delay: function delay() {
  6249      return function delayImpl(time, complete) {
  6250        var cy = this._private.cy || this;
  6251  
  6252        if (!cy.styleEnabled()) {
  6253          return this;
  6254        }
  6255  
  6256        return this.animate({
  6257          delay: time,
  6258          duration: time,
  6259          complete: complete
  6260        });
  6261      };
  6262    },
  6263    // delay
  6264    delayAnimation: function delayAnimation() {
  6265      return function delayAnimationImpl(time, complete) {
  6266        var cy = this._private.cy || this;
  6267  
  6268        if (!cy.styleEnabled()) {
  6269          return this;
  6270        }
  6271  
  6272        return this.animation({
  6273          delay: time,
  6274          duration: time,
  6275          complete: complete
  6276        });
  6277      };
  6278    },
  6279    // delay
  6280    animation: function animation() {
  6281      return function animationImpl(properties, params) {
  6282        var self = this;
  6283        var selfIsArrayLike = self.length !== undefined;
  6284        var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6285  
  6286        var cy = this._private.cy || this;
  6287        var isCore = !selfIsArrayLike;
  6288        var isEles = !isCore;
  6289  
  6290        if (!cy.styleEnabled()) {
  6291          return this;
  6292        }
  6293  
  6294        var style = cy.style();
  6295        properties = extend({}, properties, params);
  6296        var propertiesEmpty = Object.keys(properties).length === 0;
  6297  
  6298        if (propertiesEmpty) {
  6299          return new Animation(all[0], properties); // nothing to animate
  6300        }
  6301  
  6302        if (properties.duration === undefined) {
  6303          properties.duration = 400;
  6304        }
  6305  
  6306        switch (properties.duration) {
  6307          case 'slow':
  6308            properties.duration = 600;
  6309            break;
  6310  
  6311          case 'fast':
  6312            properties.duration = 200;
  6313            break;
  6314        }
  6315  
  6316        if (isEles) {
  6317          properties.style = style.getPropsList(properties.style || properties.css);
  6318          properties.css = undefined;
  6319        }
  6320  
  6321        if (isEles && properties.renderedPosition != null) {
  6322          var rpos = properties.renderedPosition;
  6323          var pan = cy.pan();
  6324          var zoom = cy.zoom();
  6325          properties.position = renderedToModelPosition(rpos, zoom, pan);
  6326        } // override pan w/ panBy if set
  6327  
  6328  
  6329        if (isCore && properties.panBy != null) {
  6330          var panBy = properties.panBy;
  6331          var cyPan = cy.pan();
  6332          properties.pan = {
  6333            x: cyPan.x + panBy.x,
  6334            y: cyPan.y + panBy.y
  6335          };
  6336        } // override pan w/ center if set
  6337  
  6338  
  6339        var center = properties.center || properties.centre;
  6340  
  6341        if (isCore && center != null) {
  6342          var centerPan = cy.getCenterPan(center.eles, properties.zoom);
  6343  
  6344          if (centerPan != null) {
  6345            properties.pan = centerPan;
  6346          }
  6347        } // override pan & zoom w/ fit if set
  6348  
  6349  
  6350        if (isCore && properties.fit != null) {
  6351          var fit = properties.fit;
  6352          var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
  6353  
  6354          if (fitVp != null) {
  6355            properties.pan = fitVp.pan;
  6356            properties.zoom = fitVp.zoom;
  6357          }
  6358        } // override zoom (& potentially pan) w/ zoom obj if set
  6359  
  6360  
  6361        if (isCore && plainObject(properties.zoom)) {
  6362          var vp = cy.getZoomedViewport(properties.zoom);
  6363  
  6364          if (vp != null) {
  6365            if (vp.zoomed) {
  6366              properties.zoom = vp.zoom;
  6367            }
  6368  
  6369            if (vp.panned) {
  6370              properties.pan = vp.pan;
  6371            }
  6372          } else {
  6373            properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
  6374          }
  6375        }
  6376  
  6377        return new Animation(all[0], properties);
  6378      };
  6379    },
  6380    // animate
  6381    animate: function animate() {
  6382      return function animateImpl(properties, params) {
  6383        var self = this;
  6384        var selfIsArrayLike = self.length !== undefined;
  6385        var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6386  
  6387        var cy = this._private.cy || this;
  6388  
  6389        if (!cy.styleEnabled()) {
  6390          return this;
  6391        }
  6392  
  6393        if (params) {
  6394          properties = extend({}, properties, params);
  6395        } // manually hook and run the animation
  6396  
  6397  
  6398        for (var i = 0; i < all.length; i++) {
  6399          var ele = all[i];
  6400          var queue = ele.animated() && (properties.queue === undefined || properties.queue);
  6401          var ani = ele.animation(properties, queue ? {
  6402            queue: true
  6403          } : undefined);
  6404          ani.play();
  6405        }
  6406  
  6407        return this; // chaining
  6408      };
  6409    },
  6410    // animate
  6411    stop: function stop() {
  6412      return function stopImpl(clearQueue, jumpToEnd) {
  6413        var self = this;
  6414        var selfIsArrayLike = self.length !== undefined;
  6415        var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6416  
  6417        var cy = this._private.cy || this;
  6418  
  6419        if (!cy.styleEnabled()) {
  6420          return this;
  6421        }
  6422  
  6423        for (var i = 0; i < all.length; i++) {
  6424          var ele = all[i];
  6425          var _p = ele._private;
  6426          var anis = _p.animation.current;
  6427  
  6428          for (var j = 0; j < anis.length; j++) {
  6429            var ani = anis[j];
  6430            var ani_p = ani._private;
  6431  
  6432            if (jumpToEnd) {
  6433              // next iteration of the animation loop, the animation
  6434              // will go straight to the end and be removed
  6435              ani_p.duration = 0;
  6436            }
  6437          } // clear the queue of future animations
  6438  
  6439  
  6440          if (clearQueue) {
  6441            _p.animation.queue = [];
  6442          }
  6443  
  6444          if (!jumpToEnd) {
  6445            _p.animation.current = [];
  6446          }
  6447        } // we have to notify (the animation loop doesn't do it for us on `stop`)
  6448  
  6449  
  6450        cy.notify('draw');
  6451        return this;
  6452      };
  6453    } // stop
  6454  
  6455  }; // define
  6456  
  6457  var define$1 = {
  6458    // access data field
  6459    data: function data(params) {
  6460      var defaults = {
  6461        field: 'data',
  6462        bindingEvent: 'data',
  6463        allowBinding: false,
  6464        allowSetting: false,
  6465        allowGetting: false,
  6466        settingEvent: 'data',
  6467        settingTriggersEvent: false,
  6468        triggerFnName: 'trigger',
  6469        immutableKeys: {},
  6470        // key => true if immutable
  6471        updateStyle: false,
  6472        beforeGet: function beforeGet(self) {},
  6473        beforeSet: function beforeSet(self, obj) {},
  6474        onSet: function onSet(self) {},
  6475        canSet: function canSet(self) {
  6476          return true;
  6477        }
  6478      };
  6479      params = extend({}, defaults, params);
  6480      return function dataImpl(name, value) {
  6481        var p = params;
  6482        var self = this;
  6483        var selfIsArrayLike = self.length !== undefined;
  6484        var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6485  
  6486        var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
  6487  
  6488        if (string(name)) {
  6489          // set or get property
  6490          // .data('foo')
  6491          if (p.allowGetting && value === undefined) {
  6492            // get
  6493            var ret;
  6494  
  6495            if (single) {
  6496              p.beforeGet(single);
  6497              ret = single._private[p.field][name];
  6498            }
  6499  
  6500            return ret; // .data('foo', 'bar')
  6501          } else if (p.allowSetting && value !== undefined) {
  6502            // set
  6503            var valid = !p.immutableKeys[name];
  6504  
  6505            if (valid) {
  6506              var change = _defineProperty({}, name, value);
  6507  
  6508              p.beforeSet(self, change);
  6509  
  6510              for (var i = 0, l = all.length; i < l; i++) {
  6511                var ele = all[i];
  6512  
  6513                if (p.canSet(ele)) {
  6514                  ele._private[p.field][name] = value;
  6515                }
  6516              } // update mappers if asked
  6517  
  6518  
  6519              if (p.updateStyle) {
  6520                self.updateStyle();
  6521              } // call onSet callback
  6522  
  6523  
  6524              p.onSet(self);
  6525  
  6526              if (p.settingTriggersEvent) {
  6527                self[p.triggerFnName](p.settingEvent);
  6528              }
  6529            }
  6530          } // .data({ 'foo': 'bar' })
  6531  
  6532        } else if (p.allowSetting && plainObject(name)) {
  6533          // extend
  6534          var obj = name;
  6535          var k, v;
  6536          var keys = Object.keys(obj);
  6537          p.beforeSet(self, obj);
  6538  
  6539          for (var _i = 0; _i < keys.length; _i++) {
  6540            k = keys[_i];
  6541            v = obj[k];
  6542  
  6543            var _valid = !p.immutableKeys[k];
  6544  
  6545            if (_valid) {
  6546              for (var j = 0; j < all.length; j++) {
  6547                var _ele = all[j];
  6548  
  6549                if (p.canSet(_ele)) {
  6550                  _ele._private[p.field][k] = v;
  6551                }
  6552              }
  6553            }
  6554          } // update mappers if asked
  6555  
  6556  
  6557          if (p.updateStyle) {
  6558            self.updateStyle();
  6559          } // call onSet callback
  6560  
  6561  
  6562          p.onSet(self);
  6563  
  6564          if (p.settingTriggersEvent) {
  6565            self[p.triggerFnName](p.settingEvent);
  6566          } // .data(function(){ ... })
  6567  
  6568        } else if (p.allowBinding && fn(name)) {
  6569          // bind to event
  6570          var fn$1 = name;
  6571          self.on(p.bindingEvent, fn$1); // .data()
  6572        } else if (p.allowGetting && name === undefined) {
  6573          // get whole object
  6574          var _ret;
  6575  
  6576          if (single) {
  6577            p.beforeGet(single);
  6578            _ret = single._private[p.field];
  6579          }
  6580  
  6581          return _ret;
  6582        }
  6583  
  6584        return self; // maintain chainability
  6585      }; // function
  6586    },
  6587    // data
  6588    // remove data field
  6589    removeData: function removeData(params) {
  6590      var defaults = {
  6591        field: 'data',
  6592        event: 'data',
  6593        triggerFnName: 'trigger',
  6594        triggerEvent: false,
  6595        immutableKeys: {} // key => true if immutable
  6596  
  6597      };
  6598      params = extend({}, defaults, params);
  6599      return function removeDataImpl(names) {
  6600        var p = params;
  6601        var self = this;
  6602        var selfIsArrayLike = self.length !== undefined;
  6603        var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6604        // .removeData('foo bar')
  6605  
  6606        if (string(names)) {
  6607          // then get the list of keys, and delete them
  6608          var keys = names.split(/\s+/);
  6609          var l = keys.length;
  6610  
  6611          for (var i = 0; i < l; i++) {
  6612            // delete each non-empty key
  6613            var key = keys[i];
  6614  
  6615            if (emptyString(key)) {
  6616              continue;
  6617            }
  6618  
  6619            var valid = !p.immutableKeys[key]; // not valid if immutable
  6620  
  6621            if (valid) {
  6622              for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
  6623                all[i_a]._private[p.field][key] = undefined;
  6624              }
  6625            }
  6626          }
  6627  
  6628          if (p.triggerEvent) {
  6629            self[p.triggerFnName](p.event);
  6630          } // .removeData()
  6631  
  6632        } else if (names === undefined) {
  6633          // then delete all keys
  6634          for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
  6635            var _privateFields = all[_i_a]._private[p.field];
  6636  
  6637            var _keys = Object.keys(_privateFields);
  6638  
  6639            for (var _i2 = 0; _i2 < _keys.length; _i2++) {
  6640              var _key = _keys[_i2];
  6641              var validKeyToDelete = !p.immutableKeys[_key];
  6642  
  6643              if (validKeyToDelete) {
  6644                _privateFields[_key] = undefined;
  6645              }
  6646            }
  6647          }
  6648  
  6649          if (p.triggerEvent) {
  6650            self[p.triggerFnName](p.event);
  6651          }
  6652        }
  6653  
  6654        return self; // maintain chaining
  6655      }; // function
  6656    } // removeData
  6657  
  6658  }; // define
  6659  
  6660  var define$2 = {
  6661    eventAliasesOn: function eventAliasesOn(proto) {
  6662      var p = proto;
  6663      p.addListener = p.listen = p.bind = p.on;
  6664      p.unlisten = p.unbind = p.off = p.removeListener;
  6665      p.trigger = p.emit; // this is just a wrapper alias of .on()
  6666  
  6667      p.pon = p.promiseOn = function (events, selector) {
  6668        var self = this;
  6669        var args = Array.prototype.slice.call(arguments, 0);
  6670        return new Promise$1(function (resolve, reject) {
  6671          var callback = function callback(e) {
  6672            self.off.apply(self, offArgs);
  6673            resolve(e);
  6674          };
  6675  
  6676          var onArgs = args.concat([callback]);
  6677          var offArgs = onArgs.concat([]);
  6678          self.on.apply(self, onArgs);
  6679        });
  6680      };
  6681    }
  6682  }; // define
  6683  
  6684  // use this module to cherry pick functions into your prototype
  6685  var define$3 = {};
  6686  [define, define$1, define$2].forEach(function (m) {
  6687    extend(define$3, m);
  6688  });
  6689  
  6690  var elesfn$d = {
  6691    animate: define$3.animate(),
  6692    animation: define$3.animation(),
  6693    animated: define$3.animated(),
  6694    clearQueue: define$3.clearQueue(),
  6695    delay: define$3.delay(),
  6696    delayAnimation: define$3.delayAnimation(),
  6697    stop: define$3.stop()
  6698  };
  6699  
  6700  var elesfn$e = {
  6701    classes: function classes(_classes) {
  6702      var self = this;
  6703  
  6704      if (_classes === undefined) {
  6705        var ret = [];
  6706  
  6707        self[0]._private.classes.forEach(function (cls) {
  6708          return ret.push(cls);
  6709        });
  6710  
  6711        return ret;
  6712      } else if (!array(_classes)) {
  6713        // extract classes from string
  6714        _classes = (_classes || '').match(/\S+/g) || [];
  6715      }
  6716  
  6717      var changed = [];
  6718      var classesSet = new Set$1(_classes); // check and update each ele
  6719  
  6720      for (var j = 0; j < self.length; j++) {
  6721        var ele = self[j];
  6722        var _p = ele._private;
  6723        var eleClasses = _p.classes;
  6724        var changedEle = false; // check if ele has all of the passed classes
  6725  
  6726        for (var i = 0; i < _classes.length; i++) {
  6727          var cls = _classes[i];
  6728          var eleHasClass = eleClasses.has(cls);
  6729  
  6730          if (!eleHasClass) {
  6731            changedEle = true;
  6732            break;
  6733          }
  6734        } // check if ele has classes outside of those passed
  6735  
  6736  
  6737        if (!changedEle) {
  6738          changedEle = eleClasses.size !== _classes.length;
  6739        }
  6740  
  6741        if (changedEle) {
  6742          _p.classes = classesSet;
  6743          changed.push(ele);
  6744        }
  6745      } // trigger update style on those eles that had class changes
  6746  
  6747  
  6748      if (changed.length > 0) {
  6749        this.spawn(changed).updateStyle().emit('class');
  6750      }
  6751  
  6752      return self;
  6753    },
  6754    addClass: function addClass(classes) {
  6755      return this.toggleClass(classes, true);
  6756    },
  6757    hasClass: function hasClass(className) {
  6758      var ele = this[0];
  6759      return ele != null && ele._private.classes.has(className);
  6760    },
  6761    toggleClass: function toggleClass(classes, toggle) {
  6762      if (!array(classes)) {
  6763        // extract classes from string
  6764        classes = classes.match(/\S+/g) || [];
  6765      }
  6766  
  6767      var self = this;
  6768      var toggleUndefd = toggle === undefined;
  6769      var changed = []; // eles who had classes changed
  6770  
  6771      for (var i = 0, il = self.length; i < il; i++) {
  6772        var ele = self[i];
  6773        var eleClasses = ele._private.classes;
  6774        var changedEle = false;
  6775  
  6776        for (var j = 0; j < classes.length; j++) {
  6777          var cls = classes[j];
  6778          var hasClass = eleClasses.has(cls);
  6779          var changedNow = false;
  6780  
  6781          if (toggle || toggleUndefd && !hasClass) {
  6782            eleClasses.add(cls);
  6783            changedNow = true;
  6784          } else if (!toggle || toggleUndefd && hasClass) {
  6785            eleClasses["delete"](cls);
  6786            changedNow = true;
  6787          }
  6788  
  6789          if (!changedEle && changedNow) {
  6790            changed.push(ele);
  6791            changedEle = true;
  6792          }
  6793        } // for j classes
  6794  
  6795      } // for i eles
  6796      // trigger update style on those eles that had class changes
  6797  
  6798  
  6799      if (changed.length > 0) {
  6800        this.spawn(changed).updateStyle().emit('class');
  6801      }
  6802  
  6803      return self;
  6804    },
  6805    removeClass: function removeClass(classes) {
  6806      return this.toggleClass(classes, false);
  6807    },
  6808    flashClass: function flashClass(classes, duration) {
  6809      var self = this;
  6810  
  6811      if (duration == null) {
  6812        duration = 250;
  6813      } else if (duration === 0) {
  6814        return self; // nothing to do really
  6815      }
  6816  
  6817      self.addClass(classes);
  6818      setTimeout(function () {
  6819        self.removeClass(classes);
  6820      }, duration);
  6821      return self;
  6822    }
  6823  };
  6824  elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
  6825  
  6826  var tokens = {
  6827    metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
  6828    // chars we need to escape in let names, etc
  6829    comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
  6830    // binary comparison op (used in data selectors)
  6831    boolOp: '\\?|\\!|\\^',
  6832    // boolean (unary) operators (used in data selectors)
  6833    string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
  6834    // string literals (used in data selectors) -- doublequotes | singlequotes
  6835    number: number$1,
  6836    // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
  6837    meta: 'degree|indegree|outdegree',
  6838    // allowed metadata fields (i.e. allowed functions to use from Collection)
  6839    separator: '\\s*,\\s*',
  6840    // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
  6841    descendant: '\\s+',
  6842    child: '\\s+>\\s+',
  6843    subject: '\\$',
  6844    group: 'node|edge|\\*',
  6845    directedEdge: '\\s+->\\s+',
  6846    undirectedEdge: '\\s+<->\\s+'
  6847  };
  6848  tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name
  6849  
  6850  tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
  6851  
  6852  tokens.className = tokens.variable; // a class name (follows variable conventions)
  6853  
  6854  tokens.id = tokens.variable; // an element id (follows variable conventions)
  6855  
  6856  (function () {
  6857    var ops, op, i; // add @ variants to comparatorOp
  6858  
  6859    ops = tokens.comparatorOp.split('|');
  6860  
  6861    for (i = 0; i < ops.length; i++) {
  6862      op = ops[i];
  6863      tokens.comparatorOp += '|@' + op;
  6864    } // add ! variants to comparatorOp
  6865  
  6866  
  6867    ops = tokens.comparatorOp.split('|');
  6868  
  6869    for (i = 0; i < ops.length; i++) {
  6870      op = ops[i];
  6871  
  6872      if (op.indexOf('!') >= 0) {
  6873        continue;
  6874      } // skip ops that explicitly contain !
  6875  
  6876  
  6877      if (op === '=') {
  6878        continue;
  6879      } // skip = b/c != is explicitly defined
  6880  
  6881  
  6882      tokens.comparatorOp += '|\\!' + op;
  6883    }
  6884  })();
  6885  
  6886  /**
  6887   * Make a new query object
  6888   *
  6889   * @prop type {Type} The type enum (int) of the query
  6890   * @prop checks List of checks to make against an ele to test for a match
  6891   */
  6892  var newQuery = function newQuery() {
  6893    return {
  6894      checks: []
  6895    };
  6896  };
  6897  
  6898  /**
  6899   * A check type enum-like object.  Uses integer values for fast match() lookup.
  6900   * The ordering does not matter as long as the ints are unique.
  6901   */
  6902  var Type = {
  6903    /** E.g. node */
  6904    GROUP: 0,
  6905  
  6906    /** A collection of elements */
  6907    COLLECTION: 1,
  6908  
  6909    /** A filter(ele) function */
  6910    FILTER: 2,
  6911  
  6912    /** E.g. [foo > 1] */
  6913    DATA_COMPARE: 3,
  6914  
  6915    /** E.g. [foo] */
  6916    DATA_EXIST: 4,
  6917  
  6918    /** E.g. [?foo] */
  6919    DATA_BOOL: 5,
  6920  
  6921    /** E.g. [[degree > 2]] */
  6922    META_COMPARE: 6,
  6923  
  6924    /** E.g. :selected */
  6925    STATE: 7,
  6926  
  6927    /** E.g. #foo */
  6928    ID: 8,
  6929  
  6930    /** E.g. .foo */
  6931    CLASS: 9,
  6932  
  6933    /** E.g. #foo <-> #bar */
  6934    UNDIRECTED_EDGE: 10,
  6935  
  6936    /** E.g. #foo -> #bar */
  6937    DIRECTED_EDGE: 11,
  6938  
  6939    /** E.g. $#foo -> #bar */
  6940    NODE_SOURCE: 12,
  6941  
  6942    /** E.g. #foo -> $#bar */
  6943    NODE_TARGET: 13,
  6944  
  6945    /** E.g. $#foo <-> #bar */
  6946    NODE_NEIGHBOR: 14,
  6947  
  6948    /** E.g. #foo > #bar */
  6949    CHILD: 15,
  6950  
  6951    /** E.g. #foo #bar */
  6952    DESCENDANT: 16,
  6953  
  6954    /** E.g. $#foo > #bar */
  6955    PARENT: 17,
  6956  
  6957    /** E.g. $#foo #bar */
  6958    ANCESTOR: 18,
  6959  
  6960    /** E.g. #foo > $bar > #baz */
  6961    COMPOUND_SPLIT: 19,
  6962  
  6963    /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
  6964    TRUE: 20
  6965  };
  6966  
  6967  var stateSelectors = [{
  6968    selector: ':selected',
  6969    matches: function matches(ele) {
  6970      return ele.selected();
  6971    }
  6972  }, {
  6973    selector: ':unselected',
  6974    matches: function matches(ele) {
  6975      return !ele.selected();
  6976    }
  6977  }, {
  6978    selector: ':selectable',
  6979    matches: function matches(ele) {
  6980      return ele.selectable();
  6981    }
  6982  }, {
  6983    selector: ':unselectable',
  6984    matches: function matches(ele) {
  6985      return !ele.selectable();
  6986    }
  6987  }, {
  6988    selector: ':locked',
  6989    matches: function matches(ele) {
  6990      return ele.locked();
  6991    }
  6992  }, {
  6993    selector: ':unlocked',
  6994    matches: function matches(ele) {
  6995      return !ele.locked();
  6996    }
  6997  }, {
  6998    selector: ':visible',
  6999    matches: function matches(ele) {
  7000      return ele.visible();
  7001    }
  7002  }, {
  7003    selector: ':hidden',
  7004    matches: function matches(ele) {
  7005      return !ele.visible();
  7006    }
  7007  }, {
  7008    selector: ':transparent',
  7009    matches: function matches(ele) {
  7010      return ele.transparent();
  7011    }
  7012  }, {
  7013    selector: ':grabbed',
  7014    matches: function matches(ele) {
  7015      return ele.grabbed();
  7016    }
  7017  }, {
  7018    selector: ':free',
  7019    matches: function matches(ele) {
  7020      return !ele.grabbed();
  7021    }
  7022  }, {
  7023    selector: ':removed',
  7024    matches: function matches(ele) {
  7025      return ele.removed();
  7026    }
  7027  }, {
  7028    selector: ':inside',
  7029    matches: function matches(ele) {
  7030      return !ele.removed();
  7031    }
  7032  }, {
  7033    selector: ':grabbable',
  7034    matches: function matches(ele) {
  7035      return ele.grabbable();
  7036    }
  7037  }, {
  7038    selector: ':ungrabbable',
  7039    matches: function matches(ele) {
  7040      return !ele.grabbable();
  7041    }
  7042  }, {
  7043    selector: ':animated',
  7044    matches: function matches(ele) {
  7045      return ele.animated();
  7046    }
  7047  }, {
  7048    selector: ':unanimated',
  7049    matches: function matches(ele) {
  7050      return !ele.animated();
  7051    }
  7052  }, {
  7053    selector: ':parent',
  7054    matches: function matches(ele) {
  7055      return ele.isParent();
  7056    }
  7057  }, {
  7058    selector: ':childless',
  7059    matches: function matches(ele) {
  7060      return ele.isChildless();
  7061    }
  7062  }, {
  7063    selector: ':child',
  7064    matches: function matches(ele) {
  7065      return ele.isChild();
  7066    }
  7067  }, {
  7068    selector: ':orphan',
  7069    matches: function matches(ele) {
  7070      return ele.isOrphan();
  7071    }
  7072  }, {
  7073    selector: ':nonorphan',
  7074    matches: function matches(ele) {
  7075      return ele.isChild();
  7076    }
  7077  }, {
  7078    selector: ':compound',
  7079    matches: function matches(ele) {
  7080      if (ele.isNode()) {
  7081        return ele.isParent();
  7082      } else {
  7083        return ele.source().isParent() || ele.target().isParent();
  7084      }
  7085    }
  7086  }, {
  7087    selector: ':loop',
  7088    matches: function matches(ele) {
  7089      return ele.isLoop();
  7090    }
  7091  }, {
  7092    selector: ':simple',
  7093    matches: function matches(ele) {
  7094      return ele.isSimple();
  7095    }
  7096  }, {
  7097    selector: ':active',
  7098    matches: function matches(ele) {
  7099      return ele.active();
  7100    }
  7101  }, {
  7102    selector: ':inactive',
  7103    matches: function matches(ele) {
  7104      return !ele.active();
  7105    }
  7106  }, {
  7107    selector: ':backgrounding',
  7108    matches: function matches(ele) {
  7109      return ele.backgrounding();
  7110    }
  7111  }, {
  7112    selector: ':nonbackgrounding',
  7113    matches: function matches(ele) {
  7114      return !ele.backgrounding();
  7115    }
  7116  }].sort(function (a, b) {
  7117    // n.b. selectors that are starting substrings of others must have the longer ones first
  7118    return descending(a.selector, b.selector);
  7119  });
  7120  
  7121  var lookup = function () {
  7122    var selToFn = {};
  7123    var s;
  7124  
  7125    for (var i = 0; i < stateSelectors.length; i++) {
  7126      s = stateSelectors[i];
  7127      selToFn[s.selector] = s.matches;
  7128    }
  7129  
  7130    return selToFn;
  7131  }();
  7132  
  7133  var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
  7134    return lookup[sel](ele);
  7135  };
  7136  var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
  7137    return s.selector;
  7138  }).join('|') + ')';
  7139  
  7140  // so that values get compared properly in Selector.filter()
  7141  
  7142  var cleanMetaChars = function cleanMetaChars(str) {
  7143    return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
  7144      return $1;
  7145    });
  7146  };
  7147  
  7148  var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
  7149    selector[selector.length - 1] = replacementQuery;
  7150  }; // NOTE: add new expression syntax here to have it recognised by the parser;
  7151  // - a query contains all adjacent (i.e. no separator in between) expressions;
  7152  // - the current query is stored in selector[i]
  7153  // - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
  7154  
  7155  
  7156  var exprs = [{
  7157    name: 'group',
  7158    // just used for identifying when debugging
  7159    query: true,
  7160    regex: '(' + tokens.group + ')',
  7161    populate: function populate(selector, query, _ref) {
  7162      var _ref2 = _slicedToArray(_ref, 1),
  7163          group = _ref2[0];
  7164  
  7165      query.checks.push({
  7166        type: Type.GROUP,
  7167        value: group === '*' ? group : group + 's'
  7168      });
  7169    }
  7170  }, {
  7171    name: 'state',
  7172    query: true,
  7173    regex: stateSelectorRegex,
  7174    populate: function populate(selector, query, _ref3) {
  7175      var _ref4 = _slicedToArray(_ref3, 1),
  7176          state = _ref4[0];
  7177  
  7178      query.checks.push({
  7179        type: Type.STATE,
  7180        value: state
  7181      });
  7182    }
  7183  }, {
  7184    name: 'id',
  7185    query: true,
  7186    regex: '\\#(' + tokens.id + ')',
  7187    populate: function populate(selector, query, _ref5) {
  7188      var _ref6 = _slicedToArray(_ref5, 1),
  7189          id = _ref6[0];
  7190  
  7191      query.checks.push({
  7192        type: Type.ID,
  7193        value: cleanMetaChars(id)
  7194      });
  7195    }
  7196  }, {
  7197    name: 'className',
  7198    query: true,
  7199    regex: '\\.(' + tokens.className + ')',
  7200    populate: function populate(selector, query, _ref7) {
  7201      var _ref8 = _slicedToArray(_ref7, 1),
  7202          className = _ref8[0];
  7203  
  7204      query.checks.push({
  7205        type: Type.CLASS,
  7206        value: cleanMetaChars(className)
  7207      });
  7208    }
  7209  }, {
  7210    name: 'dataExists',
  7211    query: true,
  7212    regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
  7213    populate: function populate(selector, query, _ref9) {
  7214      var _ref10 = _slicedToArray(_ref9, 1),
  7215          variable = _ref10[0];
  7216  
  7217      query.checks.push({
  7218        type: Type.DATA_EXIST,
  7219        field: cleanMetaChars(variable)
  7220      });
  7221    }
  7222  }, {
  7223    name: 'dataCompare',
  7224    query: true,
  7225    regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
  7226    populate: function populate(selector, query, _ref11) {
  7227      var _ref12 = _slicedToArray(_ref11, 3),
  7228          variable = _ref12[0],
  7229          comparatorOp = _ref12[1],
  7230          value = _ref12[2];
  7231  
  7232      var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
  7233  
  7234      if (valueIsString) {
  7235        value = value.substring(1, value.length - 1);
  7236      } else {
  7237        value = parseFloat(value);
  7238      }
  7239  
  7240      query.checks.push({
  7241        type: Type.DATA_COMPARE,
  7242        field: cleanMetaChars(variable),
  7243        operator: comparatorOp,
  7244        value: value
  7245      });
  7246    }
  7247  }, {
  7248    name: 'dataBool',
  7249    query: true,
  7250    regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
  7251    populate: function populate(selector, query, _ref13) {
  7252      var _ref14 = _slicedToArray(_ref13, 2),
  7253          boolOp = _ref14[0],
  7254          variable = _ref14[1];
  7255  
  7256      query.checks.push({
  7257        type: Type.DATA_BOOL,
  7258        field: cleanMetaChars(variable),
  7259        operator: boolOp
  7260      });
  7261    }
  7262  }, {
  7263    name: 'metaCompare',
  7264    query: true,
  7265    regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
  7266    populate: function populate(selector, query, _ref15) {
  7267      var _ref16 = _slicedToArray(_ref15, 3),
  7268          meta = _ref16[0],
  7269          comparatorOp = _ref16[1],
  7270          number = _ref16[2];
  7271  
  7272      query.checks.push({
  7273        type: Type.META_COMPARE,
  7274        field: cleanMetaChars(meta),
  7275        operator: comparatorOp,
  7276        value: parseFloat(number)
  7277      });
  7278    }
  7279  }, {
  7280    name: 'nextQuery',
  7281    separator: true,
  7282    regex: tokens.separator,
  7283    populate: function populate(selector, query) {
  7284      var currentSubject = selector.currentSubject;
  7285      var edgeCount = selector.edgeCount;
  7286      var compoundCount = selector.compoundCount;
  7287      var lastQ = selector[selector.length - 1];
  7288  
  7289      if (currentSubject != null) {
  7290        lastQ.subject = currentSubject;
  7291        selector.currentSubject = null;
  7292      }
  7293  
  7294      lastQ.edgeCount = edgeCount;
  7295      lastQ.compoundCount = compoundCount;
  7296      selector.edgeCount = 0;
  7297      selector.compoundCount = 0; // go on to next query
  7298  
  7299      var nextQuery = selector[selector.length++] = newQuery();
  7300      return nextQuery; // this is the new query to be filled by the following exprs
  7301    }
  7302  }, {
  7303    name: 'directedEdge',
  7304    separator: true,
  7305    regex: tokens.directedEdge,
  7306    populate: function populate(selector, query) {
  7307      if (selector.currentSubject == null) {
  7308        // undirected edge
  7309        var edgeQuery = newQuery();
  7310        var source = query;
  7311        var target = newQuery();
  7312        edgeQuery.checks.push({
  7313          type: Type.DIRECTED_EDGE,
  7314          source: source,
  7315          target: target
  7316        }); // the query in the selector should be the edge rather than the source
  7317  
  7318        replaceLastQuery(selector, query, edgeQuery);
  7319        selector.edgeCount++; // we're now populating the target query with expressions that follow
  7320  
  7321        return target;
  7322      } else {
  7323        // source/target
  7324        var srcTgtQ = newQuery();
  7325        var _source = query;
  7326  
  7327        var _target = newQuery();
  7328  
  7329        srcTgtQ.checks.push({
  7330          type: Type.NODE_SOURCE,
  7331          source: _source,
  7332          target: _target
  7333        }); // the query in the selector should be the neighbourhood rather than the node
  7334  
  7335        replaceLastQuery(selector, query, srcTgtQ);
  7336        selector.edgeCount++;
  7337        return _target; // now populating the target with the following expressions
  7338      }
  7339    }
  7340  }, {
  7341    name: 'undirectedEdge',
  7342    separator: true,
  7343    regex: tokens.undirectedEdge,
  7344    populate: function populate(selector, query) {
  7345      if (selector.currentSubject == null) {
  7346        // undirected edge
  7347        var edgeQuery = newQuery();
  7348        var source = query;
  7349        var target = newQuery();
  7350        edgeQuery.checks.push({
  7351          type: Type.UNDIRECTED_EDGE,
  7352          nodes: [source, target]
  7353        }); // the query in the selector should be the edge rather than the source
  7354  
  7355        replaceLastQuery(selector, query, edgeQuery);
  7356        selector.edgeCount++; // we're now populating the target query with expressions that follow
  7357  
  7358        return target;
  7359      } else {
  7360        // neighbourhood
  7361        var nhoodQ = newQuery();
  7362        var node = query;
  7363        var neighbor = newQuery();
  7364        nhoodQ.checks.push({
  7365          type: Type.NODE_NEIGHBOR,
  7366          node: node,
  7367          neighbor: neighbor
  7368        }); // the query in the selector should be the neighbourhood rather than the node
  7369  
  7370        replaceLastQuery(selector, query, nhoodQ);
  7371        return neighbor; // now populating the neighbor with following expressions
  7372      }
  7373    }
  7374  }, {
  7375    name: 'child',
  7376    separator: true,
  7377    regex: tokens.child,
  7378    populate: function populate(selector, query) {
  7379      if (selector.currentSubject == null) {
  7380        // default: child query
  7381        var parentChildQuery = newQuery();
  7382        var child = newQuery();
  7383        var parent = selector[selector.length - 1];
  7384        parentChildQuery.checks.push({
  7385          type: Type.CHILD,
  7386          parent: parent,
  7387          child: child
  7388        }); // the query in the selector should be the '>' itself
  7389  
  7390        replaceLastQuery(selector, query, parentChildQuery);
  7391        selector.compoundCount++; // we're now populating the child query with expressions that follow
  7392  
  7393        return child;
  7394      } else if (selector.currentSubject === query) {
  7395        // compound split query
  7396        var compound = newQuery();
  7397        var left = selector[selector.length - 1];
  7398        var right = newQuery();
  7399        var subject = newQuery();
  7400  
  7401        var _child = newQuery();
  7402  
  7403        var _parent = newQuery(); // set up the root compound q
  7404  
  7405  
  7406        compound.checks.push({
  7407          type: Type.COMPOUND_SPLIT,
  7408          left: left,
  7409          right: right,
  7410          subject: subject
  7411        }); // populate the subject and replace the q at the old spot (within left) with TRUE
  7412  
  7413        subject.checks = query.checks; // take the checks from the left
  7414  
  7415        query.checks = [{
  7416          type: Type.TRUE
  7417        }]; // checks under left refs the subject implicitly
  7418        // set up the right q
  7419  
  7420        _parent.checks.push({
  7421          type: Type.TRUE
  7422        }); // parent implicitly refs the subject
  7423  
  7424  
  7425        right.checks.push({
  7426          type: Type.PARENT,
  7427          // type is swapped on right side queries
  7428          parent: _parent,
  7429          child: _child // empty for now
  7430  
  7431        });
  7432        replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
  7433  
  7434        selector.currentSubject = subject;
  7435        selector.compoundCount++;
  7436        return _child; // now populating the right side's child
  7437      } else {
  7438        // parent query
  7439        // info for parent query
  7440        var _parent2 = newQuery();
  7441  
  7442        var _child2 = newQuery();
  7443  
  7444        var pcQChecks = [{
  7445          type: Type.PARENT,
  7446          parent: _parent2,
  7447          child: _child2
  7448        }]; // the parent-child query takes the place of the query previously being populated
  7449  
  7450        _parent2.checks = query.checks; // the previous query contains the checks for the parent
  7451  
  7452        query.checks = pcQChecks; // pc query takes over
  7453  
  7454        selector.compoundCount++;
  7455        return _child2; // we're now populating the child
  7456      }
  7457    }
  7458  }, {
  7459    name: 'descendant',
  7460    separator: true,
  7461    regex: tokens.descendant,
  7462    populate: function populate(selector, query) {
  7463      if (selector.currentSubject == null) {
  7464        // default: descendant query
  7465        var ancChQuery = newQuery();
  7466        var descendant = newQuery();
  7467        var ancestor = selector[selector.length - 1];
  7468        ancChQuery.checks.push({
  7469          type: Type.DESCENDANT,
  7470          ancestor: ancestor,
  7471          descendant: descendant
  7472        }); // the query in the selector should be the '>' itself
  7473  
  7474        replaceLastQuery(selector, query, ancChQuery);
  7475        selector.compoundCount++; // we're now populating the descendant query with expressions that follow
  7476  
  7477        return descendant;
  7478      } else if (selector.currentSubject === query) {
  7479        // compound split query
  7480        var compound = newQuery();
  7481        var left = selector[selector.length - 1];
  7482        var right = newQuery();
  7483        var subject = newQuery();
  7484  
  7485        var _descendant = newQuery();
  7486  
  7487        var _ancestor = newQuery(); // set up the root compound q
  7488  
  7489  
  7490        compound.checks.push({
  7491          type: Type.COMPOUND_SPLIT,
  7492          left: left,
  7493          right: right,
  7494          subject: subject
  7495        }); // populate the subject and replace the q at the old spot (within left) with TRUE
  7496  
  7497        subject.checks = query.checks; // take the checks from the left
  7498  
  7499        query.checks = [{
  7500          type: Type.TRUE
  7501        }]; // checks under left refs the subject implicitly
  7502        // set up the right q
  7503  
  7504        _ancestor.checks.push({
  7505          type: Type.TRUE
  7506        }); // ancestor implicitly refs the subject
  7507  
  7508  
  7509        right.checks.push({
  7510          type: Type.ANCESTOR,
  7511          // type is swapped on right side queries
  7512          ancestor: _ancestor,
  7513          descendant: _descendant // empty for now
  7514  
  7515        });
  7516        replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
  7517  
  7518        selector.currentSubject = subject;
  7519        selector.compoundCount++;
  7520        return _descendant; // now populating the right side's descendant
  7521      } else {
  7522        // ancestor query
  7523        // info for parent query
  7524        var _ancestor2 = newQuery();
  7525  
  7526        var _descendant2 = newQuery();
  7527  
  7528        var adQChecks = [{
  7529          type: Type.ANCESTOR,
  7530          ancestor: _ancestor2,
  7531          descendant: _descendant2
  7532        }]; // the parent-child query takes the place of the query previously being populated
  7533  
  7534        _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
  7535  
  7536        query.checks = adQChecks; // pc query takes over
  7537  
  7538        selector.compoundCount++;
  7539        return _descendant2; // we're now populating the child
  7540      }
  7541    }
  7542  }, {
  7543    name: 'subject',
  7544    modifier: true,
  7545    regex: tokens.subject,
  7546    populate: function populate(selector, query) {
  7547      if (selector.currentSubject != null && selector.currentSubject !== query) {
  7548        warn('Redefinition of subject in selector `' + selector.toString() + '`');
  7549        return false;
  7550      }
  7551  
  7552      selector.currentSubject = query;
  7553      var topQ = selector[selector.length - 1];
  7554      var topChk = topQ.checks[0];
  7555      var topType = topChk == null ? null : topChk.type;
  7556  
  7557      if (topType === Type.DIRECTED_EDGE) {
  7558        // directed edge with subject on the target
  7559        // change to target node check
  7560        topChk.type = Type.NODE_TARGET;
  7561      } else if (topType === Type.UNDIRECTED_EDGE) {
  7562        // undirected edge with subject on the second node
  7563        // change to neighbor check
  7564        topChk.type = Type.NODE_NEIGHBOR;
  7565        topChk.node = topChk.nodes[1]; // second node is subject
  7566  
  7567        topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
  7568  
  7569        topChk.nodes = null;
  7570      }
  7571    }
  7572  }];
  7573  exprs.forEach(function (e) {
  7574    return e.regexObj = new RegExp('^' + e.regex);
  7575  });
  7576  
  7577  /**
  7578   * Of all the expressions, find the first match in the remaining text.
  7579   * @param {string} remaining The remaining text to parse
  7580   * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
  7581   */
  7582  
  7583  var consumeExpr = function consumeExpr(remaining) {
  7584    var expr;
  7585    var match;
  7586    var name;
  7587  
  7588    for (var j = 0; j < exprs.length; j++) {
  7589      var e = exprs[j];
  7590      var n = e.name;
  7591      var m = remaining.match(e.regexObj);
  7592  
  7593      if (m != null) {
  7594        match = m;
  7595        expr = e;
  7596        name = n;
  7597        var consumed = m[0];
  7598        remaining = remaining.substring(consumed.length);
  7599        break; // we've consumed one expr, so we can return now
  7600      }
  7601    }
  7602  
  7603    return {
  7604      expr: expr,
  7605      match: match,
  7606      name: name,
  7607      remaining: remaining
  7608    };
  7609  };
  7610  /**
  7611   * Consume all the leading whitespace
  7612   * @param {string} remaining The text to consume
  7613   * @returns The text with the leading whitespace removed
  7614   */
  7615  
  7616  
  7617  var consumeWhitespace = function consumeWhitespace(remaining) {
  7618    var match = remaining.match(/^\s+/);
  7619  
  7620    if (match) {
  7621      var consumed = match[0];
  7622      remaining = remaining.substring(consumed.length);
  7623    }
  7624  
  7625    return remaining;
  7626  };
  7627  /**
  7628   * Parse the string and store the parsed representation in the Selector.
  7629   * @param {string} selector The selector string
  7630   * @returns `true` if the selector was successfully parsed, `false` otherwise
  7631   */
  7632  
  7633  
  7634  var parse = function parse(selector) {
  7635    var self = this;
  7636    var remaining = self.inputText = selector;
  7637    var currentQuery = self[0] = newQuery();
  7638    self.length = 1;
  7639    remaining = consumeWhitespace(remaining); // get rid of leading whitespace
  7640  
  7641    for (;;) {
  7642      var exprInfo = consumeExpr(remaining);
  7643  
  7644      if (exprInfo.expr == null) {
  7645        warn('The selector `' + selector + '`is invalid');
  7646        return false;
  7647      } else {
  7648        var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
  7649  
  7650        var ret = exprInfo.expr.populate(self, currentQuery, args);
  7651  
  7652        if (ret === false) {
  7653          return false; // exit if population failed
  7654        } else if (ret != null) {
  7655          currentQuery = ret; // change the current query to be filled if the expr specifies
  7656        }
  7657      }
  7658  
  7659      remaining = exprInfo.remaining; // we're done when there's nothing left to parse
  7660  
  7661      if (remaining.match(/^\s*$/)) {
  7662        break;
  7663      }
  7664    }
  7665  
  7666    var lastQ = self[self.length - 1];
  7667  
  7668    if (self.currentSubject != null) {
  7669      lastQ.subject = self.currentSubject;
  7670    }
  7671  
  7672    lastQ.edgeCount = self.edgeCount;
  7673    lastQ.compoundCount = self.compoundCount;
  7674  
  7675    for (var i = 0; i < self.length; i++) {
  7676      var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
  7677  
  7678      if (q.compoundCount > 0 && q.edgeCount > 0) {
  7679        warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
  7680        return false;
  7681      }
  7682  
  7683      if (q.edgeCount > 1) {
  7684        warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
  7685        return false;
  7686      } else if (q.edgeCount === 1) {
  7687        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.');
  7688      }
  7689    }
  7690  
  7691    return true; // success
  7692  };
  7693  /**
  7694   * Get the selector represented as a string.  This value uses default formatting,
  7695   * so things like spacing may differ from the input text passed to the constructor.
  7696   * @returns {string} The selector string
  7697   */
  7698  
  7699  
  7700  var toString = function toString() {
  7701    if (this.toStringCache != null) {
  7702      return this.toStringCache;
  7703    }
  7704  
  7705    var clean = function clean(obj) {
  7706      if (obj == null) {
  7707        return '';
  7708      } else {
  7709        return obj;
  7710      }
  7711    };
  7712  
  7713    var cleanVal = function cleanVal(val) {
  7714      if (string(val)) {
  7715        return '"' + val + '"';
  7716      } else {
  7717        return clean(val);
  7718      }
  7719    };
  7720  
  7721    var space = function space(val) {
  7722      return ' ' + val + ' ';
  7723    };
  7724  
  7725    var checkToString = function checkToString(check, subject) {
  7726      var type = check.type,
  7727          value = check.value;
  7728  
  7729      switch (type) {
  7730        case Type.GROUP:
  7731          {
  7732            var group = clean(value);
  7733            return group.substring(0, group.length - 1);
  7734          }
  7735  
  7736        case Type.DATA_COMPARE:
  7737          {
  7738            var field = check.field,
  7739                operator = check.operator;
  7740            return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
  7741          }
  7742  
  7743        case Type.DATA_BOOL:
  7744          {
  7745            var _operator = check.operator,
  7746                _field = check.field;
  7747            return '[' + clean(_operator) + _field + ']';
  7748          }
  7749  
  7750        case Type.DATA_EXIST:
  7751          {
  7752            var _field2 = check.field;
  7753            return '[' + _field2 + ']';
  7754          }
  7755  
  7756        case Type.META_COMPARE:
  7757          {
  7758            var _operator2 = check.operator,
  7759                _field3 = check.field;
  7760            return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
  7761          }
  7762  
  7763        case Type.STATE:
  7764          {
  7765            return value;
  7766          }
  7767  
  7768        case Type.ID:
  7769          {
  7770            return '#' + value;
  7771          }
  7772  
  7773        case Type.CLASS:
  7774          {
  7775            return '.' + value;
  7776          }
  7777  
  7778        case Type.PARENT:
  7779        case Type.CHILD:
  7780          {
  7781            return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
  7782          }
  7783  
  7784        case Type.ANCESTOR:
  7785        case Type.DESCENDANT:
  7786          {
  7787            return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
  7788          }
  7789  
  7790        case Type.COMPOUND_SPLIT:
  7791          {
  7792            var lhs = queryToString(check.left, subject);
  7793            var sub = queryToString(check.subject, subject);
  7794            var rhs = queryToString(check.right, subject);
  7795            return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
  7796          }
  7797  
  7798        case Type.TRUE:
  7799          {
  7800            return '';
  7801          }
  7802      }
  7803    };
  7804  
  7805    var queryToString = function queryToString(query, subject) {
  7806      return query.checks.reduce(function (str, chk, i) {
  7807        return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
  7808      }, '');
  7809    };
  7810  
  7811    var str = '';
  7812  
  7813    for (var i = 0; i < this.length; i++) {
  7814      var query = this[i];
  7815      str += queryToString(query, query.subject);
  7816  
  7817      if (this.length > 1 && i < this.length - 1) {
  7818        str += ', ';
  7819      }
  7820    }
  7821  
  7822    this.toStringCache = str;
  7823    return str;
  7824  };
  7825  var parse$1 = {
  7826    parse: parse,
  7827    toString: toString
  7828  };
  7829  
  7830  var valCmp = function valCmp(fieldVal, operator, value) {
  7831    var matches;
  7832    var isFieldStr = string(fieldVal);
  7833    var isFieldNum = number(fieldVal);
  7834    var isValStr = string(value);
  7835    var fieldStr, valStr;
  7836    var caseInsensitive = false;
  7837    var notExpr = false;
  7838    var isIneqCmp = false;
  7839  
  7840    if (operator.indexOf('!') >= 0) {
  7841      operator = operator.replace('!', '');
  7842      notExpr = true;
  7843    }
  7844  
  7845    if (operator.indexOf('@') >= 0) {
  7846      operator = operator.replace('@', '');
  7847      caseInsensitive = true;
  7848    }
  7849  
  7850    if (isFieldStr || isValStr || caseInsensitive) {
  7851      fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
  7852      valStr = '' + value;
  7853    } // if we're doing a case insensitive comparison, then we're using a STRING comparison
  7854    // even if we're comparing numbers
  7855  
  7856  
  7857    if (caseInsensitive) {
  7858      fieldVal = fieldStr = fieldStr.toLowerCase();
  7859      value = valStr = valStr.toLowerCase();
  7860    }
  7861  
  7862    switch (operator) {
  7863      case '*=':
  7864        matches = fieldStr.indexOf(valStr) >= 0;
  7865        break;
  7866  
  7867      case '$=':
  7868        matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
  7869        break;
  7870  
  7871      case '^=':
  7872        matches = fieldStr.indexOf(valStr) === 0;
  7873        break;
  7874  
  7875      case '=':
  7876        matches = fieldVal === value;
  7877        break;
  7878  
  7879      case '>':
  7880        isIneqCmp = true;
  7881        matches = fieldVal > value;
  7882        break;
  7883  
  7884      case '>=':
  7885        isIneqCmp = true;
  7886        matches = fieldVal >= value;
  7887        break;
  7888  
  7889      case '<':
  7890        isIneqCmp = true;
  7891        matches = fieldVal < value;
  7892        break;
  7893  
  7894      case '<=':
  7895        isIneqCmp = true;
  7896        matches = fieldVal <= value;
  7897        break;
  7898  
  7899      default:
  7900        matches = false;
  7901        break;
  7902    } // apply the not op, but null vals for inequalities should always stay non-matching
  7903  
  7904  
  7905    if (notExpr && (fieldVal != null || !isIneqCmp)) {
  7906      matches = !matches;
  7907    }
  7908  
  7909    return matches;
  7910  };
  7911  var boolCmp = function boolCmp(fieldVal, operator) {
  7912    switch (operator) {
  7913      case '?':
  7914        return fieldVal ? true : false;
  7915  
  7916      case '!':
  7917        return fieldVal ? false : true;
  7918  
  7919      case '^':
  7920        return fieldVal === undefined;
  7921    }
  7922  };
  7923  var existCmp = function existCmp(fieldVal) {
  7924    return fieldVal !== undefined;
  7925  };
  7926  var data = function data(ele, field) {
  7927    return ele.data(field);
  7928  };
  7929  var meta = function meta(ele, field) {
  7930    return ele[field]();
  7931  };
  7932  
  7933  /** A lookup of `match(check, ele)` functions by `Type` int */
  7934  
  7935  var match = [];
  7936  /**
  7937   * Returns whether the query matches for the element
  7938   * @param query The `{ type, value, ... }` query object
  7939   * @param ele The element to compare against
  7940  */
  7941  
  7942  var matches = function matches(query, ele) {
  7943    return query.checks.every(function (chk) {
  7944      return match[chk.type](chk, ele);
  7945    });
  7946  };
  7947  
  7948  match[Type.GROUP] = function (check, ele) {
  7949    var group = check.value;
  7950    return group === '*' || group === ele.group();
  7951  };
  7952  
  7953  match[Type.STATE] = function (check, ele) {
  7954    var stateSelector = check.value;
  7955    return stateSelectorMatches(stateSelector, ele);
  7956  };
  7957  
  7958  match[Type.ID] = function (check, ele) {
  7959    var id = check.value;
  7960    return ele.id() === id;
  7961  };
  7962  
  7963  match[Type.CLASS] = function (check, ele) {
  7964    var cls = check.value;
  7965    return ele.hasClass(cls);
  7966  };
  7967  
  7968  match[Type.META_COMPARE] = function (check, ele) {
  7969    var field = check.field,
  7970        operator = check.operator,
  7971        value = check.value;
  7972    return valCmp(meta(ele, field), operator, value);
  7973  };
  7974  
  7975  match[Type.DATA_COMPARE] = function (check, ele) {
  7976    var field = check.field,
  7977        operator = check.operator,
  7978        value = check.value;
  7979    return valCmp(data(ele, field), operator, value);
  7980  };
  7981  
  7982  match[Type.DATA_BOOL] = function (check, ele) {
  7983    var field = check.field,
  7984        operator = check.operator;
  7985    return boolCmp(data(ele, field), operator);
  7986  };
  7987  
  7988  match[Type.DATA_EXIST] = function (check, ele) {
  7989    var field = check.field,
  7990        operator = check.operator;
  7991    return existCmp(data(ele, field));
  7992  };
  7993  
  7994  match[Type.UNDIRECTED_EDGE] = function (check, ele) {
  7995    var qA = check.nodes[0];
  7996    var qB = check.nodes[1];
  7997    var src = ele.source();
  7998    var tgt = ele.target();
  7999    return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
  8000  };
  8001  
  8002  match[Type.NODE_NEIGHBOR] = function (check, ele) {
  8003    return matches(check.node, ele) && ele.neighborhood().some(function (n) {
  8004      return n.isNode() && matches(check.neighbor, n);
  8005    });
  8006  };
  8007  
  8008  match[Type.DIRECTED_EDGE] = function (check, ele) {
  8009    return matches(check.source, ele.source()) && matches(check.target, ele.target());
  8010  };
  8011  
  8012  match[Type.NODE_SOURCE] = function (check, ele) {
  8013    return matches(check.source, ele) && ele.outgoers().some(function (n) {
  8014      return n.isNode() && matches(check.target, n);
  8015    });
  8016  };
  8017  
  8018  match[Type.NODE_TARGET] = function (check, ele) {
  8019    return matches(check.target, ele) && ele.incomers().some(function (n) {
  8020      return n.isNode() && matches(check.source, n);
  8021    });
  8022  };
  8023  
  8024  match[Type.CHILD] = function (check, ele) {
  8025    return matches(check.child, ele) && matches(check.parent, ele.parent());
  8026  };
  8027  
  8028  match[Type.PARENT] = function (check, ele) {
  8029    return matches(check.parent, ele) && ele.children().some(function (c) {
  8030      return matches(check.child, c);
  8031    });
  8032  };
  8033  
  8034  match[Type.DESCENDANT] = function (check, ele) {
  8035    return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
  8036      return matches(check.ancestor, a);
  8037    });
  8038  };
  8039  
  8040  match[Type.ANCESTOR] = function (check, ele) {
  8041    return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
  8042      return matches(check.descendant, d);
  8043    });
  8044  };
  8045  
  8046  match[Type.COMPOUND_SPLIT] = function (check, ele) {
  8047    return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
  8048  };
  8049  
  8050  match[Type.TRUE] = function () {
  8051    return true;
  8052  };
  8053  
  8054  match[Type.COLLECTION] = function (check, ele) {
  8055    var collection = check.value;
  8056    return collection.has(ele);
  8057  };
  8058  
  8059  match[Type.FILTER] = function (check, ele) {
  8060    var filter = check.value;
  8061    return filter(ele);
  8062  };
  8063  
  8064  var filter = function filter(collection) {
  8065    var self = this; // for 1 id #foo queries, just get the element
  8066  
  8067    if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
  8068      return collection.getElementById(self[0].checks[0].value).collection();
  8069    }
  8070  
  8071    var selectorFunction = function selectorFunction(element) {
  8072      for (var j = 0; j < self.length; j++) {
  8073        var query = self[j];
  8074  
  8075        if (matches(query, element)) {
  8076          return true;
  8077        }
  8078      }
  8079  
  8080      return false;
  8081    };
  8082  
  8083    if (self.text() == null) {
  8084      selectorFunction = function selectorFunction() {
  8085        return true;
  8086      };
  8087    }
  8088  
  8089    return collection.filter(selectorFunction);
  8090  }; // filter
  8091  // does selector match a single element?
  8092  
  8093  
  8094  var matches$1 = function matches$1(ele) {
  8095    var self = this;
  8096  
  8097    for (var j = 0; j < self.length; j++) {
  8098      var query = self[j];
  8099  
  8100      if (matches(query, ele)) {
  8101        return true;
  8102      }
  8103    }
  8104  
  8105    return false;
  8106  }; // matches
  8107  
  8108  
  8109  var matching = {
  8110    matches: matches$1,
  8111    filter: filter
  8112  };
  8113  
  8114  var Selector = function Selector(selector) {
  8115    this.inputText = selector;
  8116    this.currentSubject = null;
  8117    this.compoundCount = 0;
  8118    this.edgeCount = 0;
  8119    this.length = 0;
  8120  
  8121    if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
  8122      this.addQuery({
  8123        checks: [{
  8124          type: Type.COLLECTION,
  8125          value: selector.collection()
  8126        }]
  8127      });
  8128    } else if (fn(selector)) {
  8129      this.addQuery({
  8130        checks: [{
  8131          type: Type.FILTER,
  8132          value: selector
  8133        }]
  8134      });
  8135    } else if (string(selector)) {
  8136      if (!this.parse(selector)) {
  8137        this.invalid = true;
  8138      }
  8139    } else {
  8140      error('A selector must be created from a string; found ');
  8141    }
  8142  };
  8143  
  8144  var selfn = Selector.prototype;
  8145  [parse$1, matching].forEach(function (p) {
  8146    return extend(selfn, p);
  8147  });
  8148  
  8149  selfn.text = function () {
  8150    return this.inputText;
  8151  };
  8152  
  8153  selfn.size = function () {
  8154    return this.length;
  8155  };
  8156  
  8157  selfn.eq = function (i) {
  8158    return this[i];
  8159  };
  8160  
  8161  selfn.sameText = function (otherSel) {
  8162    return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
  8163  };
  8164  
  8165  selfn.addQuery = function (q) {
  8166    this[this.length++] = q;
  8167  };
  8168  
  8169  selfn.selector = selfn.toString;
  8170  
  8171  var elesfn$f = {
  8172    allAre: function allAre(selector) {
  8173      var selObj = new Selector(selector);
  8174      return this.every(function (ele) {
  8175        return selObj.matches(ele);
  8176      });
  8177    },
  8178    is: function is(selector) {
  8179      var selObj = new Selector(selector);
  8180      return this.some(function (ele) {
  8181        return selObj.matches(ele);
  8182      });
  8183    },
  8184    some: function some(fn, thisArg) {
  8185      for (var i = 0; i < this.length; i++) {
  8186        var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
  8187  
  8188        if (ret) {
  8189          return true;
  8190        }
  8191      }
  8192  
  8193      return false;
  8194    },
  8195    every: function every(fn, thisArg) {
  8196      for (var i = 0; i < this.length; i++) {
  8197        var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
  8198  
  8199        if (!ret) {
  8200          return false;
  8201        }
  8202      }
  8203  
  8204      return true;
  8205    },
  8206    same: function same(collection) {
  8207      // cheap collection ref check
  8208      if (this === collection) {
  8209        return true;
  8210      }
  8211  
  8212      collection = this.cy().collection(collection);
  8213      var thisLength = this.length;
  8214      var collectionLength = collection.length; // cheap length check
  8215  
  8216      if (thisLength !== collectionLength) {
  8217        return false;
  8218      } // cheap element ref check
  8219  
  8220  
  8221      if (thisLength === 1) {
  8222        return this[0] === collection[0];
  8223      }
  8224  
  8225      return this.every(function (ele) {
  8226        return collection.hasElementWithId(ele.id());
  8227      });
  8228    },
  8229    anySame: function anySame(collection) {
  8230      collection = this.cy().collection(collection);
  8231      return this.some(function (ele) {
  8232        return collection.hasElementWithId(ele.id());
  8233      });
  8234    },
  8235    allAreNeighbors: function allAreNeighbors(collection) {
  8236      collection = this.cy().collection(collection);
  8237      var nhood = this.neighborhood();
  8238      return collection.every(function (ele) {
  8239        return nhood.hasElementWithId(ele.id());
  8240      });
  8241    },
  8242    contains: function contains(collection) {
  8243      collection = this.cy().collection(collection);
  8244      var self = this;
  8245      return collection.every(function (ele) {
  8246        return self.hasElementWithId(ele.id());
  8247      });
  8248    }
  8249  };
  8250  elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
  8251  elesfn$f.has = elesfn$f.contains;
  8252  elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
  8253  
  8254  var cache = function cache(fn, name) {
  8255    return function traversalCache(arg1, arg2, arg3, arg4) {
  8256      var selectorOrEles = arg1;
  8257      var eles = this;
  8258      var key;
  8259  
  8260      if (selectorOrEles == null) {
  8261        key = '';
  8262      } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
  8263        key = selectorOrEles.id();
  8264      }
  8265  
  8266      if (eles.length === 1 && key) {
  8267        var _p = eles[0]._private;
  8268        var tch = _p.traversalCache = _p.traversalCache || {};
  8269        var ch = tch[name] = tch[name] || [];
  8270        var hash = hashString(key);
  8271        var cacheHit = ch[hash];
  8272  
  8273        if (cacheHit) {
  8274          return cacheHit;
  8275        } else {
  8276          return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
  8277        }
  8278      } else {
  8279        return fn.call(eles, arg1, arg2, arg3, arg4);
  8280      }
  8281    };
  8282  };
  8283  
  8284  var elesfn$g = {
  8285    parent: function parent(selector) {
  8286      var parents = []; // optimisation for single ele call
  8287  
  8288      if (this.length === 1) {
  8289        var parent = this[0]._private.parent;
  8290  
  8291        if (parent) {
  8292          return parent;
  8293        }
  8294      }
  8295  
  8296      for (var i = 0; i < this.length; i++) {
  8297        var ele = this[i];
  8298        var _parent = ele._private.parent;
  8299  
  8300        if (_parent) {
  8301          parents.push(_parent);
  8302        }
  8303      }
  8304  
  8305      return this.spawn(parents, {
  8306        unique: true
  8307      }).filter(selector);
  8308    },
  8309    parents: function parents(selector) {
  8310      var parents = [];
  8311      var eles = this.parent();
  8312  
  8313      while (eles.nonempty()) {
  8314        for (var i = 0; i < eles.length; i++) {
  8315          var ele = eles[i];
  8316          parents.push(ele);
  8317        }
  8318  
  8319        eles = eles.parent();
  8320      }
  8321  
  8322      return this.spawn(parents, {
  8323        unique: true
  8324      }).filter(selector);
  8325    },
  8326    commonAncestors: function commonAncestors(selector) {
  8327      var ancestors;
  8328  
  8329      for (var i = 0; i < this.length; i++) {
  8330        var ele = this[i];
  8331        var parents = ele.parents();
  8332        ancestors = ancestors || parents;
  8333        ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
  8334      }
  8335  
  8336      return ancestors.filter(selector);
  8337    },
  8338    orphans: function orphans(selector) {
  8339      return this.stdFilter(function (ele) {
  8340        return ele.isOrphan();
  8341      }).filter(selector);
  8342    },
  8343    nonorphans: function nonorphans(selector) {
  8344      return this.stdFilter(function (ele) {
  8345        return ele.isChild();
  8346      }).filter(selector);
  8347    },
  8348    children: cache(function (selector) {
  8349      var children = [];
  8350  
  8351      for (var i = 0; i < this.length; i++) {
  8352        var ele = this[i];
  8353        var eleChildren = ele._private.children;
  8354  
  8355        for (var j = 0; j < eleChildren.length; j++) {
  8356          children.push(eleChildren[j]);
  8357        }
  8358      }
  8359  
  8360      return this.spawn(children, {
  8361        unique: true
  8362      }).filter(selector);
  8363    }, 'children'),
  8364    siblings: function siblings(selector) {
  8365      return this.parent().children().not(this).filter(selector);
  8366    },
  8367    isParent: function isParent() {
  8368      var ele = this[0];
  8369  
  8370      if (ele) {
  8371        return ele.isNode() && ele._private.children.length !== 0;
  8372      }
  8373    },
  8374    isChildless: function isChildless() {
  8375      var ele = this[0];
  8376  
  8377      if (ele) {
  8378        return ele.isNode() && ele._private.children.length === 0;
  8379      }
  8380    },
  8381    isChild: function isChild() {
  8382      var ele = this[0];
  8383  
  8384      if (ele) {
  8385        return ele.isNode() && ele._private.parent != null;
  8386      }
  8387    },
  8388    isOrphan: function isOrphan() {
  8389      var ele = this[0];
  8390  
  8391      if (ele) {
  8392        return ele.isNode() && ele._private.parent == null;
  8393      }
  8394    },
  8395    descendants: function descendants(selector) {
  8396      var elements = [];
  8397  
  8398      function add(eles) {
  8399        for (var i = 0; i < eles.length; i++) {
  8400          var ele = eles[i];
  8401          elements.push(ele);
  8402  
  8403          if (ele.children().nonempty()) {
  8404            add(ele.children());
  8405          }
  8406        }
  8407      }
  8408  
  8409      add(this.children());
  8410      return this.spawn(elements, {
  8411        unique: true
  8412      }).filter(selector);
  8413    }
  8414  };
  8415  
  8416  function forEachCompound(eles, fn, includeSelf, recursiveStep) {
  8417    var q = [];
  8418    var did = new Set$1();
  8419    var cy = eles.cy();
  8420    var hasCompounds = cy.hasCompoundNodes();
  8421  
  8422    for (var i = 0; i < eles.length; i++) {
  8423      var ele = eles[i];
  8424  
  8425      if (includeSelf) {
  8426        q.push(ele);
  8427      } else if (hasCompounds) {
  8428        recursiveStep(q, did, ele);
  8429      }
  8430    }
  8431  
  8432    while (q.length > 0) {
  8433      var _ele = q.shift();
  8434  
  8435      fn(_ele);
  8436      did.add(_ele.id());
  8437  
  8438      if (hasCompounds) {
  8439        recursiveStep(q, did, _ele);
  8440      }
  8441    }
  8442  
  8443    return eles;
  8444  }
  8445  
  8446  function addChildren(q, did, ele) {
  8447    if (ele.isParent()) {
  8448      var children = ele._private.children;
  8449  
  8450      for (var i = 0; i < children.length; i++) {
  8451        var child = children[i];
  8452  
  8453        if (!did.has(child.id())) {
  8454          q.push(child);
  8455        }
  8456      }
  8457    }
  8458  } // very efficient version of eles.add( eles.descendants() ).forEach()
  8459  // for internal use
  8460  
  8461  
  8462  elesfn$g.forEachDown = function (fn) {
  8463    var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  8464    return forEachCompound(this, fn, includeSelf, addChildren);
  8465  };
  8466  
  8467  function addParent(q, did, ele) {
  8468    if (ele.isChild()) {
  8469      var parent = ele._private.parent;
  8470  
  8471      if (!did.has(parent.id())) {
  8472        q.push(parent);
  8473      }
  8474    }
  8475  }
  8476  
  8477  elesfn$g.forEachUp = function (fn) {
  8478    var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  8479    return forEachCompound(this, fn, includeSelf, addParent);
  8480  };
  8481  
  8482  function addParentAndChildren(q, did, ele) {
  8483    addParent(q, did, ele);
  8484    addChildren(q, did, ele);
  8485  }
  8486  
  8487  elesfn$g.forEachUpAndDown = function (fn) {
  8488    var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  8489    return forEachCompound(this, fn, includeSelf, addParentAndChildren);
  8490  }; // aliases
  8491  
  8492  
  8493  elesfn$g.ancestors = elesfn$g.parents;
  8494  
  8495  var fn$1, elesfn$h;
  8496  fn$1 = elesfn$h = {
  8497    data: define$3.data({
  8498      field: 'data',
  8499      bindingEvent: 'data',
  8500      allowBinding: true,
  8501      allowSetting: true,
  8502      settingEvent: 'data',
  8503      settingTriggersEvent: true,
  8504      triggerFnName: 'trigger',
  8505      allowGetting: true,
  8506      immutableKeys: {
  8507        'id': true,
  8508        'source': true,
  8509        'target': true,
  8510        'parent': true
  8511      },
  8512      updateStyle: true
  8513    }),
  8514    removeData: define$3.removeData({
  8515      field: 'data',
  8516      event: 'data',
  8517      triggerFnName: 'trigger',
  8518      triggerEvent: true,
  8519      immutableKeys: {
  8520        'id': true,
  8521        'source': true,
  8522        'target': true,
  8523        'parent': true
  8524      },
  8525      updateStyle: true
  8526    }),
  8527    scratch: define$3.data({
  8528      field: 'scratch',
  8529      bindingEvent: 'scratch',
  8530      allowBinding: true,
  8531      allowSetting: true,
  8532      settingEvent: 'scratch',
  8533      settingTriggersEvent: true,
  8534      triggerFnName: 'trigger',
  8535      allowGetting: true,
  8536      updateStyle: true
  8537    }),
  8538    removeScratch: define$3.removeData({
  8539      field: 'scratch',
  8540      event: 'scratch',
  8541      triggerFnName: 'trigger',
  8542      triggerEvent: true,
  8543      updateStyle: true
  8544    }),
  8545    rscratch: define$3.data({
  8546      field: 'rscratch',
  8547      allowBinding: false,
  8548      allowSetting: true,
  8549      settingTriggersEvent: false,
  8550      allowGetting: true
  8551    }),
  8552    removeRscratch: define$3.removeData({
  8553      field: 'rscratch',
  8554      triggerEvent: false
  8555    }),
  8556    id: function id() {
  8557      var ele = this[0];
  8558  
  8559      if (ele) {
  8560        return ele._private.data.id;
  8561      }
  8562    }
  8563  }; // aliases
  8564  
  8565  fn$1.attr = fn$1.data;
  8566  fn$1.removeAttr = fn$1.removeData;
  8567  var data$1 = elesfn$h;
  8568  
  8569  var elesfn$i = {};
  8570  
  8571  function defineDegreeFunction(callback) {
  8572    return function (includeLoops) {
  8573      var self = this;
  8574  
  8575      if (includeLoops === undefined) {
  8576        includeLoops = true;
  8577      }
  8578  
  8579      if (self.length === 0) {
  8580        return;
  8581      }
  8582  
  8583      if (self.isNode() && !self.removed()) {
  8584        var degree = 0;
  8585        var node = self[0];
  8586        var connectedEdges = node._private.edges;
  8587  
  8588        for (var i = 0; i < connectedEdges.length; i++) {
  8589          var edge = connectedEdges[i];
  8590  
  8591          if (!includeLoops && edge.isLoop()) {
  8592            continue;
  8593          }
  8594  
  8595          degree += callback(node, edge);
  8596        }
  8597  
  8598        return degree;
  8599      } else {
  8600        return;
  8601      }
  8602    };
  8603  }
  8604  
  8605  extend(elesfn$i, {
  8606    degree: defineDegreeFunction(function (node, edge) {
  8607      if (edge.source().same(edge.target())) {
  8608        return 2;
  8609      } else {
  8610        return 1;
  8611      }
  8612    }),
  8613    indegree: defineDegreeFunction(function (node, edge) {
  8614      if (edge.target().same(node)) {
  8615        return 1;
  8616      } else {
  8617        return 0;
  8618      }
  8619    }),
  8620    outdegree: defineDegreeFunction(function (node, edge) {
  8621      if (edge.source().same(node)) {
  8622        return 1;
  8623      } else {
  8624        return 0;
  8625      }
  8626    })
  8627  });
  8628  
  8629  function defineDegreeBoundsFunction(degreeFn, callback) {
  8630    return function (includeLoops) {
  8631      var ret;
  8632      var nodes = this.nodes();
  8633  
  8634      for (var i = 0; i < nodes.length; i++) {
  8635        var ele = nodes[i];
  8636        var degree = ele[degreeFn](includeLoops);
  8637  
  8638        if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
  8639          ret = degree;
  8640        }
  8641      }
  8642  
  8643      return ret;
  8644    };
  8645  }
  8646  
  8647  extend(elesfn$i, {
  8648    minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
  8649      return degree < min;
  8650    }),
  8651    maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
  8652      return degree > max;
  8653    }),
  8654    minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
  8655      return degree < min;
  8656    }),
  8657    maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
  8658      return degree > max;
  8659    }),
  8660    minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
  8661      return degree < min;
  8662    }),
  8663    maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
  8664      return degree > max;
  8665    })
  8666  });
  8667  extend(elesfn$i, {
  8668    totalDegree: function totalDegree(includeLoops) {
  8669      var total = 0;
  8670      var nodes = this.nodes();
  8671  
  8672      for (var i = 0; i < nodes.length; i++) {
  8673        total += nodes[i].degree(includeLoops);
  8674      }
  8675  
  8676      return total;
  8677    }
  8678  });
  8679  
  8680  var fn$2, elesfn$j;
  8681  
  8682  var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
  8683    for (var i = 0; i < eles.length; i++) {
  8684      var ele = eles[i];
  8685  
  8686      if (!ele.locked()) {
  8687        var oldPos = ele._private.position;
  8688        var delta = {
  8689          x: newPos.x != null ? newPos.x - oldPos.x : 0,
  8690          y: newPos.y != null ? newPos.y - oldPos.y : 0
  8691        };
  8692  
  8693        if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
  8694          ele.children().shift(delta, silent);
  8695        }
  8696  
  8697        ele.shiftCachedBoundingBox(delta);
  8698      }
  8699    }
  8700  };
  8701  
  8702  var positionDef = {
  8703    field: 'position',
  8704    bindingEvent: 'position',
  8705    allowBinding: true,
  8706    allowSetting: true,
  8707    settingEvent: 'position',
  8708    settingTriggersEvent: true,
  8709    triggerFnName: 'emitAndNotify',
  8710    allowGetting: true,
  8711    validKeys: ['x', 'y'],
  8712    beforeGet: function beforeGet(ele) {
  8713      ele.updateCompoundBounds();
  8714    },
  8715    beforeSet: function beforeSet(eles, newPos) {
  8716      beforePositionSet(eles, newPos, false);
  8717    },
  8718    onSet: function onSet(eles) {
  8719      eles.dirtyCompoundBoundsCache();
  8720    },
  8721    canSet: function canSet(ele) {
  8722      return !ele.locked();
  8723    }
  8724  };
  8725  fn$2 = elesfn$j = {
  8726    position: define$3.data(positionDef),
  8727    // position but no notification to renderer
  8728    silentPosition: define$3.data(extend({}, positionDef, {
  8729      allowBinding: false,
  8730      allowSetting: true,
  8731      settingTriggersEvent: false,
  8732      allowGetting: false,
  8733      beforeSet: function beforeSet(eles, newPos) {
  8734        beforePositionSet(eles, newPos, true);
  8735      }
  8736    })),
  8737    positions: function positions(pos, silent) {
  8738      if (plainObject(pos)) {
  8739        if (silent) {
  8740          this.silentPosition(pos);
  8741        } else {
  8742          this.position(pos);
  8743        }
  8744      } else if (fn(pos)) {
  8745        var _fn = pos;
  8746        var cy = this.cy();
  8747        cy.startBatch();
  8748  
  8749        for (var i = 0; i < this.length; i++) {
  8750          var ele = this[i];
  8751  
  8752          var _pos = void 0;
  8753  
  8754          if (_pos = _fn(ele, i)) {
  8755            if (silent) {
  8756              ele.silentPosition(_pos);
  8757            } else {
  8758              ele.position(_pos);
  8759            }
  8760          }
  8761        }
  8762  
  8763        cy.endBatch();
  8764      }
  8765  
  8766      return this; // chaining
  8767    },
  8768    silentPositions: function silentPositions(pos) {
  8769      return this.positions(pos, true);
  8770    },
  8771    shift: function shift(dim, val, silent) {
  8772      var delta;
  8773  
  8774      if (plainObject(dim)) {
  8775        delta = {
  8776          x: number(dim.x) ? dim.x : 0,
  8777          y: number(dim.y) ? dim.y : 0
  8778        };
  8779        silent = val;
  8780      } else if (string(dim) && number(val)) {
  8781        delta = {
  8782          x: 0,
  8783          y: 0
  8784        };
  8785        delta[dim] = val;
  8786      }
  8787  
  8788      if (delta != null) {
  8789        var cy = this.cy();
  8790        cy.startBatch();
  8791  
  8792        for (var i = 0; i < this.length; i++) {
  8793          var ele = this[i];
  8794          var pos = ele.position();
  8795          var newPos = {
  8796            x: pos.x + delta.x,
  8797            y: pos.y + delta.y
  8798          };
  8799  
  8800          if (silent) {
  8801            ele.silentPosition(newPos);
  8802          } else {
  8803            ele.position(newPos);
  8804          }
  8805        }
  8806  
  8807        cy.endBatch();
  8808      }
  8809  
  8810      return this;
  8811    },
  8812    silentShift: function silentShift(dim, val) {
  8813      if (plainObject(dim)) {
  8814        this.shift(dim, true);
  8815      } else if (string(dim) && number(val)) {
  8816        this.shift(dim, val, true);
  8817      }
  8818  
  8819      return this;
  8820    },
  8821    // get/set the rendered (i.e. on screen) positon of the element
  8822    renderedPosition: function renderedPosition(dim, val) {
  8823      var ele = this[0];
  8824      var cy = this.cy();
  8825      var zoom = cy.zoom();
  8826      var pan = cy.pan();
  8827      var rpos = plainObject(dim) ? dim : undefined;
  8828      var setting = rpos !== undefined || val !== undefined && string(dim);
  8829  
  8830      if (ele && ele.isNode()) {
  8831        // must have an element and must be a node to return position
  8832        if (setting) {
  8833          for (var i = 0; i < this.length; i++) {
  8834            var _ele = this[i];
  8835  
  8836            if (val !== undefined) {
  8837              // set one dimension
  8838              _ele.position(dim, (val - pan[dim]) / zoom);
  8839            } else if (rpos !== undefined) {
  8840              // set whole position
  8841              _ele.position(renderedToModelPosition(rpos, zoom, pan));
  8842            }
  8843          }
  8844        } else {
  8845          // getting
  8846          var pos = ele.position();
  8847          rpos = modelToRenderedPosition(pos, zoom, pan);
  8848  
  8849          if (dim === undefined) {
  8850            // then return the whole rendered position
  8851            return rpos;
  8852          } else {
  8853            // then return the specified dimension
  8854            return rpos[dim];
  8855          }
  8856        }
  8857      } else if (!setting) {
  8858        return undefined; // for empty collection case
  8859      }
  8860  
  8861      return this; // chaining
  8862    },
  8863    // get/set the position relative to the parent
  8864    relativePosition: function relativePosition(dim, val) {
  8865      var ele = this[0];
  8866      var cy = this.cy();
  8867      var ppos = plainObject(dim) ? dim : undefined;
  8868      var setting = ppos !== undefined || val !== undefined && string(dim);
  8869      var hasCompoundNodes = cy.hasCompoundNodes();
  8870  
  8871      if (ele && ele.isNode()) {
  8872        // must have an element and must be a node to return position
  8873        if (setting) {
  8874          for (var i = 0; i < this.length; i++) {
  8875            var _ele2 = this[i];
  8876            var parent = hasCompoundNodes ? _ele2.parent() : null;
  8877            var hasParent = parent && parent.length > 0;
  8878            var relativeToParent = hasParent;
  8879  
  8880            if (hasParent) {
  8881              parent = parent[0];
  8882            }
  8883  
  8884            var origin = relativeToParent ? parent.position() : {
  8885              x: 0,
  8886              y: 0
  8887            };
  8888  
  8889            if (val !== undefined) {
  8890              // set one dimension
  8891              _ele2.position(dim, val + origin[dim]);
  8892            } else if (ppos !== undefined) {
  8893              // set whole position
  8894              _ele2.position({
  8895                x: ppos.x + origin.x,
  8896                y: ppos.y + origin.y
  8897              });
  8898            }
  8899          }
  8900        } else {
  8901          // getting
  8902          var pos = ele.position();
  8903  
  8904          var _parent = hasCompoundNodes ? ele.parent() : null;
  8905  
  8906          var _hasParent = _parent && _parent.length > 0;
  8907  
  8908          var _relativeToParent = _hasParent;
  8909  
  8910          if (_hasParent) {
  8911            _parent = _parent[0];
  8912          }
  8913  
  8914          var _origin = _relativeToParent ? _parent.position() : {
  8915            x: 0,
  8916            y: 0
  8917          };
  8918  
  8919          ppos = {
  8920            x: pos.x - _origin.x,
  8921            y: pos.y - _origin.y
  8922          };
  8923  
  8924          if (dim === undefined) {
  8925            // then return the whole rendered position
  8926            return ppos;
  8927          } else {
  8928            // then return the specified dimension
  8929            return ppos[dim];
  8930          }
  8931        }
  8932      } else if (!setting) {
  8933        return undefined; // for empty collection case
  8934      }
  8935  
  8936      return this; // chaining
  8937    }
  8938  }; // aliases
  8939  
  8940  fn$2.modelPosition = fn$2.point = fn$2.position;
  8941  fn$2.modelPositions = fn$2.points = fn$2.positions;
  8942  fn$2.renderedPoint = fn$2.renderedPosition;
  8943  fn$2.relativePoint = fn$2.relativePosition;
  8944  var position = elesfn$j;
  8945  
  8946  var fn$3, elesfn$k;
  8947  fn$3 = elesfn$k = {};
  8948  
  8949  elesfn$k.renderedBoundingBox = function (options) {
  8950    var bb = this.boundingBox(options);
  8951    var cy = this.cy();
  8952    var zoom = cy.zoom();
  8953    var pan = cy.pan();
  8954    var x1 = bb.x1 * zoom + pan.x;
  8955    var x2 = bb.x2 * zoom + pan.x;
  8956    var y1 = bb.y1 * zoom + pan.y;
  8957    var y2 = bb.y2 * zoom + pan.y;
  8958    return {
  8959      x1: x1,
  8960      x2: x2,
  8961      y1: y1,
  8962      y2: y2,
  8963      w: x2 - x1,
  8964      h: y2 - y1
  8965    };
  8966  };
  8967  
  8968  elesfn$k.dirtyCompoundBoundsCache = function () {
  8969    var cy = this.cy();
  8970  
  8971    if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
  8972      return this;
  8973    }
  8974  
  8975    this.forEachUp(function (ele) {
  8976      if (ele.isParent()) {
  8977        var _p = ele._private;
  8978        _p.compoundBoundsClean = false;
  8979        _p.bbCache = null;
  8980        ele.emitAndNotify('bounds');
  8981      }
  8982    });
  8983    return this;
  8984  };
  8985  
  8986  elesfn$k.updateCompoundBounds = function () {
  8987    var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  8988    var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
  8989  
  8990    if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
  8991      return this;
  8992    } // save cycles when batching -- but bounds will be stale (or not exist yet)
  8993  
  8994  
  8995    if (!force && cy.batching()) {
  8996      return this;
  8997    }
  8998  
  8999    function update(parent) {
  9000      if (!parent.isParent()) {
  9001        return;
  9002      }
  9003  
  9004      var _p = parent._private;
  9005      var children = parent.children();
  9006      var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
  9007      var min = {
  9008        width: {
  9009          val: parent.pstyle('min-width').pfValue,
  9010          left: parent.pstyle('min-width-bias-left'),
  9011          right: parent.pstyle('min-width-bias-right')
  9012        },
  9013        height: {
  9014          val: parent.pstyle('min-height').pfValue,
  9015          top: parent.pstyle('min-height-bias-top'),
  9016          bottom: parent.pstyle('min-height-bias-bottom')
  9017        }
  9018      };
  9019      var bb = children.boundingBox({
  9020        includeLabels: includeLabels,
  9021        includeOverlays: false,
  9022        // updating the compound bounds happens outside of the regular
  9023        // cache cycle (i.e. before fired events)
  9024        useCache: false
  9025      });
  9026      var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
  9027  
  9028      if (bb.w === 0 || bb.h === 0) {
  9029        bb = {
  9030          w: parent.pstyle('width').pfValue,
  9031          h: parent.pstyle('height').pfValue
  9032        };
  9033        bb.x1 = pos.x - bb.w / 2;
  9034        bb.x2 = pos.x + bb.w / 2;
  9035        bb.y1 = pos.y - bb.h / 2;
  9036        bb.y2 = pos.y + bb.h / 2;
  9037      }
  9038  
  9039      function computeBiasValues(propDiff, propBias, propBiasComplement) {
  9040        var biasDiff = 0;
  9041        var biasComplementDiff = 0;
  9042        var biasTotal = propBias + propBiasComplement;
  9043  
  9044        if (propDiff > 0 && biasTotal > 0) {
  9045          biasDiff = propBias / biasTotal * propDiff;
  9046          biasComplementDiff = propBiasComplement / biasTotal * propDiff;
  9047        }
  9048  
  9049        return {
  9050          biasDiff: biasDiff,
  9051          biasComplementDiff: biasComplementDiff
  9052        };
  9053      }
  9054  
  9055      function computePaddingValues(width, height, paddingObject, relativeTo) {
  9056        // Assuming percentage is number from 0 to 1
  9057        if (paddingObject.units === '%') {
  9058          switch (relativeTo) {
  9059            case 'width':
  9060              return width > 0 ? paddingObject.pfValue * width : 0;
  9061  
  9062            case 'height':
  9063              return height > 0 ? paddingObject.pfValue * height : 0;
  9064  
  9065            case 'average':
  9066              return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
  9067  
  9068            case 'min':
  9069              return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
  9070  
  9071            case 'max':
  9072              return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
  9073  
  9074            default:
  9075              return 0;
  9076          }
  9077        } else if (paddingObject.units === 'px') {
  9078          return paddingObject.pfValue;
  9079        } else {
  9080          return 0;
  9081        }
  9082      }
  9083  
  9084      var leftVal = min.width.left.value;
  9085  
  9086      if (min.width.left.units === 'px' && min.width.val > 0) {
  9087        leftVal = leftVal * 100 / min.width.val;
  9088      }
  9089  
  9090      var rightVal = min.width.right.value;
  9091  
  9092      if (min.width.right.units === 'px' && min.width.val > 0) {
  9093        rightVal = rightVal * 100 / min.width.val;
  9094      }
  9095  
  9096      var topVal = min.height.top.value;
  9097  
  9098      if (min.height.top.units === 'px' && min.height.val > 0) {
  9099        topVal = topVal * 100 / min.height.val;
  9100      }
  9101  
  9102      var bottomVal = min.height.bottom.value;
  9103  
  9104      if (min.height.bottom.units === 'px' && min.height.val > 0) {
  9105        bottomVal = bottomVal * 100 / min.height.val;
  9106      }
  9107  
  9108      var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
  9109      var diffLeft = widthBiasDiffs.biasDiff;
  9110      var diffRight = widthBiasDiffs.biasComplementDiff;
  9111      var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
  9112      var diffTop = heightBiasDiffs.biasDiff;
  9113      var diffBottom = heightBiasDiffs.biasComplementDiff;
  9114      _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
  9115      _p.autoWidth = Math.max(bb.w, min.width.val);
  9116      pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
  9117      _p.autoHeight = Math.max(bb.h, min.height.val);
  9118      pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
  9119    }
  9120  
  9121    for (var i = 0; i < this.length; i++) {
  9122      var ele = this[i];
  9123      var _p = ele._private;
  9124  
  9125      if (!_p.compoundBoundsClean) {
  9126        update(ele);
  9127  
  9128        if (!cy.batching()) {
  9129          _p.compoundBoundsClean = true;
  9130        }
  9131      }
  9132    }
  9133  
  9134    return this;
  9135  };
  9136  
  9137  var noninf = function noninf(x) {
  9138    if (x === Infinity || x === -Infinity) {
  9139      return 0;
  9140    }
  9141  
  9142    return x;
  9143  };
  9144  
  9145  var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
  9146    // don't update with zero area boxes
  9147    if (x2 - x1 === 0 || y2 - y1 === 0) {
  9148      return;
  9149    } // don't update with null dim
  9150  
  9151  
  9152    if (x1 == null || y1 == null || x2 == null || y2 == null) {
  9153      return;
  9154    }
  9155  
  9156    b.x1 = x1 < b.x1 ? x1 : b.x1;
  9157    b.x2 = x2 > b.x2 ? x2 : b.x2;
  9158    b.y1 = y1 < b.y1 ? y1 : b.y1;
  9159    b.y2 = y2 > b.y2 ? y2 : b.y2;
  9160    b.w = b.x2 - b.x1;
  9161    b.h = b.y2 - b.y1;
  9162  };
  9163  
  9164  var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
  9165    if (b2 == null) {
  9166      return b;
  9167    }
  9168  
  9169    return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
  9170  };
  9171  
  9172  var prefixedProperty = function prefixedProperty(obj, field, prefix) {
  9173    return getPrefixedProperty(obj, field, prefix);
  9174  };
  9175  
  9176  var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
  9177    if (ele.cy().headless()) {
  9178      return;
  9179    }
  9180  
  9181    var _p = ele._private;
  9182    var rstyle = _p.rstyle;
  9183    var halfArW = rstyle.arrowWidth / 2;
  9184    var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
  9185    var x;
  9186    var y;
  9187  
  9188    if (arrowType !== 'none') {
  9189      if (prefix === 'source') {
  9190        x = rstyle.srcX;
  9191        y = rstyle.srcY;
  9192      } else if (prefix === 'target') {
  9193        x = rstyle.tgtX;
  9194        y = rstyle.tgtY;
  9195      } else {
  9196        x = rstyle.midX;
  9197        y = rstyle.midY;
  9198      } // always store the individual arrow bounds
  9199  
  9200  
  9201      var bbs = _p.arrowBounds = _p.arrowBounds || {};
  9202      var bb = bbs[prefix] = bbs[prefix] || {};
  9203      bb.x1 = x - halfArW;
  9204      bb.y1 = y - halfArW;
  9205      bb.x2 = x + halfArW;
  9206      bb.y2 = y + halfArW;
  9207      bb.w = bb.x2 - bb.x1;
  9208      bb.h = bb.y2 - bb.y1;
  9209      expandBoundingBox(bb, 1);
  9210      updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
  9211    }
  9212  };
  9213  
  9214  var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
  9215    if (ele.cy().headless()) {
  9216      return;
  9217    }
  9218  
  9219    var prefixDash;
  9220  
  9221    if (prefix) {
  9222      prefixDash = prefix + '-';
  9223    } else {
  9224      prefixDash = '';
  9225    }
  9226  
  9227    var _p = ele._private;
  9228    var rstyle = _p.rstyle;
  9229    var label = ele.pstyle(prefixDash + 'label').strValue;
  9230  
  9231    if (label) {
  9232      var halign = ele.pstyle('text-halign');
  9233      var valign = ele.pstyle('text-valign');
  9234      var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
  9235      var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
  9236      var labelX = prefixedProperty(rstyle, 'labelX', prefix);
  9237      var labelY = prefixedProperty(rstyle, 'labelY', prefix);
  9238      var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
  9239      var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
  9240      var isEdge = ele.isEdge();
  9241      var rotation = ele.pstyle(prefixDash + 'text-rotation');
  9242      var outlineWidth = ele.pstyle('text-outline-width').pfValue;
  9243      var borderWidth = ele.pstyle('text-border-width').pfValue;
  9244      var halfBorderWidth = borderWidth / 2;
  9245      var padding = ele.pstyle('text-background-padding').pfValue;
  9246      var lh = labelHeight;
  9247      var lw = labelWidth;
  9248      var lw_2 = lw / 2;
  9249      var lh_2 = lh / 2;
  9250      var lx1, lx2, ly1, ly2;
  9251  
  9252      if (isEdge) {
  9253        lx1 = labelX - lw_2;
  9254        lx2 = labelX + lw_2;
  9255        ly1 = labelY - lh_2;
  9256        ly2 = labelY + lh_2;
  9257      } else {
  9258        switch (halign.value) {
  9259          case 'left':
  9260            lx1 = labelX - lw;
  9261            lx2 = labelX;
  9262            break;
  9263  
  9264          case 'center':
  9265            lx1 = labelX - lw_2;
  9266            lx2 = labelX + lw_2;
  9267            break;
  9268  
  9269          case 'right':
  9270            lx1 = labelX;
  9271            lx2 = labelX + lw;
  9272            break;
  9273        }
  9274  
  9275        switch (valign.value) {
  9276          case 'top':
  9277            ly1 = labelY - lh;
  9278            ly2 = labelY;
  9279            break;
  9280  
  9281          case 'center':
  9282            ly1 = labelY - lh_2;
  9283            ly2 = labelY + lh_2;
  9284            break;
  9285  
  9286          case 'bottom':
  9287            ly1 = labelY;
  9288            ly2 = labelY + lh;
  9289            break;
  9290        }
  9291      } // shift by margin and expand by outline and border
  9292  
  9293  
  9294      lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding;
  9295      lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding;
  9296      ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding;
  9297      ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding; // always store the unrotated label bounds separately
  9298  
  9299      var bbPrefix = prefix || 'main';
  9300      var bbs = _p.labelBounds;
  9301      var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
  9302      bb.x1 = lx1;
  9303      bb.y1 = ly1;
  9304      bb.x2 = lx2;
  9305      bb.y2 = ly2;
  9306      bb.w = lx2 - lx1;
  9307      bb.h = ly2 - ly1;
  9308      expandBoundingBox(bb, 1); // expand to work around browser dimension inaccuracies
  9309  
  9310      var isAutorotate = isEdge && rotation.strValue === 'autorotate';
  9311      var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
  9312  
  9313      if (isAutorotate || isPfValue) {
  9314        var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
  9315        var cos = Math.cos(theta);
  9316        var sin = Math.sin(theta); // rotation point (default value for center-center)
  9317  
  9318        var xo = (lx1 + lx2) / 2;
  9319        var yo = (ly1 + ly2) / 2;
  9320  
  9321        if (!isEdge) {
  9322          switch (halign.value) {
  9323            case 'left':
  9324              xo = lx2;
  9325              break;
  9326  
  9327            case 'right':
  9328              xo = lx1;
  9329              break;
  9330          }
  9331  
  9332          switch (valign.value) {
  9333            case 'top':
  9334              yo = ly2;
  9335              break;
  9336  
  9337            case 'bottom':
  9338              yo = ly1;
  9339              break;
  9340          }
  9341        }
  9342  
  9343        var rotate = function rotate(x, y) {
  9344          x = x - xo;
  9345          y = y - yo;
  9346          return {
  9347            x: x * cos - y * sin + xo,
  9348            y: x * sin + y * cos + yo
  9349          };
  9350        };
  9351  
  9352        var px1y1 = rotate(lx1, ly1);
  9353        var px1y2 = rotate(lx1, ly2);
  9354        var px2y1 = rotate(lx2, ly1);
  9355        var px2y2 = rotate(lx2, ly2);
  9356        lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
  9357        lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
  9358        ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
  9359        ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
  9360      }
  9361  
  9362      var bbPrefixRot = bbPrefix + 'Rot';
  9363      var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
  9364      bbRot.x1 = lx1;
  9365      bbRot.y1 = ly1;
  9366      bbRot.x2 = lx2;
  9367      bbRot.y2 = ly2;
  9368      bbRot.w = lx2 - lx1;
  9369      bbRot.h = ly2 - ly1;
  9370      updateBounds(bounds, lx1, ly1, lx2, ly2);
  9371      updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
  9372    }
  9373  
  9374    return bounds;
  9375  }; // get the bounding box of the elements (in raw model position)
  9376  
  9377  
  9378  var boundingBoxImpl = function boundingBoxImpl(ele, options) {
  9379    var cy = ele._private.cy;
  9380    var styleEnabled = cy.styleEnabled();
  9381    var headless = cy.headless();
  9382    var bounds = makeBoundingBox();
  9383    var _p = ele._private;
  9384    var isNode = ele.isNode();
  9385    var isEdge = ele.isEdge();
  9386    var ex1, ex2, ey1, ey2; // extrema of body / lines
  9387  
  9388    var x, y; // node pos
  9389  
  9390    var rstyle = _p.rstyle;
  9391    var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
  9392    // (other factors like width values will be considered later in this function anyway)
  9393  
  9394    var isDisplayed = function isDisplayed(ele) {
  9395      return ele.pstyle('display').value !== 'none';
  9396    };
  9397  
  9398    var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
  9399    && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
  9400  
  9401    if (displayed) {
  9402      // displayed suffices, since we will find zero area eles anyway
  9403      var overlayOpacity = 0;
  9404      var overlayPadding = 0;
  9405  
  9406      if (styleEnabled && options.includeOverlays) {
  9407        overlayOpacity = ele.pstyle('overlay-opacity').value;
  9408  
  9409        if (overlayOpacity !== 0) {
  9410          overlayPadding = ele.pstyle('overlay-padding').value;
  9411        }
  9412      }
  9413  
  9414      var w = 0;
  9415      var wHalf = 0;
  9416  
  9417      if (styleEnabled) {
  9418        w = ele.pstyle('width').pfValue;
  9419        wHalf = w / 2;
  9420      }
  9421  
  9422      if (isNode && options.includeNodes) {
  9423        var pos = ele.position();
  9424        x = pos.x;
  9425        y = pos.y;
  9426  
  9427        var _w = ele.outerWidth();
  9428  
  9429        var halfW = _w / 2;
  9430        var h = ele.outerHeight();
  9431        var halfH = h / 2; // handle node dimensions
  9432        /////////////////////////
  9433  
  9434        ex1 = x - halfW;
  9435        ex2 = x + halfW;
  9436        ey1 = y - halfH;
  9437        ey2 = y + halfH;
  9438        updateBounds(bounds, ex1, ey1, ex2, ey2);
  9439      } else if (isEdge && options.includeEdges) {
  9440        if (styleEnabled && !headless) {
  9441          var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
  9442          //////////////////////////////////////////////
  9443  
  9444          ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
  9445          ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
  9446          ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
  9447          ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
  9448  
  9449          ex1 -= wHalf;
  9450          ex2 += wHalf;
  9451          ey1 -= wHalf;
  9452          ey2 += wHalf;
  9453          updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
  9454          ////////////////
  9455  
  9456          if (curveStyle === 'haystack') {
  9457            var hpts = rstyle.haystackPts;
  9458  
  9459            if (hpts && hpts.length === 2) {
  9460              ex1 = hpts[0].x;
  9461              ey1 = hpts[0].y;
  9462              ex2 = hpts[1].x;
  9463              ey2 = hpts[1].y;
  9464  
  9465              if (ex1 > ex2) {
  9466                var temp = ex1;
  9467                ex1 = ex2;
  9468                ex2 = temp;
  9469              }
  9470  
  9471              if (ey1 > ey2) {
  9472                var _temp = ey1;
  9473                ey1 = ey2;
  9474                ey2 = _temp;
  9475              }
  9476  
  9477              updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
  9478            }
  9479          } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
  9480            var pts;
  9481  
  9482            switch (curveStyle) {
  9483              case 'bezier':
  9484              case 'unbundled-bezier':
  9485                pts = rstyle.bezierPts;
  9486                break;
  9487  
  9488              case 'segments':
  9489              case 'taxi':
  9490                pts = rstyle.linePts;
  9491                break;
  9492            }
  9493  
  9494            if (pts != null) {
  9495              for (var j = 0; j < pts.length; j++) {
  9496                var pt = pts[j];
  9497                ex1 = pt.x - wHalf;
  9498                ex2 = pt.x + wHalf;
  9499                ey1 = pt.y - wHalf;
  9500                ey2 = pt.y + wHalf;
  9501                updateBounds(bounds, ex1, ey1, ex2, ey2);
  9502              }
  9503            }
  9504          } // bezier-like or segment-like edge
  9505  
  9506        } else {
  9507          // headless or style disabled
  9508          // fallback on source and target positions
  9509          //////////////////////////////////////////
  9510          var n1 = ele.source();
  9511          var n1pos = n1.position();
  9512          var n2 = ele.target();
  9513          var n2pos = n2.position();
  9514          ex1 = n1pos.x;
  9515          ex2 = n2pos.x;
  9516          ey1 = n1pos.y;
  9517          ey2 = n2pos.y;
  9518  
  9519          if (ex1 > ex2) {
  9520            var _temp2 = ex1;
  9521            ex1 = ex2;
  9522            ex2 = _temp2;
  9523          }
  9524  
  9525          if (ey1 > ey2) {
  9526            var _temp3 = ey1;
  9527            ey1 = ey2;
  9528            ey2 = _temp3;
  9529          } // take into account edge width
  9530  
  9531  
  9532          ex1 -= wHalf;
  9533          ex2 += wHalf;
  9534          ey1 -= wHalf;
  9535          ey2 += wHalf;
  9536          updateBounds(bounds, ex1, ey1, ex2, ey2);
  9537        } // headless or style disabled
  9538  
  9539      } // edges
  9540      // handle edge arrow size
  9541      /////////////////////////
  9542  
  9543  
  9544      if (styleEnabled && options.includeEdges && isEdge) {
  9545        updateBoundsFromArrow(bounds, ele, 'mid-source');
  9546        updateBoundsFromArrow(bounds, ele, 'mid-target');
  9547        updateBoundsFromArrow(bounds, ele, 'source');
  9548        updateBoundsFromArrow(bounds, ele, 'target');
  9549      } // ghost
  9550      ////////
  9551  
  9552  
  9553      if (styleEnabled) {
  9554        var ghost = ele.pstyle('ghost').value === 'yes';
  9555  
  9556        if (ghost) {
  9557          var gx = ele.pstyle('ghost-offset-x').pfValue;
  9558          var gy = ele.pstyle('ghost-offset-y').pfValue;
  9559          updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
  9560        }
  9561      } // always store the body bounds separately from the labels
  9562  
  9563  
  9564      var bbBody = _p.bodyBounds = _p.bodyBounds || {};
  9565      assignBoundingBox(bbBody, bounds);
  9566      expandBoundingBoxSides(bbBody, manualExpansion);
  9567      expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
  9568      // overlay
  9569      //////////
  9570  
  9571      if (styleEnabled) {
  9572        ex1 = bounds.x1;
  9573        ex2 = bounds.x2;
  9574        ey1 = bounds.y1;
  9575        ey2 = bounds.y2;
  9576        updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding);
  9577      } // always store the body bounds separately from the labels
  9578  
  9579  
  9580      var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
  9581      assignBoundingBox(bbOverlay, bounds);
  9582      expandBoundingBoxSides(bbOverlay, manualExpansion);
  9583      expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
  9584      // handle label dimensions
  9585      //////////////////////////
  9586  
  9587      var bbLabels = _p.labelBounds = _p.labelBounds || {};
  9588  
  9589      if (bbLabels.all != null) {
  9590        clearBoundingBox(bbLabels.all);
  9591      } else {
  9592        bbLabels.all = makeBoundingBox();
  9593      }
  9594  
  9595      if (styleEnabled && options.includeLabels) {
  9596        if (options.includeMainLabels) {
  9597          updateBoundsFromLabel(bounds, ele, null);
  9598        }
  9599  
  9600        if (isEdge) {
  9601          if (options.includeSourceLabels) {
  9602            updateBoundsFromLabel(bounds, ele, 'source');
  9603          }
  9604  
  9605          if (options.includeTargetLabels) {
  9606            updateBoundsFromLabel(bounds, ele, 'target');
  9607          }
  9608        }
  9609      } // style enabled for labels
  9610  
  9611    } // if displayed
  9612  
  9613  
  9614    bounds.x1 = noninf(bounds.x1);
  9615    bounds.y1 = noninf(bounds.y1);
  9616    bounds.x2 = noninf(bounds.x2);
  9617    bounds.y2 = noninf(bounds.y2);
  9618    bounds.w = noninf(bounds.x2 - bounds.x1);
  9619    bounds.h = noninf(bounds.y2 - bounds.y1);
  9620  
  9621    if (bounds.w > 0 && bounds.h > 0 && displayed) {
  9622      expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
  9623  
  9624      expandBoundingBox(bounds, 1);
  9625    }
  9626  
  9627    return bounds;
  9628  };
  9629  
  9630  var getKey = function getKey(opts) {
  9631    var i = 0;
  9632  
  9633    var tf = function tf(val) {
  9634      return (val ? 1 : 0) << i++;
  9635    };
  9636  
  9637    var key = 0;
  9638    key += tf(opts.incudeNodes);
  9639    key += tf(opts.includeEdges);
  9640    key += tf(opts.includeLabels);
  9641    key += tf(opts.includeMainLabels);
  9642    key += tf(opts.includeSourceLabels);
  9643    key += tf(opts.includeTargetLabels);
  9644    key += tf(opts.includeOverlays);
  9645    return key;
  9646  };
  9647  
  9648  var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
  9649    if (ele.isEdge()) {
  9650      var p1 = ele.source().position();
  9651      var p2 = ele.target().position();
  9652  
  9653      var r = function r(x) {
  9654        return Math.round(x);
  9655      };
  9656  
  9657      return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
  9658    } else {
  9659      return 0;
  9660    }
  9661  };
  9662  
  9663  var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
  9664    var _p = ele._private;
  9665    var bb;
  9666    var isEdge = ele.isEdge();
  9667    var key = opts == null ? defBbOptsKey : getKey(opts);
  9668    var usingDefOpts = key === defBbOptsKey;
  9669    var currPosKey = getBoundingBoxPosKey(ele);
  9670    var isPosKeySame = _p.bbCachePosKey === currPosKey;
  9671    var useCache = opts.useCache && isPosKeySame;
  9672  
  9673    var isDirty = function isDirty(ele) {
  9674      return ele._private.bbCache == null;
  9675    };
  9676  
  9677    var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
  9678  
  9679    if (needRecalc) {
  9680      if (!isPosKeySame) {
  9681        ele.recalculateRenderedStyle();
  9682      }
  9683  
  9684      bb = boundingBoxImpl(ele, defBbOpts);
  9685      _p.bbCache = bb;
  9686      _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
  9687      _p.bbCachePosKey = currPosKey;
  9688    } else {
  9689      bb = _p.bbCache;
  9690    }
  9691  
  9692    if (!needRecalc && (_p.bbCacheShift.x !== 0 || _p.bbCacheShift.y !== 0)) {
  9693      var shift = assignShiftToBoundingBox;
  9694      var delta = _p.bbCacheShift;
  9695  
  9696      var safeShift = function safeShift(bb, delta) {
  9697        if (bb != null) {
  9698          shift(bb, delta);
  9699        }
  9700      };
  9701  
  9702      shift(bb, delta);
  9703      var bodyBounds = _p.bodyBounds,
  9704          overlayBounds = _p.overlayBounds,
  9705          labelBounds = _p.labelBounds,
  9706          arrowBounds = _p.arrowBounds;
  9707      safeShift(bodyBounds, delta);
  9708      safeShift(overlayBounds, delta);
  9709  
  9710      if (arrowBounds != null) {
  9711        safeShift(arrowBounds.source, delta);
  9712        safeShift(arrowBounds.target, delta);
  9713        safeShift(arrowBounds['mid-source'], delta);
  9714        safeShift(arrowBounds['mid-target'], delta);
  9715      }
  9716  
  9717      if (labelBounds != null) {
  9718        safeShift(labelBounds.main, delta);
  9719        safeShift(labelBounds.all, delta);
  9720        safeShift(labelBounds.source, delta);
  9721        safeShift(labelBounds.target, delta);
  9722      }
  9723    } // always reset the shift, because we either applied the shift or cleared it by doing a fresh recalc
  9724  
  9725  
  9726    _p.bbCacheShift.x = _p.bbCacheShift.y = 0; // not using def opts => need to build up bb from combination of sub bbs
  9727  
  9728    if (!usingDefOpts) {
  9729      var isNode = ele.isNode();
  9730      bb = makeBoundingBox();
  9731  
  9732      if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
  9733        if (opts.includeOverlays) {
  9734          updateBoundsFromBox(bb, _p.overlayBounds);
  9735        } else {
  9736          updateBoundsFromBox(bb, _p.bodyBounds);
  9737        }
  9738      }
  9739  
  9740      if (opts.includeLabels) {
  9741        if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
  9742          updateBoundsFromBox(bb, _p.labelBounds.all);
  9743        } else {
  9744          if (opts.includeMainLabels) {
  9745            updateBoundsFromBox(bb, _p.labelBounds.mainRot);
  9746          }
  9747  
  9748          if (opts.includeSourceLabels) {
  9749            updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
  9750          }
  9751  
  9752          if (opts.includeTargetLabels) {
  9753            updateBoundsFromBox(bb, _p.labelBounds.targetRot);
  9754          }
  9755        }
  9756      }
  9757  
  9758      bb.w = bb.x2 - bb.x1;
  9759      bb.h = bb.y2 - bb.y1;
  9760    }
  9761  
  9762    return bb;
  9763  };
  9764  
  9765  var defBbOpts = {
  9766    includeNodes: true,
  9767    includeEdges: true,
  9768    includeLabels: true,
  9769    includeMainLabels: true,
  9770    includeSourceLabels: true,
  9771    includeTargetLabels: true,
  9772    includeOverlays: true,
  9773    useCache: true
  9774  };
  9775  var defBbOptsKey = getKey(defBbOpts);
  9776  var filledBbOpts = defaults(defBbOpts);
  9777  
  9778  elesfn$k.boundingBox = function (options) {
  9779    var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
  9780    // specified s.t. the cache is used, so check for this case to make it faster by
  9781    // avoiding the overhead of the rest of the function
  9782  
  9783    if (this.length === 1 && this[0]._private.bbCache != null && (options === undefined || options.useCache === undefined || options.useCache === true)) {
  9784      if (options === undefined) {
  9785        options = defBbOpts;
  9786      } else {
  9787        options = filledBbOpts(options);
  9788      }
  9789  
  9790      bounds = cachedBoundingBoxImpl(this[0], options);
  9791    } else {
  9792      bounds = makeBoundingBox();
  9793      options = options || defBbOpts;
  9794      var opts = filledBbOpts(options);
  9795      var eles = this;
  9796      var cy = eles.cy();
  9797      var styleEnabled = cy.styleEnabled();
  9798  
  9799      if (styleEnabled) {
  9800        for (var i = 0; i < eles.length; i++) {
  9801          var ele = eles[i];
  9802          var _p = ele._private;
  9803          var currPosKey = getBoundingBoxPosKey(ele);
  9804          var isPosKeySame = _p.bbCachePosKey === currPosKey;
  9805          var useCache = opts.useCache && isPosKeySame;
  9806          ele.recalculateRenderedStyle(useCache);
  9807        }
  9808      }
  9809  
  9810      this.updateCompoundBounds();
  9811  
  9812      for (var _i = 0; _i < eles.length; _i++) {
  9813        var _ele = eles[_i];
  9814        updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
  9815      }
  9816    }
  9817  
  9818    bounds.x1 = noninf(bounds.x1);
  9819    bounds.y1 = noninf(bounds.y1);
  9820    bounds.x2 = noninf(bounds.x2);
  9821    bounds.y2 = noninf(bounds.y2);
  9822    bounds.w = noninf(bounds.x2 - bounds.x1);
  9823    bounds.h = noninf(bounds.y2 - bounds.y1);
  9824    return bounds;
  9825  };
  9826  
  9827  elesfn$k.dirtyBoundingBoxCache = function () {
  9828    for (var i = 0; i < this.length; i++) {
  9829      var _p = this[i]._private;
  9830      _p.bbCache = null;
  9831      _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
  9832      _p.bbCachePosKey = null;
  9833      _p.bodyBounds = null;
  9834      _p.overlayBounds = null;
  9835      _p.labelBounds.all = null;
  9836      _p.labelBounds.source = null;
  9837      _p.labelBounds.target = null;
  9838      _p.labelBounds.main = null;
  9839      _p.labelBounds.sourceRot = null;
  9840      _p.labelBounds.targetRot = null;
  9841      _p.labelBounds.mainRot = null;
  9842      _p.arrowBounds.source = null;
  9843      _p.arrowBounds.target = null;
  9844      _p.arrowBounds['mid-source'] = null;
  9845      _p.arrowBounds['mid-target'] = null;
  9846    }
  9847  
  9848    this.emitAndNotify('bounds');
  9849    return this;
  9850  };
  9851  
  9852  elesfn$k.shiftCachedBoundingBox = function (delta) {
  9853    for (var i = 0; i < this.length; i++) {
  9854      var ele = this[i];
  9855      var _p = ele._private;
  9856      var bb = _p.bbCache;
  9857  
  9858      if (bb != null) {
  9859        _p.bbCacheShift.x += delta.x;
  9860        _p.bbCacheShift.y += delta.y;
  9861      }
  9862    }
  9863  
  9864    this.emitAndNotify('bounds');
  9865    return this;
  9866  }; // private helper to get bounding box for custom node positions
  9867  // - good for perf in certain cases but currently requires dirtying the rendered style
  9868  // - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
  9869  // - try to use for only things like discrete layouts where the node position would change anyway
  9870  
  9871  
  9872  elesfn$k.boundingBoxAt = function (fn) {
  9873    var nodes = this.nodes();
  9874    var cy = this.cy();
  9875    var hasCompoundNodes = cy.hasCompoundNodes();
  9876  
  9877    if (hasCompoundNodes) {
  9878      nodes = nodes.filter(function (node) {
  9879        return !node.isParent();
  9880      });
  9881    }
  9882  
  9883    if (plainObject(fn)) {
  9884      var obj = fn;
  9885  
  9886      fn = function fn() {
  9887        return obj;
  9888      };
  9889    }
  9890  
  9891    var storeOldPos = function storeOldPos(node, i) {
  9892      return node._private.bbAtOldPos = fn(node, i);
  9893    };
  9894  
  9895    var getOldPos = function getOldPos(node) {
  9896      return node._private.bbAtOldPos;
  9897    };
  9898  
  9899    cy.startBatch();
  9900    nodes.forEach(storeOldPos).silentPositions(fn);
  9901  
  9902    if (hasCompoundNodes) {
  9903      this.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
  9904    }
  9905  
  9906    var bb = copyBoundingBox(this.boundingBox({
  9907      useCache: false
  9908    }));
  9909    nodes.silentPositions(getOldPos);
  9910    cy.endBatch();
  9911    return bb;
  9912  };
  9913  
  9914  fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
  9915  fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
  9916  var bounds = elesfn$k;
  9917  
  9918  var fn$4, elesfn$l;
  9919  fn$4 = elesfn$l = {};
  9920  
  9921  var defineDimFns = function defineDimFns(opts) {
  9922    opts.uppercaseName = capitalize(opts.name);
  9923    opts.autoName = 'auto' + opts.uppercaseName;
  9924    opts.labelName = 'label' + opts.uppercaseName;
  9925    opts.outerName = 'outer' + opts.uppercaseName;
  9926    opts.uppercaseOuterName = capitalize(opts.outerName);
  9927  
  9928    fn$4[opts.name] = function dimImpl() {
  9929      var ele = this[0];
  9930      var _p = ele._private;
  9931      var cy = _p.cy;
  9932      var styleEnabled = cy._private.styleEnabled;
  9933  
  9934      if (ele) {
  9935        if (styleEnabled) {
  9936          if (ele.isParent()) {
  9937            ele.updateCompoundBounds();
  9938            return _p[opts.autoName] || 0;
  9939          }
  9940  
  9941          var d = ele.pstyle(opts.name);
  9942  
  9943          switch (d.strValue) {
  9944            case 'label':
  9945              ele.recalculateRenderedStyle();
  9946              return _p.rstyle[opts.labelName] || 0;
  9947  
  9948            default:
  9949              return d.pfValue;
  9950          }
  9951        } else {
  9952          return 1;
  9953        }
  9954      }
  9955    };
  9956  
  9957    fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
  9958      var ele = this[0];
  9959      var _p = ele._private;
  9960      var cy = _p.cy;
  9961      var styleEnabled = cy._private.styleEnabled;
  9962  
  9963      if (ele) {
  9964        if (styleEnabled) {
  9965          var dim = ele[opts.name]();
  9966          var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
  9967  
  9968          var padding = 2 * ele.padding();
  9969          return dim + border + padding;
  9970        } else {
  9971          return 1;
  9972        }
  9973      }
  9974    };
  9975  
  9976    fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
  9977      var ele = this[0];
  9978  
  9979      if (ele) {
  9980        var d = ele[opts.name]();
  9981        return d * this.cy().zoom();
  9982      }
  9983    };
  9984  
  9985    fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
  9986      var ele = this[0];
  9987  
  9988      if (ele) {
  9989        var od = ele[opts.outerName]();
  9990        return od * this.cy().zoom();
  9991      }
  9992    };
  9993  };
  9994  
  9995  defineDimFns({
  9996    name: 'width'
  9997  });
  9998  defineDimFns({
  9999    name: 'height'
 10000  });
 10001  
 10002  elesfn$l.padding = function () {
 10003    var ele = this[0];
 10004    var _p = ele._private;
 10005  
 10006    if (ele.isParent()) {
 10007      ele.updateCompoundBounds();
 10008  
 10009      if (_p.autoPadding !== undefined) {
 10010        return _p.autoPadding;
 10011      } else {
 10012        return ele.pstyle('padding').pfValue;
 10013      }
 10014    } else {
 10015      return ele.pstyle('padding').pfValue;
 10016    }
 10017  };
 10018  
 10019  elesfn$l.paddedHeight = function () {
 10020    var ele = this[0];
 10021    return ele.height() + 2 * ele.padding();
 10022  };
 10023  
 10024  elesfn$l.paddedWidth = function () {
 10025    var ele = this[0];
 10026    return ele.width() + 2 * ele.padding();
 10027  };
 10028  
 10029  var widthHeight = elesfn$l;
 10030  
 10031  var ifEdge = function ifEdge(ele, getValue) {
 10032    if (ele.isEdge()) {
 10033      return getValue(ele);
 10034    }
 10035  };
 10036  
 10037  var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
 10038    if (ele.isEdge()) {
 10039      var cy = ele.cy();
 10040      return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
 10041    }
 10042  };
 10043  
 10044  var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
 10045    if (ele.isEdge()) {
 10046      var cy = ele.cy();
 10047      var pan = cy.pan();
 10048      var zoom = cy.zoom();
 10049      return getPoints(ele).map(function (p) {
 10050        return modelToRenderedPosition(p, zoom, pan);
 10051      });
 10052    }
 10053  };
 10054  
 10055  var controlPoints = function controlPoints(ele) {
 10056    return ele.renderer().getControlPoints(ele);
 10057  };
 10058  
 10059  var segmentPoints = function segmentPoints(ele) {
 10060    return ele.renderer().getSegmentPoints(ele);
 10061  };
 10062  
 10063  var sourceEndpoint = function sourceEndpoint(ele) {
 10064    return ele.renderer().getSourceEndpoint(ele);
 10065  };
 10066  
 10067  var targetEndpoint = function targetEndpoint(ele) {
 10068    return ele.renderer().getTargetEndpoint(ele);
 10069  };
 10070  
 10071  var midpoint = function midpoint(ele) {
 10072    return ele.renderer().getEdgeMidpoint(ele);
 10073  };
 10074  
 10075  var pts = {
 10076    controlPoints: {
 10077      get: controlPoints,
 10078      mult: true
 10079    },
 10080    segmentPoints: {
 10081      get: segmentPoints,
 10082      mult: true
 10083    },
 10084    sourceEndpoint: {
 10085      get: sourceEndpoint
 10086    },
 10087    targetEndpoint: {
 10088      get: targetEndpoint
 10089    },
 10090    midpoint: {
 10091      get: midpoint
 10092    }
 10093  };
 10094  
 10095  var renderedName = function renderedName(name) {
 10096    return 'rendered' + name[0].toUpperCase() + name.substr(1);
 10097  };
 10098  
 10099  var edgePoints = Object.keys(pts).reduce(function (obj, name) {
 10100    var spec = pts[name];
 10101    var rName = renderedName(name);
 10102  
 10103    obj[name] = function () {
 10104      return ifEdge(this, spec.get);
 10105    };
 10106  
 10107    if (spec.mult) {
 10108      obj[rName] = function () {
 10109        return ifEdgeRenderedPositions(this, spec.get);
 10110      };
 10111    } else {
 10112      obj[rName] = function () {
 10113        return ifEdgeRenderedPosition(this, spec.get);
 10114      };
 10115    }
 10116  
 10117    return obj;
 10118  }, {});
 10119  
 10120  var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
 10121  
 10122  /*!
 10123  Event object based on jQuery events, MIT license
 10124  
 10125  https://jquery.org/license/
 10126  https://tldrlegal.com/license/mit-license
 10127  https://github.com/jquery/jquery/blob/master/src/event.js
 10128  */
 10129  var Event = function Event(src, props) {
 10130    this.recycle(src, props);
 10131  };
 10132  
 10133  function returnFalse() {
 10134    return false;
 10135  }
 10136  
 10137  function returnTrue() {
 10138    return true;
 10139  } // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
 10140  
 10141  
 10142  Event.prototype = {
 10143    instanceString: function instanceString() {
 10144      return 'event';
 10145    },
 10146    recycle: function recycle(src, props) {
 10147      this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
 10148  
 10149      if (src != null && src.preventDefault) {
 10150        // Browser Event object
 10151        this.type = src.type; // Events bubbling up the document may have been marked as prevented
 10152        // by a handler lower down the tree; reflect the correct value.
 10153  
 10154        this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
 10155      } else if (src != null && src.type) {
 10156        // Plain object containing all event details
 10157        props = src;
 10158      } else {
 10159        // Event string
 10160        this.type = src;
 10161      } // Put explicitly provided properties onto the event object
 10162  
 10163  
 10164      if (props != null) {
 10165        // more efficient to manually copy fields we use
 10166        this.originalEvent = props.originalEvent;
 10167        this.type = props.type != null ? props.type : this.type;
 10168        this.cy = props.cy;
 10169        this.target = props.target;
 10170        this.position = props.position;
 10171        this.renderedPosition = props.renderedPosition;
 10172        this.namespace = props.namespace;
 10173        this.layout = props.layout;
 10174      }
 10175  
 10176      if (this.cy != null && this.position != null && this.renderedPosition == null) {
 10177        // create a rendered position based on the passed position
 10178        var pos = this.position;
 10179        var zoom = this.cy.zoom();
 10180        var pan = this.cy.pan();
 10181        this.renderedPosition = {
 10182          x: pos.x * zoom + pan.x,
 10183          y: pos.y * zoom + pan.y
 10184        };
 10185      } // Create a timestamp if incoming event doesn't have one
 10186  
 10187  
 10188      this.timeStamp = src && src.timeStamp || Date.now();
 10189    },
 10190    preventDefault: function preventDefault() {
 10191      this.isDefaultPrevented = returnTrue;
 10192      var e = this.originalEvent;
 10193  
 10194      if (!e) {
 10195        return;
 10196      } // if preventDefault exists run it on the original event
 10197  
 10198  
 10199      if (e.preventDefault) {
 10200        e.preventDefault();
 10201      }
 10202    },
 10203    stopPropagation: function stopPropagation() {
 10204      this.isPropagationStopped = returnTrue;
 10205      var e = this.originalEvent;
 10206  
 10207      if (!e) {
 10208        return;
 10209      } // if stopPropagation exists run it on the original event
 10210  
 10211  
 10212      if (e.stopPropagation) {
 10213        e.stopPropagation();
 10214      }
 10215    },
 10216    stopImmediatePropagation: function stopImmediatePropagation() {
 10217      this.isImmediatePropagationStopped = returnTrue;
 10218      this.stopPropagation();
 10219    },
 10220    isDefaultPrevented: returnFalse,
 10221    isPropagationStopped: returnFalse,
 10222    isImmediatePropagationStopped: returnFalse
 10223  };
 10224  
 10225  var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
 10226  
 10227  var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
 10228  
 10229  var defaults$8 = {
 10230    qualifierCompare: function qualifierCompare(q1, q2) {
 10231      return q1 === q2;
 10232    },
 10233    eventMatches: function eventMatches()
 10234    /*context, listener, eventObj*/
 10235    {
 10236      return true;
 10237    },
 10238    addEventFields: function addEventFields()
 10239    /*context, evt*/
 10240    {},
 10241    callbackContext: function callbackContext(context
 10242    /*, listener, eventObj*/
 10243    ) {
 10244      return context;
 10245    },
 10246    beforeEmit: function beforeEmit()
 10247    /* context, listener, eventObj */
 10248    {},
 10249    afterEmit: function afterEmit()
 10250    /* context, listener, eventObj */
 10251    {},
 10252    bubble: function bubble()
 10253    /*context*/
 10254    {
 10255      return false;
 10256    },
 10257    parent: function parent()
 10258    /*context*/
 10259    {
 10260      return null;
 10261    },
 10262    context: null
 10263  };
 10264  var defaultsKeys = Object.keys(defaults$8);
 10265  var emptyOpts = {};
 10266  
 10267  function Emitter() {
 10268    var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
 10269    var context = arguments.length > 1 ? arguments[1] : undefined;
 10270  
 10271    // micro-optimisation vs Object.assign() -- reduces Element instantiation time
 10272    for (var i = 0; i < defaultsKeys.length; i++) {
 10273      var key = defaultsKeys[i];
 10274      this[key] = opts[key] || defaults$8[key];
 10275    }
 10276  
 10277    this.context = context || this.context;
 10278    this.listeners = [];
 10279    this.emitting = 0;
 10280  }
 10281  
 10282  var p = Emitter.prototype;
 10283  
 10284  var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
 10285    if (fn(qualifier)) {
 10286      callback = qualifier;
 10287      qualifier = null;
 10288    }
 10289  
 10290    if (confOverrides) {
 10291      if (conf == null) {
 10292        conf = confOverrides;
 10293      } else {
 10294        conf = extend({}, conf, confOverrides);
 10295      }
 10296    }
 10297  
 10298    var eventList = array(events) ? events : events.split(/\s+/);
 10299  
 10300    for (var i = 0; i < eventList.length; i++) {
 10301      var evt = eventList[i];
 10302  
 10303      if (emptyString(evt)) {
 10304        continue;
 10305      }
 10306  
 10307      var match = evt.match(eventRegex); // type[.namespace]
 10308  
 10309      if (match) {
 10310        var type = match[1];
 10311        var namespace = match[2] ? match[2] : null;
 10312        var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
 10313  
 10314        if (ret === false) {
 10315          break;
 10316        } // allow exiting early
 10317  
 10318      }
 10319    }
 10320  };
 10321  
 10322  var makeEventObj = function makeEventObj(self, obj) {
 10323    self.addEventFields(self.context, obj);
 10324    return new Event(obj.type, obj);
 10325  };
 10326  
 10327  var forEachEventObj = function forEachEventObj(self, handler, events) {
 10328    if (event(events)) {
 10329      handler(self, events);
 10330      return;
 10331    } else if (plainObject(events)) {
 10332      handler(self, makeEventObj(self, events));
 10333      return;
 10334    }
 10335  
 10336    var eventList = array(events) ? events : events.split(/\s+/);
 10337  
 10338    for (var i = 0; i < eventList.length; i++) {
 10339      var evt = eventList[i];
 10340  
 10341      if (emptyString(evt)) {
 10342        continue;
 10343      }
 10344  
 10345      var match = evt.match(eventRegex); // type[.namespace]
 10346  
 10347      if (match) {
 10348        var type = match[1];
 10349        var namespace = match[2] ? match[2] : null;
 10350        var eventObj = makeEventObj(self, {
 10351          type: type,
 10352          namespace: namespace,
 10353          target: self.context
 10354        });
 10355        handler(self, eventObj);
 10356      }
 10357    }
 10358  };
 10359  
 10360  p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
 10361    forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
 10362      if (fn(callback)) {
 10363        self.listeners.push({
 10364          event: event,
 10365          // full event string
 10366          callback: callback,
 10367          // callback to run
 10368          type: type,
 10369          // the event type (e.g. 'click')
 10370          namespace: namespace,
 10371          // the event namespace (e.g. ".foo")
 10372          qualifier: qualifier,
 10373          // a restriction on whether to match this emitter
 10374          conf: conf // additional configuration
 10375  
 10376        });
 10377      }
 10378    }, events, qualifier, callback, conf, confOverrides);
 10379    return this;
 10380  };
 10381  
 10382  p.one = function (events, qualifier, callback, conf) {
 10383    return this.on(events, qualifier, callback, conf, {
 10384      one: true
 10385    });
 10386  };
 10387  
 10388  p.removeListener = p.off = function (events, qualifier, callback, conf) {
 10389    var _this = this;
 10390  
 10391    if (this.emitting !== 0) {
 10392      this.listeners = copyArray(this.listeners);
 10393    }
 10394  
 10395    var listeners = this.listeners;
 10396  
 10397    var _loop = function _loop(i) {
 10398      var listener = listeners[i];
 10399      forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
 10400      /*, conf*/
 10401      ) {
 10402        if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
 10403          listeners.splice(i, 1);
 10404          return false;
 10405        }
 10406      }, events, qualifier, callback, conf);
 10407    };
 10408  
 10409    for (var i = listeners.length - 1; i >= 0; i--) {
 10410      _loop(i);
 10411    }
 10412  
 10413    return this;
 10414  };
 10415  
 10416  p.removeAllListeners = function () {
 10417    return this.removeListener('*');
 10418  };
 10419  
 10420  p.emit = p.trigger = function (events, extraParams, manualCallback) {
 10421    var listeners = this.listeners;
 10422    var numListenersBeforeEmit = listeners.length;
 10423    this.emitting++;
 10424  
 10425    if (!array(extraParams)) {
 10426      extraParams = [extraParams];
 10427    }
 10428  
 10429    forEachEventObj(this, function (self, eventObj) {
 10430      if (manualCallback != null) {
 10431        listeners = [{
 10432          event: eventObj.event,
 10433          type: eventObj.type,
 10434          namespace: eventObj.namespace,
 10435          callback: manualCallback
 10436        }];
 10437        numListenersBeforeEmit = listeners.length;
 10438      }
 10439  
 10440      var _loop2 = function _loop2(i) {
 10441        var listener = listeners[i];
 10442  
 10443        if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
 10444          var args = [eventObj];
 10445  
 10446          if (extraParams != null) {
 10447            push(args, extraParams);
 10448          }
 10449  
 10450          self.beforeEmit(self.context, listener, eventObj);
 10451  
 10452          if (listener.conf && listener.conf.one) {
 10453            self.listeners = self.listeners.filter(function (l) {
 10454              return l !== listener;
 10455            });
 10456          }
 10457  
 10458          var context = self.callbackContext(self.context, listener, eventObj);
 10459          var ret = listener.callback.apply(context, args);
 10460          self.afterEmit(self.context, listener, eventObj);
 10461  
 10462          if (ret === false) {
 10463            eventObj.stopPropagation();
 10464            eventObj.preventDefault();
 10465          }
 10466        } // if listener matches
 10467  
 10468      };
 10469  
 10470      for (var i = 0; i < numListenersBeforeEmit; i++) {
 10471        _loop2(i);
 10472      } // for listener
 10473  
 10474  
 10475      if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
 10476        self.parent(self.context).emit(eventObj, extraParams);
 10477      }
 10478    }, events);
 10479    this.emitting--;
 10480    return this;
 10481  };
 10482  
 10483  var emitterOptions = {
 10484    qualifierCompare: function qualifierCompare(selector1, selector2) {
 10485      if (selector1 == null || selector2 == null) {
 10486        return selector1 == null && selector2 == null;
 10487      } else {
 10488        return selector1.sameText(selector2);
 10489      }
 10490    },
 10491    eventMatches: function eventMatches(ele, listener, eventObj) {
 10492      var selector = listener.qualifier;
 10493  
 10494      if (selector != null) {
 10495        return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
 10496      }
 10497  
 10498      return true;
 10499    },
 10500    addEventFields: function addEventFields(ele, evt) {
 10501      evt.cy = ele.cy();
 10502      evt.target = ele;
 10503    },
 10504    callbackContext: function callbackContext(ele, listener, eventObj) {
 10505      return listener.qualifier != null ? eventObj.target : ele;
 10506    },
 10507    beforeEmit: function beforeEmit(context, listener
 10508    /*, eventObj*/
 10509    ) {
 10510      if (listener.conf && listener.conf.once) {
 10511        listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
 10512      }
 10513    },
 10514    bubble: function bubble() {
 10515      return true;
 10516    },
 10517    parent: function parent(ele) {
 10518      return ele.isChild() ? ele.parent() : ele.cy();
 10519    }
 10520  };
 10521  
 10522  var argSelector = function argSelector(arg) {
 10523    if (string(arg)) {
 10524      return new Selector(arg);
 10525    } else {
 10526      return arg;
 10527    }
 10528  };
 10529  
 10530  var elesfn$m = {
 10531    createEmitter: function createEmitter() {
 10532      for (var i = 0; i < this.length; i++) {
 10533        var ele = this[i];
 10534        var _p = ele._private;
 10535  
 10536        if (!_p.emitter) {
 10537          _p.emitter = new Emitter(emitterOptions, ele);
 10538        }
 10539      }
 10540  
 10541      return this;
 10542    },
 10543    emitter: function emitter() {
 10544      return this._private.emitter;
 10545    },
 10546    on: function on(events, selector, callback) {
 10547      var argSel = argSelector(selector);
 10548  
 10549      for (var i = 0; i < this.length; i++) {
 10550        var ele = this[i];
 10551        ele.emitter().on(events, argSel, callback);
 10552      }
 10553  
 10554      return this;
 10555    },
 10556    removeListener: function removeListener(events, selector, callback) {
 10557      var argSel = argSelector(selector);
 10558  
 10559      for (var i = 0; i < this.length; i++) {
 10560        var ele = this[i];
 10561        ele.emitter().removeListener(events, argSel, callback);
 10562      }
 10563  
 10564      return this;
 10565    },
 10566    removeAllListeners: function removeAllListeners() {
 10567      for (var i = 0; i < this.length; i++) {
 10568        var ele = this[i];
 10569        ele.emitter().removeAllListeners();
 10570      }
 10571  
 10572      return this;
 10573    },
 10574    one: function one(events, selector, callback) {
 10575      var argSel = argSelector(selector);
 10576  
 10577      for (var i = 0; i < this.length; i++) {
 10578        var ele = this[i];
 10579        ele.emitter().one(events, argSel, callback);
 10580      }
 10581  
 10582      return this;
 10583    },
 10584    once: function once(events, selector, callback) {
 10585      var argSel = argSelector(selector);
 10586  
 10587      for (var i = 0; i < this.length; i++) {
 10588        var ele = this[i];
 10589        ele.emitter().on(events, argSel, callback, {
 10590          once: true,
 10591          onceCollection: this
 10592        });
 10593      }
 10594    },
 10595    emit: function emit(events, extraParams) {
 10596      for (var i = 0; i < this.length; i++) {
 10597        var ele = this[i];
 10598        ele.emitter().emit(events, extraParams);
 10599      }
 10600  
 10601      return this;
 10602    },
 10603    emitAndNotify: function emitAndNotify(event, extraParams) {
 10604      // for internal use only
 10605      if (this.length === 0) {
 10606        return;
 10607      } // empty collections don't need to notify anything
 10608      // notify renderer
 10609  
 10610  
 10611      this.cy().notify(event, this);
 10612      this.emit(event, extraParams);
 10613      return this;
 10614    }
 10615  };
 10616  define$3.eventAliasesOn(elesfn$m);
 10617  
 10618  var elesfn$n = {
 10619    nodes: function nodes(selector) {
 10620      return this.filter(function (ele) {
 10621        return ele.isNode();
 10622      }).filter(selector);
 10623    },
 10624    edges: function edges(selector) {
 10625      return this.filter(function (ele) {
 10626        return ele.isEdge();
 10627      }).filter(selector);
 10628    },
 10629    // internal helper to get nodes and edges as separate collections with single iteration over elements
 10630    byGroup: function byGroup() {
 10631      var nodes = this.spawn();
 10632      var edges = this.spawn();
 10633  
 10634      for (var i = 0; i < this.length; i++) {
 10635        var ele = this[i];
 10636  
 10637        if (ele.isNode()) {
 10638          nodes.merge(ele);
 10639        } else {
 10640          edges.merge(ele);
 10641        }
 10642      }
 10643  
 10644      return {
 10645        nodes: nodes,
 10646        edges: edges
 10647      };
 10648    },
 10649    filter: function filter(_filter, thisArg) {
 10650      if (_filter === undefined) {
 10651        // check this first b/c it's the most common/performant case
 10652        return this;
 10653      } else if (string(_filter) || elementOrCollection(_filter)) {
 10654        return new Selector(_filter).filter(this);
 10655      } else if (fn(_filter)) {
 10656        var filterEles = this.spawn();
 10657        var eles = this;
 10658  
 10659        for (var i = 0; i < eles.length; i++) {
 10660          var ele = eles[i];
 10661          var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
 10662  
 10663          if (include) {
 10664            filterEles.merge(ele);
 10665          }
 10666        }
 10667  
 10668        return filterEles;
 10669      }
 10670  
 10671      return this.spawn(); // if not handled by above, give 'em an empty collection
 10672    },
 10673    not: function not(toRemove) {
 10674      if (!toRemove) {
 10675        return this;
 10676      } else {
 10677        if (string(toRemove)) {
 10678          toRemove = this.filter(toRemove);
 10679        }
 10680  
 10681        var elements = [];
 10682        var rMap = toRemove._private.map;
 10683  
 10684        for (var i = 0; i < this.length; i++) {
 10685          var element = this[i];
 10686          var remove = rMap.has(element.id());
 10687  
 10688          if (!remove) {
 10689            elements.push(element);
 10690          }
 10691        }
 10692  
 10693        return this.spawn(elements);
 10694      }
 10695    },
 10696    absoluteComplement: function absoluteComplement() {
 10697      var cy = this.cy();
 10698      return cy.mutableElements().not(this);
 10699    },
 10700    intersect: function intersect(other) {
 10701      // if a selector is specified, then filter by it instead
 10702      if (string(other)) {
 10703        var selector = other;
 10704        return this.filter(selector);
 10705      }
 10706  
 10707      var elements = [];
 10708      var col1 = this;
 10709      var col2 = other;
 10710      var col1Smaller = this.length < other.length;
 10711      var map2 = col1Smaller ? col2._private.map : col1._private.map;
 10712      var col = col1Smaller ? col1 : col2;
 10713  
 10714      for (var i = 0; i < col.length; i++) {
 10715        var id = col[i]._private.data.id;
 10716        var entry = map2.get(id);
 10717  
 10718        if (entry) {
 10719          elements.push(entry.ele);
 10720        }
 10721      }
 10722  
 10723      return this.spawn(elements);
 10724    },
 10725    xor: function xor(other) {
 10726      var cy = this._private.cy;
 10727  
 10728      if (string(other)) {
 10729        other = cy.$(other);
 10730      }
 10731  
 10732      var elements = [];
 10733      var col1 = this;
 10734      var col2 = other;
 10735  
 10736      var add = function add(col, other) {
 10737        for (var i = 0; i < col.length; i++) {
 10738          var ele = col[i];
 10739          var id = ele._private.data.id;
 10740          var inOther = other.hasElementWithId(id);
 10741  
 10742          if (!inOther) {
 10743            elements.push(ele);
 10744          }
 10745        }
 10746      };
 10747  
 10748      add(col1, col2);
 10749      add(col2, col1);
 10750      return this.spawn(elements);
 10751    },
 10752    diff: function diff(other) {
 10753      var cy = this._private.cy;
 10754  
 10755      if (string(other)) {
 10756        other = cy.$(other);
 10757      }
 10758  
 10759      var left = [];
 10760      var right = [];
 10761      var both = [];
 10762      var col1 = this;
 10763      var col2 = other;
 10764  
 10765      var add = function add(col, other, retEles) {
 10766        for (var i = 0; i < col.length; i++) {
 10767          var ele = col[i];
 10768          var id = ele._private.data.id;
 10769          var inOther = other.hasElementWithId(id);
 10770  
 10771          if (inOther) {
 10772            both.push(ele);
 10773          } else {
 10774            retEles.push(ele);
 10775          }
 10776        }
 10777      };
 10778  
 10779      add(col1, col2, left);
 10780      add(col2, col1, right);
 10781      return {
 10782        left: this.spawn(left, {
 10783          unique: true
 10784        }),
 10785        right: this.spawn(right, {
 10786          unique: true
 10787        }),
 10788        both: this.spawn(both, {
 10789          unique: true
 10790        })
 10791      };
 10792    },
 10793    add: function add(toAdd) {
 10794      var cy = this._private.cy;
 10795  
 10796      if (!toAdd) {
 10797        return this;
 10798      }
 10799  
 10800      if (string(toAdd)) {
 10801        var selector = toAdd;
 10802        toAdd = cy.mutableElements().filter(selector);
 10803      }
 10804  
 10805      var elements = [];
 10806  
 10807      for (var i = 0; i < this.length; i++) {
 10808        elements.push(this[i]);
 10809      }
 10810  
 10811      var map = this._private.map;
 10812  
 10813      for (var _i = 0; _i < toAdd.length; _i++) {
 10814        var add = !map.has(toAdd[_i].id());
 10815  
 10816        if (add) {
 10817          elements.push(toAdd[_i]);
 10818        }
 10819      }
 10820  
 10821      return this.spawn(elements);
 10822    },
 10823    // in place merge on calling collection
 10824    merge: function merge(toAdd) {
 10825      var _p = this._private;
 10826      var cy = _p.cy;
 10827  
 10828      if (!toAdd) {
 10829        return this;
 10830      }
 10831  
 10832      if (toAdd && string(toAdd)) {
 10833        var selector = toAdd;
 10834        toAdd = cy.mutableElements().filter(selector);
 10835      }
 10836  
 10837      var map = _p.map;
 10838  
 10839      for (var i = 0; i < toAdd.length; i++) {
 10840        var toAddEle = toAdd[i];
 10841        var id = toAddEle._private.data.id;
 10842        var add = !map.has(id);
 10843  
 10844        if (add) {
 10845          var index = this.length++;
 10846          this[index] = toAddEle;
 10847          map.set(id, {
 10848            ele: toAddEle,
 10849            index: index
 10850          });
 10851        } else {
 10852          // replace
 10853          var _index = map.get(id).index;
 10854          this[_index] = toAddEle;
 10855          map.set(id, {
 10856            ele: toAddEle,
 10857            index: _index
 10858          });
 10859        }
 10860      }
 10861  
 10862      return this; // chaining
 10863    },
 10864    unmergeAt: function unmergeAt(i) {
 10865      var ele = this[i];
 10866      var id = ele.id();
 10867      var _p = this._private;
 10868      var map = _p.map; // remove ele
 10869  
 10870      this[i] = undefined;
 10871      map["delete"](id);
 10872      var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
 10873  
 10874      if (this.length > 1 && !unmergedLastEle) {
 10875        var lastEleI = this.length - 1;
 10876        var lastEle = this[lastEleI];
 10877        var lastEleId = lastEle._private.data.id;
 10878        this[lastEleI] = undefined;
 10879        this[i] = lastEle;
 10880        map.set(lastEleId, {
 10881          ele: lastEle,
 10882          index: i
 10883        });
 10884      } // the collection is now 1 ele smaller
 10885  
 10886  
 10887      this.length--;
 10888      return this;
 10889    },
 10890    // remove single ele in place in calling collection
 10891    unmergeOne: function unmergeOne(ele) {
 10892      ele = ele[0];
 10893      var _p = this._private;
 10894      var id = ele._private.data.id;
 10895      var map = _p.map;
 10896      var entry = map.get(id);
 10897  
 10898      if (!entry) {
 10899        return this; // no need to remove
 10900      }
 10901  
 10902      var i = entry.index;
 10903      this.unmergeAt(i);
 10904      return this;
 10905    },
 10906    // remove eles in place on calling collection
 10907    unmerge: function unmerge(toRemove) {
 10908      var cy = this._private.cy;
 10909  
 10910      if (!toRemove) {
 10911        return this;
 10912      }
 10913  
 10914      if (toRemove && string(toRemove)) {
 10915        var selector = toRemove;
 10916        toRemove = cy.mutableElements().filter(selector);
 10917      }
 10918  
 10919      for (var i = 0; i < toRemove.length; i++) {
 10920        this.unmergeOne(toRemove[i]);
 10921      }
 10922  
 10923      return this; // chaining
 10924    },
 10925    unmergeBy: function unmergeBy(toRmFn) {
 10926      for (var i = this.length - 1; i >= 0; i--) {
 10927        var ele = this[i];
 10928  
 10929        if (toRmFn(ele)) {
 10930          this.unmergeAt(i);
 10931        }
 10932      }
 10933  
 10934      return this;
 10935    },
 10936    map: function map(mapFn, thisArg) {
 10937      var arr = [];
 10938      var eles = this;
 10939  
 10940      for (var i = 0; i < eles.length; i++) {
 10941        var ele = eles[i];
 10942        var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
 10943        arr.push(ret);
 10944      }
 10945  
 10946      return arr;
 10947    },
 10948    reduce: function reduce(fn, initialValue) {
 10949      var val = initialValue;
 10950      var eles = this;
 10951  
 10952      for (var i = 0; i < eles.length; i++) {
 10953        val = fn(val, eles[i], i, eles);
 10954      }
 10955  
 10956      return val;
 10957    },
 10958    max: function max(valFn, thisArg) {
 10959      var max = -Infinity;
 10960      var maxEle;
 10961      var eles = this;
 10962  
 10963      for (var i = 0; i < eles.length; i++) {
 10964        var ele = eles[i];
 10965        var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
 10966  
 10967        if (val > max) {
 10968          max = val;
 10969          maxEle = ele;
 10970        }
 10971      }
 10972  
 10973      return {
 10974        value: max,
 10975        ele: maxEle
 10976      };
 10977    },
 10978    min: function min(valFn, thisArg) {
 10979      var min = Infinity;
 10980      var minEle;
 10981      var eles = this;
 10982  
 10983      for (var i = 0; i < eles.length; i++) {
 10984        var ele = eles[i];
 10985        var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
 10986  
 10987        if (val < min) {
 10988          min = val;
 10989          minEle = ele;
 10990        }
 10991      }
 10992  
 10993      return {
 10994        value: min,
 10995        ele: minEle
 10996      };
 10997    }
 10998  }; // aliases
 10999  
 11000  var fn$5 = elesfn$n;
 11001  fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
 11002  fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
 11003  fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
 11004  fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
 11005  fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
 11006  fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
 11007  
 11008  var elesfn$o = {
 11009    isNode: function isNode() {
 11010      return this.group() === 'nodes';
 11011    },
 11012    isEdge: function isEdge() {
 11013      return this.group() === 'edges';
 11014    },
 11015    isLoop: function isLoop() {
 11016      return this.isEdge() && this.source()[0] === this.target()[0];
 11017    },
 11018    isSimple: function isSimple() {
 11019      return this.isEdge() && this.source()[0] !== this.target()[0];
 11020    },
 11021    group: function group() {
 11022      var ele = this[0];
 11023  
 11024      if (ele) {
 11025        return ele._private.group;
 11026      }
 11027    }
 11028  };
 11029  
 11030  /**
 11031   *  Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
 11032   *  and z-index (low to high).  These styles affect how this applies:
 11033   *
 11034   *  z-compound-depth: May be `bottom | orphan | auto | top`.  The first drawn is `bottom`, then `orphan` which is the
 11035   *      same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
 11036   *      root to leaves of the compound graph.  The last drawn is `top`.
 11037   *  z-index-compare: May be `auto | manual`.  The default value is `auto` which always draws edges under nodes.
 11038   *      `manual` ignores this convention and draws based on the `z-index` value setting.
 11039   *  z-index: An integer value that affects the relative draw order of elements.  In general, an element with a higher
 11040   *      `z-index` will be drawn on top of an element with a lower `z-index`.
 11041   */
 11042  
 11043  var zIndexSort = function zIndexSort(a, b) {
 11044    var cy = a.cy();
 11045    var hasCompoundNodes = cy.hasCompoundNodes();
 11046  
 11047    function getDepth(ele) {
 11048      var style = ele.pstyle('z-compound-depth');
 11049  
 11050      if (style.value === 'auto') {
 11051        return hasCompoundNodes ? ele.zDepth() : 0;
 11052      } else if (style.value === 'bottom') {
 11053        return -1;
 11054      } else if (style.value === 'top') {
 11055        return MAX_INT;
 11056      } // 'orphan'
 11057  
 11058  
 11059      return 0;
 11060    }
 11061  
 11062    var depthDiff = getDepth(a) - getDepth(b);
 11063  
 11064    if (depthDiff !== 0) {
 11065      return depthDiff;
 11066    }
 11067  
 11068    function getEleDepth(ele) {
 11069      var style = ele.pstyle('z-index-compare');
 11070  
 11071      if (style.value === 'auto') {
 11072        return ele.isNode() ? 1 : 0;
 11073      } // 'manual'
 11074  
 11075  
 11076      return 0;
 11077    }
 11078  
 11079    var eleDiff = getEleDepth(a) - getEleDepth(b);
 11080  
 11081    if (eleDiff !== 0) {
 11082      return eleDiff;
 11083    }
 11084  
 11085    var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
 11086  
 11087    if (zDiff !== 0) {
 11088      return zDiff;
 11089    } // compare indices in the core (order added to graph w/ last on top)
 11090  
 11091  
 11092    return a.poolIndex() - b.poolIndex();
 11093  };
 11094  
 11095  var elesfn$p = {
 11096    forEach: function forEach(fn$1, thisArg) {
 11097      if (fn(fn$1)) {
 11098        var N = this.length;
 11099  
 11100        for (var i = 0; i < N; i++) {
 11101          var ele = this[i];
 11102          var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
 11103  
 11104          if (ret === false) {
 11105            break;
 11106          } // exit each early on return false
 11107  
 11108        }
 11109      }
 11110  
 11111      return this;
 11112    },
 11113    toArray: function toArray() {
 11114      var array = [];
 11115  
 11116      for (var i = 0; i < this.length; i++) {
 11117        array.push(this[i]);
 11118      }
 11119  
 11120      return array;
 11121    },
 11122    slice: function slice(start, end) {
 11123      var array = [];
 11124      var thisSize = this.length;
 11125  
 11126      if (end == null) {
 11127        end = thisSize;
 11128      }
 11129  
 11130      if (start == null) {
 11131        start = 0;
 11132      }
 11133  
 11134      if (start < 0) {
 11135        start = thisSize + start;
 11136      }
 11137  
 11138      if (end < 0) {
 11139        end = thisSize + end;
 11140      }
 11141  
 11142      for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
 11143        array.push(this[i]);
 11144      }
 11145  
 11146      return this.spawn(array);
 11147    },
 11148    size: function size() {
 11149      return this.length;
 11150    },
 11151    eq: function eq(i) {
 11152      return this[i] || this.spawn();
 11153    },
 11154    first: function first() {
 11155      return this[0] || this.spawn();
 11156    },
 11157    last: function last() {
 11158      return this[this.length - 1] || this.spawn();
 11159    },
 11160    empty: function empty() {
 11161      return this.length === 0;
 11162    },
 11163    nonempty: function nonempty() {
 11164      return !this.empty();
 11165    },
 11166    sort: function sort(sortFn) {
 11167      if (!fn(sortFn)) {
 11168        return this;
 11169      }
 11170  
 11171      var sorted = this.toArray().sort(sortFn);
 11172      return this.spawn(sorted);
 11173    },
 11174    sortByZIndex: function sortByZIndex() {
 11175      return this.sort(zIndexSort);
 11176    },
 11177    zDepth: function zDepth() {
 11178      var ele = this[0];
 11179  
 11180      if (!ele) {
 11181        return undefined;
 11182      } // let cy = ele.cy();
 11183  
 11184  
 11185      var _p = ele._private;
 11186      var group = _p.group;
 11187  
 11188      if (group === 'nodes') {
 11189        var depth = _p.data.parent ? ele.parents().size() : 0;
 11190  
 11191        if (!ele.isParent()) {
 11192          return MAX_INT - 1; // childless nodes always on top
 11193        }
 11194  
 11195        return depth;
 11196      } else {
 11197        var src = _p.source;
 11198        var tgt = _p.target;
 11199        var srcDepth = src.zDepth();
 11200        var tgtDepth = tgt.zDepth();
 11201        return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
 11202      }
 11203    }
 11204  };
 11205  elesfn$p.each = elesfn$p.forEach;
 11206  
 11207  var defineSymbolIterator = function defineSymbolIterator() {
 11208    var typeofUndef =  "undefined" ;
 11209    var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
 11210  
 11211    if (isIteratorSupported) {
 11212      elesfn$p[Symbol.iterator] = function () {
 11213        var _this = this;
 11214  
 11215        // eslint-disable-line no-undef
 11216        var entry = {
 11217          value: undefined,
 11218          done: false
 11219        };
 11220        var i = 0;
 11221        var length = this.length;
 11222        return _defineProperty({
 11223          next: function next() {
 11224            if (i < length) {
 11225              entry.value = _this[i++];
 11226            } else {
 11227              entry.value = undefined;
 11228              entry.done = true;
 11229            }
 11230  
 11231            return entry;
 11232          }
 11233        }, Symbol.iterator, function () {
 11234          // eslint-disable-line no-undef
 11235          return this;
 11236        });
 11237      };
 11238    }
 11239  };
 11240  
 11241  defineSymbolIterator();
 11242  
 11243  var getLayoutDimensionOptions = defaults({
 11244    nodeDimensionsIncludeLabels: false
 11245  });
 11246  var elesfn$q = {
 11247    // Calculates and returns node dimensions { x, y } based on options given
 11248    layoutDimensions: function layoutDimensions(options) {
 11249      options = getLayoutDimensionOptions(options);
 11250      var dims;
 11251  
 11252      if (!this.takesUpSpace()) {
 11253        dims = {
 11254          w: 0,
 11255          h: 0
 11256        };
 11257      } else if (options.nodeDimensionsIncludeLabels) {
 11258        var bbDim = this.boundingBox();
 11259        dims = {
 11260          w: bbDim.w,
 11261          h: bbDim.h
 11262        };
 11263      } else {
 11264        dims = {
 11265          w: this.outerWidth(),
 11266          h: this.outerHeight()
 11267        };
 11268      } // sanitise the dimensions for external layouts (avoid division by zero)
 11269  
 11270  
 11271      if (dims.w === 0 || dims.h === 0) {
 11272        dims.w = dims.h = 1;
 11273      }
 11274  
 11275      return dims;
 11276    },
 11277    // using standard layout options, apply position function (w/ or w/o animation)
 11278    layoutPositions: function layoutPositions(layout, options, fn) {
 11279      var nodes = this.nodes();
 11280      var cy = this.cy();
 11281      var layoutEles = options.eles; // nodes & edges
 11282  
 11283      var getMemoizeKey = function getMemoizeKey(node) {
 11284        return node.id();
 11285      };
 11286  
 11287      var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
 11288  
 11289      layout.emit({
 11290        type: 'layoutstart',
 11291        layout: layout
 11292      });
 11293      layout.animations = [];
 11294  
 11295      var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
 11296        var center = {
 11297          x: nodesBb.x1 + nodesBb.w / 2,
 11298          y: nodesBb.y1 + nodesBb.h / 2
 11299        };
 11300        var spacingVector = {
 11301          // scale from center of bounding box (not necessarily 0,0)
 11302          x: (pos.x - center.x) * spacing,
 11303          y: (pos.y - center.y) * spacing
 11304        };
 11305        return {
 11306          x: center.x + spacingVector.x,
 11307          y: center.y + spacingVector.y
 11308        };
 11309      };
 11310  
 11311      var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
 11312  
 11313      var spacingBb = function spacingBb() {
 11314        if (!useSpacingFactor) {
 11315          return null;
 11316        }
 11317  
 11318        var bb = makeBoundingBox();
 11319  
 11320        for (var i = 0; i < nodes.length; i++) {
 11321          var node = nodes[i];
 11322          var pos = fnMem(node, i);
 11323          expandBoundingBoxByPoint(bb, pos.x, pos.y);
 11324        }
 11325  
 11326        return bb;
 11327      };
 11328  
 11329      var bb = spacingBb();
 11330      var getFinalPos = memoize(function (node, i) {
 11331        var newPos = fnMem(node, i);
 11332  
 11333        if (useSpacingFactor) {
 11334          var spacing = Math.abs(options.spacingFactor);
 11335          newPos = calculateSpacing(spacing, bb, newPos);
 11336        }
 11337  
 11338        if (options.transform != null) {
 11339          newPos = options.transform(node, newPos);
 11340        }
 11341  
 11342        return newPos;
 11343      }, getMemoizeKey);
 11344  
 11345      if (options.animate) {
 11346        for (var i = 0; i < nodes.length; i++) {
 11347          var node = nodes[i];
 11348          var newPos = getFinalPos(node, i);
 11349          var animateNode = options.animateFilter == null || options.animateFilter(node, i);
 11350  
 11351          if (animateNode) {
 11352            var ani = node.animation({
 11353              position: newPos,
 11354              duration: options.animationDuration,
 11355              easing: options.animationEasing
 11356            });
 11357            layout.animations.push(ani);
 11358          } else {
 11359            node.position(newPos);
 11360          }
 11361        }
 11362  
 11363        if (options.fit) {
 11364          var fitAni = cy.animation({
 11365            fit: {
 11366              boundingBox: layoutEles.boundingBoxAt(getFinalPos),
 11367              padding: options.padding
 11368            },
 11369            duration: options.animationDuration,
 11370            easing: options.animationEasing
 11371          });
 11372          layout.animations.push(fitAni);
 11373        } else if (options.zoom !== undefined && options.pan !== undefined) {
 11374          var zoomPanAni = cy.animation({
 11375            zoom: options.zoom,
 11376            pan: options.pan,
 11377            duration: options.animationDuration,
 11378            easing: options.animationEasing
 11379          });
 11380          layout.animations.push(zoomPanAni);
 11381        }
 11382  
 11383        layout.animations.forEach(function (ani) {
 11384          return ani.play();
 11385        });
 11386        layout.one('layoutready', options.ready);
 11387        layout.emit({
 11388          type: 'layoutready',
 11389          layout: layout
 11390        });
 11391        Promise$1.all(layout.animations.map(function (ani) {
 11392          return ani.promise();
 11393        })).then(function () {
 11394          layout.one('layoutstop', options.stop);
 11395          layout.emit({
 11396            type: 'layoutstop',
 11397            layout: layout
 11398          });
 11399        });
 11400      } else {
 11401        nodes.positions(getFinalPos);
 11402  
 11403        if (options.fit) {
 11404          cy.fit(options.eles, options.padding);
 11405        }
 11406  
 11407        if (options.zoom != null) {
 11408          cy.zoom(options.zoom);
 11409        }
 11410  
 11411        if (options.pan) {
 11412          cy.pan(options.pan);
 11413        }
 11414  
 11415        layout.one('layoutready', options.ready);
 11416        layout.emit({
 11417          type: 'layoutready',
 11418          layout: layout
 11419        });
 11420        layout.one('layoutstop', options.stop);
 11421        layout.emit({
 11422          type: 'layoutstop',
 11423          layout: layout
 11424        });
 11425      }
 11426  
 11427      return this; // chaining
 11428    },
 11429    layout: function layout(options) {
 11430      var cy = this.cy();
 11431      return cy.makeLayout(extend({}, options, {
 11432        eles: this
 11433      }));
 11434    }
 11435  }; // aliases:
 11436  
 11437  elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
 11438  
 11439  function styleCache(key, fn, ele) {
 11440    var _p = ele._private;
 11441    var cache = _p.styleCache = _p.styleCache || [];
 11442    var val;
 11443  
 11444    if ((val = cache[key]) != null) {
 11445      return val;
 11446    } else {
 11447      val = cache[key] = fn(ele);
 11448      return val;
 11449    }
 11450  }
 11451  
 11452  function cacheStyleFunction(key, fn) {
 11453    key = hashString(key);
 11454    return function cachedStyleFunction(ele) {
 11455      return styleCache(key, fn, ele);
 11456    };
 11457  }
 11458  
 11459  function cachePrototypeStyleFunction(key, fn) {
 11460    key = hashString(key);
 11461  
 11462    var selfFn = function selfFn(ele) {
 11463      return fn.call(ele);
 11464    };
 11465  
 11466    return function cachedPrototypeStyleFunction() {
 11467      var ele = this[0];
 11468  
 11469      if (ele) {
 11470        return styleCache(key, selfFn, ele);
 11471      }
 11472    };
 11473  }
 11474  
 11475  var elesfn$r = {
 11476    recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
 11477      var cy = this.cy();
 11478      var renderer = cy.renderer();
 11479      var styleEnabled = cy.styleEnabled();
 11480  
 11481      if (renderer && styleEnabled) {
 11482        renderer.recalculateRenderedStyle(this, useCache);
 11483      }
 11484  
 11485      return this;
 11486    },
 11487    dirtyStyleCache: function dirtyStyleCache() {
 11488      var cy = this.cy();
 11489  
 11490      var dirty = function dirty(ele) {
 11491        return ele._private.styleCache = null;
 11492      };
 11493  
 11494      if (cy.hasCompoundNodes()) {
 11495        var eles;
 11496        eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
 11497        eles.merge(eles.connectedEdges());
 11498        eles.forEach(dirty);
 11499      } else {
 11500        this.forEach(function (ele) {
 11501          dirty(ele);
 11502          ele.connectedEdges().forEach(dirty);
 11503        });
 11504      }
 11505  
 11506      return this;
 11507    },
 11508    // fully updates (recalculates) the style for the elements
 11509    updateStyle: function updateStyle(notifyRenderer) {
 11510      var cy = this._private.cy;
 11511  
 11512      if (!cy.styleEnabled()) {
 11513        return this;
 11514      }
 11515  
 11516      if (cy.batching()) {
 11517        var bEles = cy._private.batchStyleEles;
 11518        bEles.merge(this);
 11519        return this; // chaining and exit early when batching
 11520      }
 11521  
 11522      var hasCompounds = cy.hasCompoundNodes();
 11523      var style = cy.style();
 11524      var updatedEles = this;
 11525      notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
 11526  
 11527      if (hasCompounds) {
 11528        // then add everything up and down for compound selector checks
 11529        updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
 11530      }
 11531  
 11532      var changedEles = style.apply(updatedEles);
 11533  
 11534      if (notifyRenderer) {
 11535        changedEles.emitAndNotify('style'); // let renderer know we changed style
 11536      } else {
 11537        changedEles.emit('style'); // just fire the event
 11538      }
 11539  
 11540      return this; // chaining
 11541    },
 11542    // get the internal parsed style object for the specified property
 11543    parsedStyle: function parsedStyle(property) {
 11544      var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
 11545      var ele = this[0];
 11546      var cy = ele.cy();
 11547  
 11548      if (!cy.styleEnabled()) {
 11549        return;
 11550      }
 11551  
 11552      if (ele) {
 11553        var overriddenStyle = ele._private.style[property];
 11554  
 11555        if (overriddenStyle != null) {
 11556          return overriddenStyle;
 11557        } else if (includeNonDefault) {
 11558          return cy.style().getDefaultProperty(property);
 11559        } else {
 11560          return null;
 11561        }
 11562      }
 11563    },
 11564    numericStyle: function numericStyle(property) {
 11565      var ele = this[0];
 11566  
 11567      if (!ele.cy().styleEnabled()) {
 11568        return;
 11569      }
 11570  
 11571      if (ele) {
 11572        var pstyle = ele.pstyle(property);
 11573        return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
 11574      }
 11575    },
 11576    numericStyleUnits: function numericStyleUnits(property) {
 11577      var ele = this[0];
 11578  
 11579      if (!ele.cy().styleEnabled()) {
 11580        return;
 11581      }
 11582  
 11583      if (ele) {
 11584        return ele.pstyle(property).units;
 11585      }
 11586    },
 11587    // get the specified css property as a rendered value (i.e. on-screen value)
 11588    // or get the whole rendered style if no property specified (NB doesn't allow setting)
 11589    renderedStyle: function renderedStyle(property) {
 11590      var cy = this.cy();
 11591  
 11592      if (!cy.styleEnabled()) {
 11593        return this;
 11594      }
 11595  
 11596      var ele = this[0];
 11597  
 11598      if (ele) {
 11599        return cy.style().getRenderedStyle(ele, property);
 11600      }
 11601    },
 11602    // read the calculated css style of the element or override the style (via a bypass)
 11603    style: function style(name, value) {
 11604      var cy = this.cy();
 11605  
 11606      if (!cy.styleEnabled()) {
 11607        return this;
 11608      }
 11609  
 11610      var updateTransitions = false;
 11611      var style = cy.style();
 11612  
 11613      if (plainObject(name)) {
 11614        // then extend the bypass
 11615        var props = name;
 11616        style.applyBypass(this, props, updateTransitions);
 11617        this.emitAndNotify('style'); // let the renderer know we've updated style
 11618      } else if (string(name)) {
 11619        if (value === undefined) {
 11620          // then get the property from the style
 11621          var ele = this[0];
 11622  
 11623          if (ele) {
 11624            return style.getStylePropertyValue(ele, name);
 11625          } else {
 11626            // empty collection => can't get any value
 11627            return;
 11628          }
 11629        } else {
 11630          // then set the bypass with the property value
 11631          style.applyBypass(this, name, value, updateTransitions);
 11632          this.emitAndNotify('style'); // let the renderer know we've updated style
 11633        }
 11634      } else if (name === undefined) {
 11635        var _ele = this[0];
 11636  
 11637        if (_ele) {
 11638          return style.getRawStyle(_ele);
 11639        } else {
 11640          // empty collection => can't get any value
 11641          return;
 11642        }
 11643      }
 11644  
 11645      return this; // chaining
 11646    },
 11647    removeStyle: function removeStyle(names) {
 11648      var cy = this.cy();
 11649  
 11650      if (!cy.styleEnabled()) {
 11651        return this;
 11652      }
 11653  
 11654      var updateTransitions = false;
 11655      var style = cy.style();
 11656      var eles = this;
 11657  
 11658      if (names === undefined) {
 11659        for (var i = 0; i < eles.length; i++) {
 11660          var ele = eles[i];
 11661          style.removeAllBypasses(ele, updateTransitions);
 11662        }
 11663      } else {
 11664        names = names.split(/\s+/);
 11665  
 11666        for (var _i = 0; _i < eles.length; _i++) {
 11667          var _ele2 = eles[_i];
 11668          style.removeBypasses(_ele2, names, updateTransitions);
 11669        }
 11670      }
 11671  
 11672      this.emitAndNotify('style'); // let the renderer know we've updated style
 11673  
 11674      return this; // chaining
 11675    },
 11676    show: function show() {
 11677      this.css('display', 'element');
 11678      return this; // chaining
 11679    },
 11680    hide: function hide() {
 11681      this.css('display', 'none');
 11682      return this; // chaining
 11683    },
 11684    effectiveOpacity: function effectiveOpacity() {
 11685      var cy = this.cy();
 11686  
 11687      if (!cy.styleEnabled()) {
 11688        return 1;
 11689      }
 11690  
 11691      var hasCompoundNodes = cy.hasCompoundNodes();
 11692      var ele = this[0];
 11693  
 11694      if (ele) {
 11695        var _p = ele._private;
 11696        var parentOpacity = ele.pstyle('opacity').value;
 11697  
 11698        if (!hasCompoundNodes) {
 11699          return parentOpacity;
 11700        }
 11701  
 11702        var parents = !_p.data.parent ? null : ele.parents();
 11703  
 11704        if (parents) {
 11705          for (var i = 0; i < parents.length; i++) {
 11706            var parent = parents[i];
 11707            var opacity = parent.pstyle('opacity').value;
 11708            parentOpacity = opacity * parentOpacity;
 11709          }
 11710        }
 11711  
 11712        return parentOpacity;
 11713      }
 11714    },
 11715    transparent: function transparent() {
 11716      var cy = this.cy();
 11717  
 11718      if (!cy.styleEnabled()) {
 11719        return false;
 11720      }
 11721  
 11722      var ele = this[0];
 11723      var hasCompoundNodes = ele.cy().hasCompoundNodes();
 11724  
 11725      if (ele) {
 11726        if (!hasCompoundNodes) {
 11727          return ele.pstyle('opacity').value === 0;
 11728        } else {
 11729          return ele.effectiveOpacity() === 0;
 11730        }
 11731      }
 11732    },
 11733    backgrounding: function backgrounding() {
 11734      var cy = this.cy();
 11735  
 11736      if (!cy.styleEnabled()) {
 11737        return false;
 11738      }
 11739  
 11740      var ele = this[0];
 11741      return ele._private.backgrounding ? true : false;
 11742    }
 11743  };
 11744  
 11745  function checkCompound(ele, parentOk) {
 11746    var _p = ele._private;
 11747    var parents = _p.data.parent ? ele.parents() : null;
 11748  
 11749    if (parents) {
 11750      for (var i = 0; i < parents.length; i++) {
 11751        var parent = parents[i];
 11752  
 11753        if (!parentOk(parent)) {
 11754          return false;
 11755        }
 11756      }
 11757    }
 11758  
 11759    return true;
 11760  }
 11761  
 11762  function defineDerivedStateFunction(specs) {
 11763    var ok = specs.ok;
 11764    var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
 11765    var parentOk = specs.parentOk || specs.ok;
 11766    return function () {
 11767      var cy = this.cy();
 11768  
 11769      if (!cy.styleEnabled()) {
 11770        return true;
 11771      }
 11772  
 11773      var ele = this[0];
 11774      var hasCompoundNodes = cy.hasCompoundNodes();
 11775  
 11776      if (ele) {
 11777        var _p = ele._private;
 11778  
 11779        if (!ok(ele)) {
 11780          return false;
 11781        }
 11782  
 11783        if (ele.isNode()) {
 11784          return !hasCompoundNodes || checkCompound(ele, parentOk);
 11785        } else {
 11786          var src = _p.source;
 11787          var tgt = _p.target;
 11788          return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
 11789        }
 11790      }
 11791    };
 11792  }
 11793  
 11794  var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
 11795    return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
 11796  });
 11797  elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
 11798    ok: eleTakesUpSpace
 11799  }));
 11800  var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
 11801    return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
 11802  });
 11803  var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
 11804    return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
 11805  });
 11806  elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
 11807    ok: eleInteractive,
 11808    parentOk: parentInteractive,
 11809    edgeOkViaNode: eleTakesUpSpace
 11810  }));
 11811  
 11812  elesfn$r.noninteractive = function () {
 11813    var ele = this[0];
 11814  
 11815    if (ele) {
 11816      return !ele.interactive();
 11817    }
 11818  };
 11819  
 11820  var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
 11821    return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
 11822  });
 11823  var edgeVisibleViaNode = eleTakesUpSpace;
 11824  elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
 11825    ok: eleVisible,
 11826    edgeOkViaNode: edgeVisibleViaNode
 11827  }));
 11828  
 11829  elesfn$r.hidden = function () {
 11830    var ele = this[0];
 11831  
 11832    if (ele) {
 11833      return !ele.visible();
 11834    }
 11835  };
 11836  
 11837  elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
 11838    if (!this.cy().styleEnabled()) {
 11839      return false;
 11840    }
 11841  
 11842    return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
 11843  });
 11844  elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
 11845  elesfn$r.renderedCss = elesfn$r.renderedStyle;
 11846  elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
 11847  elesfn$r.pstyle = elesfn$r.parsedStyle;
 11848  
 11849  var elesfn$s = {};
 11850  
 11851  function defineSwitchFunction(params) {
 11852    return function () {
 11853      var args = arguments;
 11854      var changedEles = []; // e.g. cy.nodes().select( data, handler )
 11855  
 11856      if (args.length === 2) {
 11857        var data = args[0];
 11858        var handler = args[1];
 11859        this.on(params.event, data, handler);
 11860      } // e.g. cy.nodes().select( handler )
 11861      else if (args.length === 1 && fn(args[0])) {
 11862          var _handler = args[0];
 11863          this.on(params.event, _handler);
 11864        } // e.g. cy.nodes().select()
 11865        // e.g. (private) cy.nodes().select(['tapselect'])
 11866        else if (args.length === 0 || args.length === 1 && array(args[0])) {
 11867            var addlEvents = args.length === 1 ? args[0] : null;
 11868  
 11869            for (var i = 0; i < this.length; i++) {
 11870              var ele = this[i];
 11871              var able = !params.ableField || ele._private[params.ableField];
 11872              var changed = ele._private[params.field] != params.value;
 11873  
 11874              if (params.overrideAble) {
 11875                var overrideAble = params.overrideAble(ele);
 11876  
 11877                if (overrideAble !== undefined) {
 11878                  able = overrideAble;
 11879  
 11880                  if (!overrideAble) {
 11881                    return this;
 11882                  } // to save cycles assume not able for all on override
 11883  
 11884                }
 11885              }
 11886  
 11887              if (able) {
 11888                ele._private[params.field] = params.value;
 11889  
 11890                if (changed) {
 11891                  changedEles.push(ele);
 11892                }
 11893              }
 11894            }
 11895  
 11896            var changedColl = this.spawn(changedEles);
 11897            changedColl.updateStyle(); // change of state => possible change of style
 11898  
 11899            changedColl.emit(params.event);
 11900  
 11901            if (addlEvents) {
 11902              changedColl.emit(addlEvents);
 11903            }
 11904          }
 11905  
 11906      return this;
 11907    };
 11908  }
 11909  
 11910  function defineSwitchSet(params) {
 11911    elesfn$s[params.field] = function () {
 11912      var ele = this[0];
 11913  
 11914      if (ele) {
 11915        if (params.overrideField) {
 11916          var val = params.overrideField(ele);
 11917  
 11918          if (val !== undefined) {
 11919            return val;
 11920          }
 11921        }
 11922  
 11923        return ele._private[params.field];
 11924      }
 11925    };
 11926  
 11927    elesfn$s[params.on] = defineSwitchFunction({
 11928      event: params.on,
 11929      field: params.field,
 11930      ableField: params.ableField,
 11931      overrideAble: params.overrideAble,
 11932      value: true
 11933    });
 11934    elesfn$s[params.off] = defineSwitchFunction({
 11935      event: params.off,
 11936      field: params.field,
 11937      ableField: params.ableField,
 11938      overrideAble: params.overrideAble,
 11939      value: false
 11940    });
 11941  }
 11942  
 11943  defineSwitchSet({
 11944    field: 'locked',
 11945    overrideField: function overrideField(ele) {
 11946      return ele.cy().autolock() ? true : undefined;
 11947    },
 11948    on: 'lock',
 11949    off: 'unlock'
 11950  });
 11951  defineSwitchSet({
 11952    field: 'grabbable',
 11953    overrideField: function overrideField(ele) {
 11954      return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
 11955    },
 11956    on: 'grabify',
 11957    off: 'ungrabify'
 11958  });
 11959  defineSwitchSet({
 11960    field: 'selected',
 11961    ableField: 'selectable',
 11962    overrideAble: function overrideAble(ele) {
 11963      return ele.cy().autounselectify() ? false : undefined;
 11964    },
 11965    on: 'select',
 11966    off: 'unselect'
 11967  });
 11968  defineSwitchSet({
 11969    field: 'selectable',
 11970    overrideField: function overrideField(ele) {
 11971      return ele.cy().autounselectify() ? false : undefined;
 11972    },
 11973    on: 'selectify',
 11974    off: 'unselectify'
 11975  });
 11976  elesfn$s.deselect = elesfn$s.unselect;
 11977  
 11978  elesfn$s.grabbed = function () {
 11979    var ele = this[0];
 11980  
 11981    if (ele) {
 11982      return ele._private.grabbed;
 11983    }
 11984  };
 11985  
 11986  defineSwitchSet({
 11987    field: 'active',
 11988    on: 'activate',
 11989    off: 'unactivate'
 11990  });
 11991  defineSwitchSet({
 11992    field: 'pannable',
 11993    on: 'panify',
 11994    off: 'unpanify'
 11995  });
 11996  
 11997  elesfn$s.inactive = function () {
 11998    var ele = this[0];
 11999  
 12000    if (ele) {
 12001      return !ele._private.active;
 12002    }
 12003  };
 12004  
 12005  var elesfn$t = {}; // DAG functions
 12006  ////////////////
 12007  
 12008  var defineDagExtremity = function defineDagExtremity(params) {
 12009    return function dagExtremityImpl(selector) {
 12010      var eles = this;
 12011      var ret = [];
 12012  
 12013      for (var i = 0; i < eles.length; i++) {
 12014        var ele = eles[i];
 12015  
 12016        if (!ele.isNode()) {
 12017          continue;
 12018        }
 12019  
 12020        var disqualified = false;
 12021        var edges = ele.connectedEdges();
 12022  
 12023        for (var j = 0; j < edges.length; j++) {
 12024          var edge = edges[j];
 12025          var src = edge.source();
 12026          var tgt = edge.target();
 12027  
 12028          if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
 12029            disqualified = true;
 12030            break;
 12031          }
 12032        }
 12033  
 12034        if (!disqualified) {
 12035          ret.push(ele);
 12036        }
 12037      }
 12038  
 12039      return this.spawn(ret, {
 12040        unique: true
 12041      }).filter(selector);
 12042    };
 12043  };
 12044  
 12045  var defineDagOneHop = function defineDagOneHop(params) {
 12046    return function (selector) {
 12047      var eles = this;
 12048      var oEles = [];
 12049  
 12050      for (var i = 0; i < eles.length; i++) {
 12051        var ele = eles[i];
 12052  
 12053        if (!ele.isNode()) {
 12054          continue;
 12055        }
 12056  
 12057        var edges = ele.connectedEdges();
 12058  
 12059        for (var j = 0; j < edges.length; j++) {
 12060          var edge = edges[j];
 12061          var src = edge.source();
 12062          var tgt = edge.target();
 12063  
 12064          if (params.outgoing && src === ele) {
 12065            oEles.push(edge);
 12066            oEles.push(tgt);
 12067          } else if (params.incoming && tgt === ele) {
 12068            oEles.push(edge);
 12069            oEles.push(src);
 12070          }
 12071        }
 12072      }
 12073  
 12074      return this.spawn(oEles, {
 12075        unique: true
 12076      }).filter(selector);
 12077    };
 12078  };
 12079  
 12080  var defineDagAllHops = function defineDagAllHops(params) {
 12081    return function (selector) {
 12082      var eles = this;
 12083      var sEles = [];
 12084      var sElesIds = {};
 12085  
 12086      for (;;) {
 12087        var next = params.outgoing ? eles.outgoers() : eles.incomers();
 12088  
 12089        if (next.length === 0) {
 12090          break;
 12091        } // done if none left
 12092  
 12093  
 12094        var newNext = false;
 12095  
 12096        for (var i = 0; i < next.length; i++) {
 12097          var n = next[i];
 12098          var nid = n.id();
 12099  
 12100          if (!sElesIds[nid]) {
 12101            sElesIds[nid] = true;
 12102            sEles.push(n);
 12103            newNext = true;
 12104          }
 12105        }
 12106  
 12107        if (!newNext) {
 12108          break;
 12109        } // done if touched all outgoers already
 12110  
 12111  
 12112        eles = next;
 12113      }
 12114  
 12115      return this.spawn(sEles, {
 12116        unique: true
 12117      }).filter(selector);
 12118    };
 12119  };
 12120  
 12121  elesfn$t.clearTraversalCache = function () {
 12122    for (var i = 0; i < this.length; i++) {
 12123      this[i]._private.traversalCache = null;
 12124    }
 12125  };
 12126  
 12127  extend(elesfn$t, {
 12128    // get the root nodes in the DAG
 12129    roots: defineDagExtremity({
 12130      noIncomingEdges: true
 12131    }),
 12132    // get the leaf nodes in the DAG
 12133    leaves: defineDagExtremity({
 12134      noOutgoingEdges: true
 12135    }),
 12136    // normally called children in graph theory
 12137    // these nodes =edges=> outgoing nodes
 12138    outgoers: cache(defineDagOneHop({
 12139      outgoing: true
 12140    }), 'outgoers'),
 12141    // aka DAG descendants
 12142    successors: defineDagAllHops({
 12143      outgoing: true
 12144    }),
 12145    // normally called parents in graph theory
 12146    // these nodes <=edges= incoming nodes
 12147    incomers: cache(defineDagOneHop({
 12148      incoming: true
 12149    }), 'incomers'),
 12150    // aka DAG ancestors
 12151    predecessors: defineDagAllHops({
 12152      incoming: true
 12153    })
 12154  }); // Neighbourhood functions
 12155  //////////////////////////
 12156  
 12157  extend(elesfn$t, {
 12158    neighborhood: cache(function (selector) {
 12159      var elements = [];
 12160      var nodes = this.nodes();
 12161  
 12162      for (var i = 0; i < nodes.length; i++) {
 12163        // for all nodes
 12164        var node = nodes[i];
 12165        var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
 12166  
 12167        for (var j = 0; j < connectedEdges.length; j++) {
 12168          var edge = connectedEdges[j];
 12169          var src = edge.source();
 12170          var tgt = edge.target();
 12171          var otherNode = node === src ? tgt : src; // need check in case of loop
 12172  
 12173          if (otherNode.length > 0) {
 12174            elements.push(otherNode[0]); // add node 1 hop away
 12175          } // add connected edge
 12176  
 12177  
 12178          elements.push(edge[0]);
 12179        }
 12180      }
 12181  
 12182      return this.spawn(elements, {
 12183        unique: true
 12184      }).filter(selector);
 12185    }, 'neighborhood'),
 12186    closedNeighborhood: function closedNeighborhood(selector) {
 12187      return this.neighborhood().add(this).filter(selector);
 12188    },
 12189    openNeighborhood: function openNeighborhood(selector) {
 12190      return this.neighborhood(selector);
 12191    }
 12192  }); // aliases
 12193  
 12194  elesfn$t.neighbourhood = elesfn$t.neighborhood;
 12195  elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
 12196  elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
 12197  /////////////////
 12198  
 12199  extend(elesfn$t, {
 12200    source: cache(function sourceImpl(selector) {
 12201      var ele = this[0];
 12202      var src;
 12203  
 12204      if (ele) {
 12205        src = ele._private.source || ele.cy().collection();
 12206      }
 12207  
 12208      return src && selector ? src.filter(selector) : src;
 12209    }, 'source'),
 12210    target: cache(function targetImpl(selector) {
 12211      var ele = this[0];
 12212      var tgt;
 12213  
 12214      if (ele) {
 12215        tgt = ele._private.target || ele.cy().collection();
 12216      }
 12217  
 12218      return tgt && selector ? tgt.filter(selector) : tgt;
 12219    }, 'target'),
 12220    sources: defineSourceFunction({
 12221      attr: 'source'
 12222    }),
 12223    targets: defineSourceFunction({
 12224      attr: 'target'
 12225    })
 12226  });
 12227  
 12228  function defineSourceFunction(params) {
 12229    return function sourceImpl(selector) {
 12230      var sources = [];
 12231  
 12232      for (var i = 0; i < this.length; i++) {
 12233        var ele = this[i];
 12234        var src = ele._private[params.attr];
 12235  
 12236        if (src) {
 12237          sources.push(src);
 12238        }
 12239      }
 12240  
 12241      return this.spawn(sources, {
 12242        unique: true
 12243      }).filter(selector);
 12244    };
 12245  }
 12246  
 12247  extend(elesfn$t, {
 12248    edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
 12249    edgesTo: cache(defineEdgesWithFunction({
 12250      thisIsSrc: true
 12251    }), 'edgesTo')
 12252  });
 12253  
 12254  function defineEdgesWithFunction(params) {
 12255    return function edgesWithImpl(otherNodes) {
 12256      var elements = [];
 12257      var cy = this._private.cy;
 12258      var p = params || {}; // get elements if a selector is specified
 12259  
 12260      if (string(otherNodes)) {
 12261        otherNodes = cy.$(otherNodes);
 12262      }
 12263  
 12264      for (var h = 0; h < otherNodes.length; h++) {
 12265        var edges = otherNodes[h]._private.edges;
 12266  
 12267        for (var i = 0; i < edges.length; i++) {
 12268          var edge = edges[i];
 12269          var edgeData = edge._private.data;
 12270          var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
 12271          var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
 12272          var edgeConnectsThisAndOther = thisToOther || otherToThis;
 12273  
 12274          if (!edgeConnectsThisAndOther) {
 12275            continue;
 12276          }
 12277  
 12278          if (p.thisIsSrc || p.thisIsTgt) {
 12279            if (p.thisIsSrc && !thisToOther) {
 12280              continue;
 12281            }
 12282  
 12283            if (p.thisIsTgt && !otherToThis) {
 12284              continue;
 12285            }
 12286          }
 12287  
 12288          elements.push(edge);
 12289        }
 12290      }
 12291  
 12292      return this.spawn(elements, {
 12293        unique: true
 12294      });
 12295    };
 12296  }
 12297  
 12298  extend(elesfn$t, {
 12299    connectedEdges: cache(function (selector) {
 12300      var retEles = [];
 12301      var eles = this;
 12302  
 12303      for (var i = 0; i < eles.length; i++) {
 12304        var node = eles[i];
 12305  
 12306        if (!node.isNode()) {
 12307          continue;
 12308        }
 12309  
 12310        var edges = node._private.edges;
 12311  
 12312        for (var j = 0; j < edges.length; j++) {
 12313          var edge = edges[j];
 12314          retEles.push(edge);
 12315        }
 12316      }
 12317  
 12318      return this.spawn(retEles, {
 12319        unique: true
 12320      }).filter(selector);
 12321    }, 'connectedEdges'),
 12322    connectedNodes: cache(function (selector) {
 12323      var retEles = [];
 12324      var eles = this;
 12325  
 12326      for (var i = 0; i < eles.length; i++) {
 12327        var edge = eles[i];
 12328  
 12329        if (!edge.isEdge()) {
 12330          continue;
 12331        }
 12332  
 12333        retEles.push(edge.source()[0]);
 12334        retEles.push(edge.target()[0]);
 12335      }
 12336  
 12337      return this.spawn(retEles, {
 12338        unique: true
 12339      }).filter(selector);
 12340    }, 'connectedNodes'),
 12341    parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
 12342    codirectedEdges: cache(defineParallelEdgesFunction({
 12343      codirected: true
 12344    }), 'codirectedEdges')
 12345  });
 12346  
 12347  function defineParallelEdgesFunction(params) {
 12348    var defaults = {
 12349      codirected: false
 12350    };
 12351    params = extend({}, defaults, params);
 12352    return function parallelEdgesImpl(selector) {
 12353      // micro-optimised for renderer
 12354      var elements = [];
 12355      var edges = this.edges();
 12356      var p = params; // look at all the edges in the collection
 12357  
 12358      for (var i = 0; i < edges.length; i++) {
 12359        var edge1 = edges[i];
 12360        var edge1_p = edge1._private;
 12361        var src1 = edge1_p.source;
 12362        var srcid1 = src1._private.data.id;
 12363        var tgtid1 = edge1_p.data.target;
 12364        var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
 12365  
 12366        for (var j = 0; j < srcEdges1.length; j++) {
 12367          var edge2 = srcEdges1[j];
 12368          var edge2data = edge2._private.data;
 12369          var tgtid2 = edge2data.target;
 12370          var srcid2 = edge2data.source;
 12371          var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
 12372          var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
 12373  
 12374          if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
 12375            elements.push(edge2);
 12376          }
 12377        }
 12378      }
 12379  
 12380      return this.spawn(elements, {
 12381        unique: true
 12382      }).filter(selector);
 12383    };
 12384  } // Misc functions
 12385  /////////////////
 12386  
 12387  
 12388  extend(elesfn$t, {
 12389    components: function components(root) {
 12390      var self = this;
 12391      var cy = self.cy();
 12392      var visited = cy.collection();
 12393      var unvisited = root == null ? self.nodes() : root.nodes();
 12394      var components = [];
 12395  
 12396      if (root != null && unvisited.empty()) {
 12397        // root may contain only edges
 12398        unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
 12399      }
 12400  
 12401      var visitInComponent = function visitInComponent(node, component) {
 12402        visited.merge(node);
 12403        unvisited.unmerge(node);
 12404        component.merge(node);
 12405      };
 12406  
 12407      if (unvisited.empty()) {
 12408        return self.spawn();
 12409      }
 12410  
 12411      var _loop = function _loop() {
 12412        // each iteration yields a component
 12413        var cmpt = cy.collection();
 12414        components.push(cmpt);
 12415        var root = unvisited[0];
 12416        visitInComponent(root, cmpt);
 12417        self.bfs({
 12418          directed: false,
 12419          roots: root,
 12420          visit: function visit(v) {
 12421            return visitInComponent(v, cmpt);
 12422          }
 12423        });
 12424        cmpt.forEach(function (node) {
 12425          node.connectedEdges().forEach(function (e) {
 12426            // connectedEdges() usually cached
 12427            if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
 12428              // has() is cheap
 12429              cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
 12430            }
 12431          });
 12432        });
 12433      };
 12434  
 12435      do {
 12436        _loop();
 12437      } while (unvisited.length > 0);
 12438  
 12439      return components;
 12440    },
 12441    component: function component() {
 12442      var ele = this[0];
 12443      return ele.cy().mutableElements().components(ele)[0];
 12444    }
 12445  });
 12446  elesfn$t.componentsOf = elesfn$t.components;
 12447  
 12448  var idFactory = {
 12449    generate: function generate(cy, element, tryThisId) {
 12450      var id = tryThisId != null ? tryThisId : uuid();
 12451  
 12452      while (cy.hasElementWithId(id)) {
 12453        id = uuid();
 12454      }
 12455  
 12456      return id;
 12457    }
 12458  }; // represents a set of nodes, edges, or both together
 12459  
 12460  var Collection = function Collection(cy, elements, options) {
 12461    if (cy === undefined || !core(cy)) {
 12462      error('A collection must have a reference to the core');
 12463      return;
 12464    }
 12465  
 12466    var map = new Map$1();
 12467    var createdElements = false;
 12468  
 12469    if (!elements) {
 12470      elements = [];
 12471    } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
 12472      createdElements = true; // make elements from json and restore all at once later
 12473  
 12474      var eles = [];
 12475      var elesIds = new Set$1();
 12476  
 12477      for (var i = 0, l = elements.length; i < l; i++) {
 12478        var json = elements[i];
 12479  
 12480        if (json.data == null) {
 12481          json.data = {};
 12482        }
 12483  
 12484        var _data = json.data; // make sure newly created elements have valid ids
 12485  
 12486        if (_data.id == null) {
 12487          _data.id = idFactory.generate(cy, json);
 12488        } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
 12489          continue; // can't create element if prior id already exists
 12490        }
 12491  
 12492        var ele = new Element(cy, json, false);
 12493        eles.push(ele);
 12494        elesIds.add(_data.id);
 12495      }
 12496  
 12497      elements = eles;
 12498    }
 12499  
 12500    this.length = 0;
 12501  
 12502    for (var _i = 0, _l = elements.length; _i < _l; _i++) {
 12503      var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
 12504  
 12505      if (element$1 == null) {
 12506        continue;
 12507      }
 12508  
 12509      var id = element$1._private.data.id;
 12510  
 12511      if (options == null || options.unique && !map.has(id)) {
 12512        map.set(id, {
 12513          index: this.length,
 12514          ele: element$1
 12515        });
 12516        this[this.length] = element$1;
 12517        this.length++;
 12518      }
 12519    }
 12520  
 12521    this._private = {
 12522      cy: cy,
 12523      map: map
 12524    }; // restore the elements if we created them from json
 12525  
 12526    if (createdElements) {
 12527      this.restore();
 12528    }
 12529  }; // Functions
 12530  ////////////////////////////////////////////////////////////////////////////////////////////////////
 12531  // keep the prototypes in sync (an element has the same functions as a collection)
 12532  // and use elefn and elesfn as shorthands to the prototypes
 12533  
 12534  
 12535  var elesfn$u = Element.prototype = Collection.prototype;
 12536  
 12537  elesfn$u.instanceString = function () {
 12538    return 'collection';
 12539  };
 12540  
 12541  elesfn$u.spawn = function (cy, eles, opts) {
 12542    if (!core(cy)) {
 12543      // cy is optional
 12544      opts = eles;
 12545      eles = cy;
 12546      cy = this.cy();
 12547    }
 12548  
 12549    return new Collection(cy, eles, opts);
 12550  };
 12551  
 12552  elesfn$u.spawnSelf = function () {
 12553    return this.spawn(this);
 12554  };
 12555  
 12556  elesfn$u.cy = function () {
 12557    return this._private.cy;
 12558  };
 12559  
 12560  elesfn$u.renderer = function () {
 12561    return this._private.cy.renderer();
 12562  };
 12563  
 12564  elesfn$u.element = function () {
 12565    return this[0];
 12566  };
 12567  
 12568  elesfn$u.collection = function () {
 12569    if (collection(this)) {
 12570      return this;
 12571    } else {
 12572      // an element
 12573      return new Collection(this._private.cy, [this]);
 12574    }
 12575  };
 12576  
 12577  elesfn$u.unique = function () {
 12578    return new Collection(this._private.cy, this, {
 12579      unique: true
 12580    });
 12581  };
 12582  
 12583  elesfn$u.hasElementWithId = function (id) {
 12584    id = '' + id; // id must be string
 12585  
 12586    return this._private.map.has(id);
 12587  };
 12588  
 12589  elesfn$u.getElementById = function (id) {
 12590    id = '' + id; // id must be string
 12591  
 12592    var cy = this._private.cy;
 12593  
 12594    var entry = this._private.map.get(id);
 12595  
 12596    return entry ? entry.ele : new Collection(cy); // get ele or empty collection
 12597  };
 12598  
 12599  elesfn$u.$id = elesfn$u.getElementById;
 12600  
 12601  elesfn$u.poolIndex = function () {
 12602    var cy = this._private.cy;
 12603    var eles = cy._private.elements;
 12604    var id = this[0]._private.data.id;
 12605    return eles._private.map.get(id).index;
 12606  };
 12607  
 12608  elesfn$u.indexOf = function (ele) {
 12609    var id = ele[0]._private.data.id;
 12610    return this._private.map.get(id).index;
 12611  };
 12612  
 12613  elesfn$u.indexOfId = function (id) {
 12614    id = '' + id; // id must be string
 12615  
 12616    return this._private.map.get(id).index;
 12617  };
 12618  
 12619  elesfn$u.json = function (obj) {
 12620    var ele = this.element();
 12621    var cy = this.cy();
 12622  
 12623    if (ele == null && obj) {
 12624      return this;
 12625    } // can't set to no eles
 12626  
 12627  
 12628    if (ele == null) {
 12629      return undefined;
 12630    } // can't get from no eles
 12631  
 12632  
 12633    var p = ele._private;
 12634  
 12635    if (plainObject(obj)) {
 12636      // set
 12637      cy.startBatch();
 12638  
 12639      if (obj.data) {
 12640        ele.data(obj.data);
 12641        var _data2 = p.data;
 12642  
 12643        if (ele.isEdge()) {
 12644          // source and target are immutable via data()
 12645          var move = false;
 12646          var spec = {};
 12647          var src = obj.data.source;
 12648          var tgt = obj.data.target;
 12649  
 12650          if (src != null && src != _data2.source) {
 12651            spec.source = '' + src; // id must be string
 12652  
 12653            move = true;
 12654          }
 12655  
 12656          if (tgt != null && tgt != _data2.target) {
 12657            spec.target = '' + tgt; // id must be string
 12658  
 12659            move = true;
 12660          }
 12661  
 12662          if (move) {
 12663            ele = ele.move(spec);
 12664          }
 12665        } else {
 12666          // parent is immutable via data()
 12667          var newParentValSpecd = 'parent' in obj.data;
 12668          var parent = obj.data.parent;
 12669  
 12670          if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
 12671            if (parent === undefined) {
 12672              // can't set undefined imperatively, so use null
 12673              parent = null;
 12674            }
 12675  
 12676            if (parent != null) {
 12677              parent = '' + parent; // id must be string
 12678            }
 12679  
 12680            ele = ele.move({
 12681              parent: parent
 12682            });
 12683          }
 12684        }
 12685      }
 12686  
 12687      if (obj.position) {
 12688        ele.position(obj.position);
 12689      } // ignore group -- immutable
 12690  
 12691  
 12692      var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
 12693        var obj_k = obj[k];
 12694  
 12695        if (obj_k != null && obj_k !== p[k]) {
 12696          if (obj_k) {
 12697            ele[trueFnName]();
 12698          } else {
 12699            ele[falseFnName]();
 12700          }
 12701        }
 12702      };
 12703  
 12704      checkSwitch('removed', 'remove', 'restore');
 12705      checkSwitch('selected', 'select', 'unselect');
 12706      checkSwitch('selectable', 'selectify', 'unselectify');
 12707      checkSwitch('locked', 'lock', 'unlock');
 12708      checkSwitch('grabbable', 'grabify', 'ungrabify');
 12709      checkSwitch('pannable', 'panify', 'unpanify');
 12710  
 12711      if (obj.classes != null) {
 12712        ele.classes(obj.classes);
 12713      }
 12714  
 12715      cy.endBatch();
 12716      return this;
 12717    } else if (obj === undefined) {
 12718      // get
 12719      var json = {
 12720        data: copy(p.data),
 12721        position: copy(p.position),
 12722        group: p.group,
 12723        removed: p.removed,
 12724        selected: p.selected,
 12725        selectable: p.selectable,
 12726        locked: p.locked,
 12727        grabbable: p.grabbable,
 12728        pannable: p.pannable,
 12729        classes: null
 12730      };
 12731      json.classes = '';
 12732      var i = 0;
 12733      p.classes.forEach(function (cls) {
 12734        return json.classes += i++ === 0 ? cls : ' ' + cls;
 12735      });
 12736      return json;
 12737    }
 12738  };
 12739  
 12740  elesfn$u.jsons = function () {
 12741    var jsons = [];
 12742  
 12743    for (var i = 0; i < this.length; i++) {
 12744      var ele = this[i];
 12745      var json = ele.json();
 12746      jsons.push(json);
 12747    }
 12748  
 12749    return jsons;
 12750  };
 12751  
 12752  elesfn$u.clone = function () {
 12753    var cy = this.cy();
 12754    var elesArr = [];
 12755  
 12756    for (var i = 0; i < this.length; i++) {
 12757      var ele = this[i];
 12758      var json = ele.json();
 12759      var clone = new Element(cy, json, false); // NB no restore
 12760  
 12761      elesArr.push(clone);
 12762    }
 12763  
 12764    return new Collection(cy, elesArr);
 12765  };
 12766  
 12767  elesfn$u.copy = elesfn$u.clone;
 12768  
 12769  elesfn$u.restore = function () {
 12770    var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
 12771    var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
 12772    var self = this;
 12773    var cy = self.cy();
 12774    var cy_p = cy._private; // create arrays of nodes and edges, since we need to
 12775    // restore the nodes first
 12776  
 12777    var nodes = [];
 12778    var edges = [];
 12779    var elements;
 12780  
 12781    for (var _i2 = 0, l = self.length; _i2 < l; _i2++) {
 12782      var ele = self[_i2];
 12783  
 12784      if (addToPool && !ele.removed()) {
 12785        // don't need to handle this ele
 12786        continue;
 12787      } // keep nodes first in the array and edges after
 12788  
 12789  
 12790      if (ele.isNode()) {
 12791        // put to front of array if node
 12792        nodes.push(ele);
 12793      } else {
 12794        // put to end of array if edge
 12795        edges.push(ele);
 12796      }
 12797    }
 12798  
 12799    elements = nodes.concat(edges);
 12800    var i;
 12801  
 12802    var removeFromElements = function removeFromElements() {
 12803      elements.splice(i, 1);
 12804      i--;
 12805    }; // now, restore each element
 12806  
 12807  
 12808    for (i = 0; i < elements.length; i++) {
 12809      var _ele = elements[i];
 12810      var _private = _ele._private;
 12811      var _data3 = _private.data; // the traversal cache should start fresh when ele is added
 12812  
 12813      _ele.clearTraversalCache(); // set id and validate
 12814  
 12815  
 12816      if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
 12817        _data3.id = idFactory.generate(cy, _ele);
 12818      } else if (number(_data3.id)) {
 12819        _data3.id = '' + _data3.id; // now it's a string
 12820      } else if (emptyString(_data3.id) || !string(_data3.id)) {
 12821        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
 12822  
 12823        removeFromElements();
 12824        continue;
 12825      } else if (cy.hasElementWithId(_data3.id)) {
 12826        error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
 12827  
 12828        removeFromElements();
 12829        continue;
 12830      }
 12831  
 12832      var id = _data3.id; // id is finalised, now let's keep a ref
 12833  
 12834      if (_ele.isNode()) {
 12835        // extra checks for nodes
 12836        var pos = _private.position; // make sure the nodes have a defined position
 12837  
 12838        if (pos.x == null) {
 12839          pos.x = 0;
 12840        }
 12841  
 12842        if (pos.y == null) {
 12843          pos.y = 0;
 12844        }
 12845      }
 12846  
 12847      if (_ele.isEdge()) {
 12848        // extra checks for edges
 12849        var edge = _ele;
 12850        var fields = ['source', 'target'];
 12851        var fieldsLength = fields.length;
 12852        var badSourceOrTarget = false;
 12853  
 12854        for (var j = 0; j < fieldsLength; j++) {
 12855          var field = fields[j];
 12856          var val = _data3[field];
 12857  
 12858          if (number(val)) {
 12859            val = _data3[field] = '' + _data3[field]; // now string
 12860          }
 12861  
 12862          if (val == null || val === '') {
 12863            // can't create if source or target is not defined properly
 12864            error('Can not create edge `' + id + '` with unspecified ' + field);
 12865            badSourceOrTarget = true;
 12866          } else if (!cy.hasElementWithId(val)) {
 12867            // can't create edge if one of its nodes doesn't exist
 12868            error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
 12869            badSourceOrTarget = true;
 12870          }
 12871        }
 12872  
 12873        if (badSourceOrTarget) {
 12874          removeFromElements();
 12875          continue;
 12876        } // can't create this
 12877  
 12878  
 12879        var src = cy.getElementById(_data3.source);
 12880        var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
 12881  
 12882        if (src.same(tgt)) {
 12883          src._private.edges.push(edge);
 12884        } else {
 12885          src._private.edges.push(edge);
 12886  
 12887          tgt._private.edges.push(edge);
 12888        }
 12889  
 12890        edge._private.source = src;
 12891        edge._private.target = tgt;
 12892      } // if is edge
 12893      // create mock ids / indexes maps for element so it can be used like collections
 12894  
 12895  
 12896      _private.map = new Map$1();
 12897  
 12898      _private.map.set(id, {
 12899        ele: _ele,
 12900        index: 0
 12901      });
 12902  
 12903      _private.removed = false;
 12904  
 12905      if (addToPool) {
 12906        cy.addToPool(_ele);
 12907      }
 12908    } // for each element
 12909    // do compound node sanity checks
 12910  
 12911  
 12912    for (var _i3 = 0; _i3 < nodes.length; _i3++) {
 12913      // each node
 12914      var node = nodes[_i3];
 12915      var _data4 = node._private.data;
 12916  
 12917      if (number(_data4.parent)) {
 12918        // then automake string
 12919        _data4.parent = '' + _data4.parent;
 12920      }
 12921  
 12922      var parentId = _data4.parent;
 12923      var specifiedParent = parentId != null;
 12924  
 12925      if (specifiedParent) {
 12926        var parent = cy.getElementById(parentId);
 12927  
 12928        if (parent.empty()) {
 12929          // non-existant parent; just remove it
 12930          _data4.parent = undefined;
 12931        } else {
 12932          var selfAsParent = false;
 12933          var ancestor = parent;
 12934  
 12935          while (!ancestor.empty()) {
 12936            if (node.same(ancestor)) {
 12937              // mark self as parent and remove from data
 12938              selfAsParent = true;
 12939              _data4.parent = undefined; // remove parent reference
 12940              // exit or we loop forever
 12941  
 12942              break;
 12943            }
 12944  
 12945            ancestor = ancestor.parent();
 12946          }
 12947  
 12948          if (!selfAsParent) {
 12949            // connect with children
 12950            parent[0]._private.children.push(node);
 12951  
 12952            node._private.parent = parent[0]; // let the core know we have a compound graph
 12953  
 12954            cy_p.hasCompoundNodes = true;
 12955          }
 12956        } // else
 12957  
 12958      } // if specified parent
 12959  
 12960    } // for each node
 12961  
 12962  
 12963    if (elements.length > 0) {
 12964      var restored = new Collection(cy, elements);
 12965  
 12966      for (var _i4 = 0; _i4 < restored.length; _i4++) {
 12967        var _ele2 = restored[_i4];
 12968  
 12969        if (_ele2.isNode()) {
 12970          continue;
 12971        } // adding an edge invalidates the traversal caches for the parallel edges
 12972  
 12973  
 12974        _ele2.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
 12975  
 12976  
 12977        _ele2.source().clearTraversalCache();
 12978  
 12979        _ele2.target().clearTraversalCache();
 12980      }
 12981  
 12982      var toUpdateStyle;
 12983  
 12984      if (cy_p.hasCompoundNodes) {
 12985        toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
 12986      } else {
 12987        toUpdateStyle = restored;
 12988      }
 12989  
 12990      toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
 12991  
 12992      if (notifyRenderer) {
 12993        restored.emitAndNotify('add');
 12994      } else if (addToPool) {
 12995        restored.emit('add');
 12996      }
 12997    }
 12998  
 12999    return self; // chainability
 13000  };
 13001  
 13002  elesfn$u.removed = function () {
 13003    var ele = this[0];
 13004    return ele && ele._private.removed;
 13005  };
 13006  
 13007  elesfn$u.inside = function () {
 13008    var ele = this[0];
 13009    return ele && !ele._private.removed;
 13010  };
 13011  
 13012  elesfn$u.remove = function () {
 13013    var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
 13014    var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
 13015    var self = this;
 13016    var elesToRemove = [];
 13017    var elesToRemoveIds = {};
 13018    var cy = self._private.cy; // add connected edges
 13019  
 13020    function addConnectedEdges(node) {
 13021      var edges = node._private.edges;
 13022  
 13023      for (var i = 0; i < edges.length; i++) {
 13024        add(edges[i]);
 13025      }
 13026    } // add descendant nodes
 13027  
 13028  
 13029    function addChildren(node) {
 13030      var children = node._private.children;
 13031  
 13032      for (var i = 0; i < children.length; i++) {
 13033        add(children[i]);
 13034      }
 13035    }
 13036  
 13037    function add(ele) {
 13038      var alreadyAdded = elesToRemoveIds[ele.id()];
 13039  
 13040      if (removeFromPool && ele.removed() || alreadyAdded) {
 13041        return;
 13042      } else {
 13043        elesToRemoveIds[ele.id()] = true;
 13044      }
 13045  
 13046      if (ele.isNode()) {
 13047        elesToRemove.push(ele); // nodes are removed last
 13048  
 13049        addConnectedEdges(ele);
 13050        addChildren(ele);
 13051      } else {
 13052        elesToRemove.unshift(ele); // edges are removed first
 13053      }
 13054    } // make the list of elements to remove
 13055    // (may be removing more than specified due to connected edges etc)
 13056  
 13057  
 13058    for (var i = 0, l = self.length; i < l; i++) {
 13059      var ele = self[i];
 13060      add(ele);
 13061    }
 13062  
 13063    function removeEdgeRef(node, edge) {
 13064      var connectedEdges = node._private.edges;
 13065      removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
 13066  
 13067      node.clearTraversalCache();
 13068    }
 13069  
 13070    function removeParallelRef(pllEdge) {
 13071      // removing an edge invalidates the traversal caches for the parallel edges
 13072      pllEdge.clearTraversalCache();
 13073    }
 13074  
 13075    var alteredParents = [];
 13076    alteredParents.ids = {};
 13077  
 13078    function removeChildRef(parent, ele) {
 13079      ele = ele[0];
 13080      parent = parent[0];
 13081      var children = parent._private.children;
 13082      var pid = parent.id();
 13083      removeFromArray(children, ele); // remove parent => child ref
 13084  
 13085      ele._private.parent = null; // remove child => parent ref
 13086  
 13087      if (!alteredParents.ids[pid]) {
 13088        alteredParents.ids[pid] = true;
 13089        alteredParents.push(parent);
 13090      }
 13091    }
 13092  
 13093    self.dirtyCompoundBoundsCache();
 13094  
 13095    if (removeFromPool) {
 13096      cy.removeFromPool(elesToRemove); // remove from core pool
 13097    }
 13098  
 13099    for (var _i5 = 0; _i5 < elesToRemove.length; _i5++) {
 13100      var _ele3 = elesToRemove[_i5];
 13101  
 13102      if (_ele3.isEdge()) {
 13103        // remove references to this edge in its connected nodes
 13104        var src = _ele3.source()[0];
 13105  
 13106        var tgt = _ele3.target()[0];
 13107  
 13108        removeEdgeRef(src, _ele3);
 13109        removeEdgeRef(tgt, _ele3);
 13110  
 13111        var pllEdges = _ele3.parallelEdges();
 13112  
 13113        for (var j = 0; j < pllEdges.length; j++) {
 13114          var pllEdge = pllEdges[j];
 13115          removeParallelRef(pllEdge);
 13116  
 13117          if (pllEdge.isBundledBezier()) {
 13118            pllEdge.dirtyBoundingBoxCache();
 13119          }
 13120        }
 13121      } else {
 13122        // remove reference to parent
 13123        var parent = _ele3.parent();
 13124  
 13125        if (parent.length !== 0) {
 13126          removeChildRef(parent, _ele3);
 13127        }
 13128      }
 13129  
 13130      if (removeFromPool) {
 13131        // mark as removed
 13132        _ele3._private.removed = true;
 13133      }
 13134    } // check to see if we have a compound graph or not
 13135  
 13136  
 13137    var elesStillInside = cy._private.elements;
 13138    cy._private.hasCompoundNodes = false;
 13139  
 13140    for (var _i6 = 0; _i6 < elesStillInside.length; _i6++) {
 13141      var _ele4 = elesStillInside[_i6];
 13142  
 13143      if (_ele4.isParent()) {
 13144        cy._private.hasCompoundNodes = true;
 13145        break;
 13146      }
 13147    }
 13148  
 13149    var removedElements = new Collection(this.cy(), elesToRemove);
 13150  
 13151    if (removedElements.size() > 0) {
 13152      // must manually notify since trigger won't do this automatically once removed
 13153      if (notifyRenderer) {
 13154        removedElements.emitAndNotify('remove');
 13155      } else if (removeFromPool) {
 13156        removedElements.emit('remove');
 13157      }
 13158    } // the parents who were modified by the removal need their style updated
 13159  
 13160  
 13161    for (var _i7 = 0; _i7 < alteredParents.length; _i7++) {
 13162      var _ele5 = alteredParents[_i7];
 13163  
 13164      if (!removeFromPool || !_ele5.removed()) {
 13165        _ele5.updateStyle();
 13166      }
 13167    }
 13168  
 13169    return removedElements;
 13170  };
 13171  
 13172  elesfn$u.move = function (struct) {
 13173    var cy = this._private.cy;
 13174    var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
 13175    // (our calls to remove/restore do not remove from the graph or make events)
 13176  
 13177    var notifyRenderer = false;
 13178    var modifyPool = false;
 13179  
 13180    var toString = function toString(id) {
 13181      return id == null ? id : '' + id;
 13182    }; // id must be string
 13183  
 13184  
 13185    if (struct.source !== undefined || struct.target !== undefined) {
 13186      var srcId = toString(struct.source);
 13187      var tgtId = toString(struct.target);
 13188      var srcExists = srcId != null && cy.hasElementWithId(srcId);
 13189      var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
 13190  
 13191      if (srcExists || tgtExists) {
 13192        cy.batch(function () {
 13193          // avoid duplicate style updates
 13194          eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
 13195  
 13196          eles.emitAndNotify('moveout');
 13197  
 13198          for (var i = 0; i < eles.length; i++) {
 13199            var ele = eles[i];
 13200            var _data5 = ele._private.data;
 13201  
 13202            if (ele.isEdge()) {
 13203              if (srcExists) {
 13204                _data5.source = srcId;
 13205              }
 13206  
 13207              if (tgtExists) {
 13208                _data5.target = tgtId;
 13209              }
 13210            }
 13211          }
 13212  
 13213          eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
 13214        });
 13215        eles.emitAndNotify('move');
 13216      }
 13217    } else if (struct.parent !== undefined) {
 13218      // move node to new parent
 13219      var parentId = toString(struct.parent);
 13220      var parentExists = parentId === null || cy.hasElementWithId(parentId);
 13221  
 13222      if (parentExists) {
 13223        var pidToAssign = parentId === null ? undefined : parentId;
 13224        cy.batch(function () {
 13225          // avoid duplicate style updates
 13226          var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
 13227  
 13228          updated.emitAndNotify('moveout');
 13229  
 13230          for (var i = 0; i < eles.length; i++) {
 13231            var ele = eles[i];
 13232            var _data6 = ele._private.data;
 13233  
 13234            if (ele.isNode()) {
 13235              _data6.parent = pidToAssign;
 13236            }
 13237          }
 13238  
 13239          updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
 13240        });
 13241        eles.emitAndNotify('move');
 13242      }
 13243    }
 13244  
 13245    return this;
 13246  };
 13247  
 13248  [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) {
 13249    extend(elesfn$u, props);
 13250  });
 13251  
 13252  var corefn = {
 13253    add: function add(opts) {
 13254      var elements;
 13255      var cy = this; // add the elements
 13256  
 13257      if (elementOrCollection(opts)) {
 13258        var eles = opts;
 13259  
 13260        if (eles._private.cy === cy) {
 13261          // same instance => just restore
 13262          elements = eles.restore();
 13263        } else {
 13264          // otherwise, copy from json
 13265          var jsons = [];
 13266  
 13267          for (var i = 0; i < eles.length; i++) {
 13268            var ele = eles[i];
 13269            jsons.push(ele.json());
 13270          }
 13271  
 13272          elements = new Collection(cy, jsons);
 13273        }
 13274      } // specify an array of options
 13275      else if (array(opts)) {
 13276          var _jsons = opts;
 13277          elements = new Collection(cy, _jsons);
 13278        } // specify via opts.nodes and opts.edges
 13279        else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
 13280            var elesByGroup = opts;
 13281            var _jsons2 = [];
 13282            var grs = ['nodes', 'edges'];
 13283  
 13284            for (var _i = 0, il = grs.length; _i < il; _i++) {
 13285              var group = grs[_i];
 13286              var elesArray = elesByGroup[group];
 13287  
 13288              if (array(elesArray)) {
 13289                for (var j = 0, jl = elesArray.length; j < jl; j++) {
 13290                  var json = extend({
 13291                    group: group
 13292                  }, elesArray[j]);
 13293  
 13294                  _jsons2.push(json);
 13295                }
 13296              }
 13297            }
 13298  
 13299            elements = new Collection(cy, _jsons2);
 13300          } // specify options for one element
 13301          else {
 13302              var _json = opts;
 13303              elements = new Element(cy, _json).collection();
 13304            }
 13305  
 13306      return elements;
 13307    },
 13308    remove: function remove(collection) {
 13309      if (elementOrCollection(collection)) ; else if (string(collection)) {
 13310        var selector = collection;
 13311        collection = this.$(selector);
 13312      }
 13313  
 13314      return collection.remove();
 13315    }
 13316  };
 13317  
 13318  /* global Float32Array */
 13319  
 13320  /*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
 13321  function generateCubicBezier(mX1, mY1, mX2, mY2) {
 13322    var NEWTON_ITERATIONS = 4,
 13323        NEWTON_MIN_SLOPE = 0.001,
 13324        SUBDIVISION_PRECISION = 0.0000001,
 13325        SUBDIVISION_MAX_ITERATIONS = 10,
 13326        kSplineTableSize = 11,
 13327        kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
 13328        float32ArraySupported = typeof Float32Array !== 'undefined';
 13329    /* Must contain four arguments. */
 13330  
 13331    if (arguments.length !== 4) {
 13332      return false;
 13333    }
 13334    /* Arguments must be numbers. */
 13335  
 13336  
 13337    for (var i = 0; i < 4; ++i) {
 13338      if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
 13339        return false;
 13340      }
 13341    }
 13342    /* X values must be in the [0, 1] range. */
 13343  
 13344  
 13345    mX1 = Math.min(mX1, 1);
 13346    mX2 = Math.min(mX2, 1);
 13347    mX1 = Math.max(mX1, 0);
 13348    mX2 = Math.max(mX2, 0);
 13349    var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
 13350  
 13351    function A(aA1, aA2) {
 13352      return 1.0 - 3.0 * aA2 + 3.0 * aA1;
 13353    }
 13354  
 13355    function B(aA1, aA2) {
 13356      return 3.0 * aA2 - 6.0 * aA1;
 13357    }
 13358  
 13359    function C(aA1) {
 13360      return 3.0 * aA1;
 13361    }
 13362  
 13363    function calcBezier(aT, aA1, aA2) {
 13364      return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
 13365    }
 13366  
 13367    function getSlope(aT, aA1, aA2) {
 13368      return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
 13369    }
 13370  
 13371    function newtonRaphsonIterate(aX, aGuessT) {
 13372      for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
 13373        var currentSlope = getSlope(aGuessT, mX1, mX2);
 13374  
 13375        if (currentSlope === 0.0) {
 13376          return aGuessT;
 13377        }
 13378  
 13379        var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
 13380        aGuessT -= currentX / currentSlope;
 13381      }
 13382  
 13383      return aGuessT;
 13384    }
 13385  
 13386    function calcSampleValues() {
 13387      for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
 13388        mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
 13389      }
 13390    }
 13391  
 13392    function binarySubdivide(aX, aA, aB) {
 13393      var currentX,
 13394          currentT,
 13395          i = 0;
 13396  
 13397      do {
 13398        currentT = aA + (aB - aA) / 2.0;
 13399        currentX = calcBezier(currentT, mX1, mX2) - aX;
 13400  
 13401        if (currentX > 0.0) {
 13402          aB = currentT;
 13403        } else {
 13404          aA = currentT;
 13405        }
 13406      } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
 13407  
 13408      return currentT;
 13409    }
 13410  
 13411    function getTForX(aX) {
 13412      var intervalStart = 0.0,
 13413          currentSample = 1,
 13414          lastSample = kSplineTableSize - 1;
 13415  
 13416      for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
 13417        intervalStart += kSampleStepSize;
 13418      }
 13419  
 13420      --currentSample;
 13421      var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
 13422          guessForT = intervalStart + dist * kSampleStepSize,
 13423          initialSlope = getSlope(guessForT, mX1, mX2);
 13424  
 13425      if (initialSlope >= NEWTON_MIN_SLOPE) {
 13426        return newtonRaphsonIterate(aX, guessForT);
 13427      } else if (initialSlope === 0.0) {
 13428        return guessForT;
 13429      } else {
 13430        return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
 13431      }
 13432    }
 13433  
 13434    var _precomputed = false;
 13435  
 13436    function precompute() {
 13437      _precomputed = true;
 13438  
 13439      if (mX1 !== mY1 || mX2 !== mY2) {
 13440        calcSampleValues();
 13441      }
 13442    }
 13443  
 13444    var f = function f(aX) {
 13445      if (!_precomputed) {
 13446        precompute();
 13447      }
 13448  
 13449      if (mX1 === mY1 && mX2 === mY2) {
 13450        return aX;
 13451      }
 13452  
 13453      if (aX === 0) {
 13454        return 0;
 13455      }
 13456  
 13457      if (aX === 1) {
 13458        return 1;
 13459      }
 13460  
 13461      return calcBezier(getTForX(aX), mY1, mY2);
 13462    };
 13463  
 13464    f.getControlPoints = function () {
 13465      return [{
 13466        x: mX1,
 13467        y: mY1
 13468      }, {
 13469        x: mX2,
 13470        y: mY2
 13471      }];
 13472    };
 13473  
 13474    var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
 13475  
 13476    f.toString = function () {
 13477      return str;
 13478    };
 13479  
 13480    return f;
 13481  }
 13482  
 13483  /*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
 13484  
 13485  /* 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
 13486     then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
 13487  var generateSpringRK4 = function () {
 13488    function springAccelerationForState(state) {
 13489      return -state.tension * state.x - state.friction * state.v;
 13490    }
 13491  
 13492    function springEvaluateStateWithDerivative(initialState, dt, derivative) {
 13493      var state = {
 13494        x: initialState.x + derivative.dx * dt,
 13495        v: initialState.v + derivative.dv * dt,
 13496        tension: initialState.tension,
 13497        friction: initialState.friction
 13498      };
 13499      return {
 13500        dx: state.v,
 13501        dv: springAccelerationForState(state)
 13502      };
 13503    }
 13504  
 13505    function springIntegrateState(state, dt) {
 13506      var a = {
 13507        dx: state.v,
 13508        dv: springAccelerationForState(state)
 13509      },
 13510          b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
 13511          c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
 13512          d = springEvaluateStateWithDerivative(state, dt, c),
 13513          dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
 13514          dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
 13515      state.x = state.x + dxdt * dt;
 13516      state.v = state.v + dvdt * dt;
 13517      return state;
 13518    }
 13519  
 13520    return function springRK4Factory(tension, friction, duration) {
 13521      var initState = {
 13522        x: -1,
 13523        v: 0,
 13524        tension: null,
 13525        friction: null
 13526      },
 13527          path = [0],
 13528          time_lapsed = 0,
 13529          tolerance = 1 / 10000,
 13530          DT = 16 / 1000,
 13531          have_duration,
 13532          dt,
 13533          last_state;
 13534      tension = parseFloat(tension) || 500;
 13535      friction = parseFloat(friction) || 20;
 13536      duration = duration || null;
 13537      initState.tension = tension;
 13538      initState.friction = friction;
 13539      have_duration = duration !== null;
 13540      /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
 13541  
 13542      if (have_duration) {
 13543        /* Run the simulation without a duration. */
 13544        time_lapsed = springRK4Factory(tension, friction);
 13545        /* Compute the adjusted time delta. */
 13546  
 13547        dt = time_lapsed / duration * DT;
 13548      } else {
 13549        dt = DT;
 13550      }
 13551  
 13552      for (;;) {
 13553        /* Next/step function .*/
 13554        last_state = springIntegrateState(last_state || initState, dt);
 13555        /* Store the position. */
 13556  
 13557        path.push(1 + last_state.x);
 13558        time_lapsed += 16;
 13559        /* If the change threshold is reached, break. */
 13560  
 13561        if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
 13562          break;
 13563        }
 13564      }
 13565      /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
 13566         computed path and returns a snapshot of the position according to a given percentComplete. */
 13567  
 13568  
 13569      return !have_duration ? time_lapsed : function (percentComplete) {
 13570        return path[percentComplete * (path.length - 1) | 0];
 13571      };
 13572    };
 13573  }();
 13574  
 13575  var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
 13576    var bezier = generateCubicBezier(t1, p1, t2, p2);
 13577    return function (start, end, percent) {
 13578      return start + (end - start) * bezier(percent);
 13579    };
 13580  };
 13581  
 13582  var easings = {
 13583    'linear': function linear(start, end, percent) {
 13584      return start + (end - start) * percent;
 13585    },
 13586    // default easings
 13587    'ease': cubicBezier(0.25, 0.1, 0.25, 1),
 13588    'ease-in': cubicBezier(0.42, 0, 1, 1),
 13589    'ease-out': cubicBezier(0, 0, 0.58, 1),
 13590    'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
 13591    // sine
 13592    'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
 13593    'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
 13594    'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
 13595    // quad
 13596    'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
 13597    'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
 13598    'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
 13599    // cubic
 13600    'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
 13601    'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
 13602    'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
 13603    // quart
 13604    'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
 13605    'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
 13606    'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
 13607    // quint
 13608    'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
 13609    'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
 13610    'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
 13611    // expo
 13612    'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
 13613    'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
 13614    'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
 13615    // circ
 13616    'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
 13617    'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
 13618    'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
 13619    // user param easings...
 13620    'spring': function spring(tension, friction, duration) {
 13621      if (duration === 0) {
 13622        // can't get a spring w/ duration 0
 13623        return easings.linear; // duration 0 => jump to end so impl doesn't matter
 13624      }
 13625  
 13626      var spring = generateSpringRK4(tension, friction, duration);
 13627      return function (start, end, percent) {
 13628        return start + (end - start) * spring(percent);
 13629      };
 13630    },
 13631    'cubic-bezier': cubicBezier
 13632  };
 13633  
 13634  function getEasedValue(type, start, end, percent, easingFn) {
 13635    if (percent === 1) {
 13636      return end;
 13637    }
 13638  
 13639    if (start === end) {
 13640      return end;
 13641    }
 13642  
 13643    var val = easingFn(start, end, percent);
 13644  
 13645    if (type == null) {
 13646      return val;
 13647    }
 13648  
 13649    if (type.roundValue || type.color) {
 13650      val = Math.round(val);
 13651    }
 13652  
 13653    if (type.min !== undefined) {
 13654      val = Math.max(val, type.min);
 13655    }
 13656  
 13657    if (type.max !== undefined) {
 13658      val = Math.min(val, type.max);
 13659    }
 13660  
 13661    return val;
 13662  }
 13663  
 13664  function getValue(prop, spec) {
 13665    if (prop.pfValue != null || prop.value != null) {
 13666      if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
 13667        return prop.pfValue;
 13668      } else {
 13669        return prop.value;
 13670      }
 13671    } else {
 13672      return prop;
 13673    }
 13674  }
 13675  
 13676  function ease(startProp, endProp, percent, easingFn, propSpec) {
 13677    var type = propSpec != null ? propSpec.type : null;
 13678  
 13679    if (percent < 0) {
 13680      percent = 0;
 13681    } else if (percent > 1) {
 13682      percent = 1;
 13683    }
 13684  
 13685    var start = getValue(startProp, propSpec);
 13686    var end = getValue(endProp, propSpec);
 13687  
 13688    if (number(start) && number(end)) {
 13689      return getEasedValue(type, start, end, percent, easingFn);
 13690    } else if (array(start) && array(end)) {
 13691      var easedArr = [];
 13692  
 13693      for (var i = 0; i < end.length; i++) {
 13694        var si = start[i];
 13695        var ei = end[i];
 13696  
 13697        if (si != null && ei != null) {
 13698          var val = getEasedValue(type, si, ei, percent, easingFn);
 13699          easedArr.push(val);
 13700        } else {
 13701          easedArr.push(ei);
 13702        }
 13703      }
 13704  
 13705      return easedArr;
 13706    }
 13707  
 13708    return undefined;
 13709  }
 13710  
 13711  function step(self, ani, now, isCore) {
 13712    var isEles = !isCore;
 13713    var _p = self._private;
 13714    var ani_p = ani._private;
 13715    var pEasing = ani_p.easing;
 13716    var startTime = ani_p.startTime;
 13717    var cy = isCore ? self : self.cy();
 13718    var style = cy.style();
 13719  
 13720    if (!ani_p.easingImpl) {
 13721      if (pEasing == null) {
 13722        // use default
 13723        ani_p.easingImpl = easings['linear'];
 13724      } else {
 13725        // then define w/ name
 13726        var easingVals;
 13727  
 13728        if (string(pEasing)) {
 13729          var easingProp = style.parse('transition-timing-function', pEasing);
 13730          easingVals = easingProp.value;
 13731        } else {
 13732          // then assume preparsed array
 13733          easingVals = pEasing;
 13734        }
 13735  
 13736        var name, args;
 13737  
 13738        if (string(easingVals)) {
 13739          name = easingVals;
 13740          args = [];
 13741        } else {
 13742          name = easingVals[1];
 13743          args = easingVals.slice(2).map(function (n) {
 13744            return +n;
 13745          });
 13746        }
 13747  
 13748        if (args.length > 0) {
 13749          // create with args
 13750          if (name === 'spring') {
 13751            args.push(ani_p.duration); // need duration to generate spring
 13752          }
 13753  
 13754          ani_p.easingImpl = easings[name].apply(null, args);
 13755        } else {
 13756          // static impl by name
 13757          ani_p.easingImpl = easings[name];
 13758        }
 13759      }
 13760    }
 13761  
 13762    var easing = ani_p.easingImpl;
 13763    var percent;
 13764  
 13765    if (ani_p.duration === 0) {
 13766      percent = 1;
 13767    } else {
 13768      percent = (now - startTime) / ani_p.duration;
 13769    }
 13770  
 13771    if (ani_p.applying) {
 13772      percent = ani_p.progress;
 13773    }
 13774  
 13775    if (percent < 0) {
 13776      percent = 0;
 13777    } else if (percent > 1) {
 13778      percent = 1;
 13779    }
 13780  
 13781    if (ani_p.delay == null) {
 13782      // then update
 13783      var startPos = ani_p.startPosition;
 13784      var endPos = ani_p.position;
 13785  
 13786      if (endPos && isEles && !self.locked()) {
 13787        var newPos = {};
 13788  
 13789        if (valid(startPos.x, endPos.x)) {
 13790          newPos.x = ease(startPos.x, endPos.x, percent, easing);
 13791        }
 13792  
 13793        if (valid(startPos.y, endPos.y)) {
 13794          newPos.y = ease(startPos.y, endPos.y, percent, easing);
 13795        }
 13796  
 13797        self.position(newPos);
 13798      }
 13799  
 13800      var startPan = ani_p.startPan;
 13801      var endPan = ani_p.pan;
 13802      var pan = _p.pan;
 13803      var animatingPan = endPan != null && isCore;
 13804  
 13805      if (animatingPan) {
 13806        if (valid(startPan.x, endPan.x)) {
 13807          pan.x = ease(startPan.x, endPan.x, percent, easing);
 13808        }
 13809  
 13810        if (valid(startPan.y, endPan.y)) {
 13811          pan.y = ease(startPan.y, endPan.y, percent, easing);
 13812        }
 13813  
 13814        self.emit('pan');
 13815      }
 13816  
 13817      var startZoom = ani_p.startZoom;
 13818      var endZoom = ani_p.zoom;
 13819      var animatingZoom = endZoom != null && isCore;
 13820  
 13821      if (animatingZoom) {
 13822        if (valid(startZoom, endZoom)) {
 13823          _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
 13824        }
 13825  
 13826        self.emit('zoom');
 13827      }
 13828  
 13829      if (animatingPan || animatingZoom) {
 13830        self.emit('viewport');
 13831      }
 13832  
 13833      var props = ani_p.style;
 13834  
 13835      if (props && props.length > 0 && isEles) {
 13836        for (var i = 0; i < props.length; i++) {
 13837          var prop = props[i];
 13838          var _name = prop.name;
 13839          var end = prop;
 13840          var start = ani_p.startStyle[_name];
 13841          var propSpec = style.properties[start.name];
 13842          var easedVal = ease(start, end, percent, easing, propSpec);
 13843          style.overrideBypass(self, _name, easedVal);
 13844        } // for props
 13845  
 13846  
 13847        self.emit('style');
 13848      } // if
 13849  
 13850    }
 13851  
 13852    ani_p.progress = percent;
 13853    return percent;
 13854  }
 13855  
 13856  function valid(start, end) {
 13857    if (start == null || end == null) {
 13858      return false;
 13859    }
 13860  
 13861    if (number(start) && number(end)) {
 13862      return true;
 13863    } else if (start && end) {
 13864      return true;
 13865    }
 13866  
 13867    return false;
 13868  }
 13869  
 13870  function startAnimation(self, ani, now, isCore) {
 13871    var ani_p = ani._private;
 13872    ani_p.started = true;
 13873    ani_p.startTime = now - ani_p.progress * ani_p.duration;
 13874  }
 13875  
 13876  function stepAll(now, cy) {
 13877    var eles = cy._private.aniEles;
 13878    var doneEles = [];
 13879  
 13880    function stepOne(ele, isCore) {
 13881      var _p = ele._private;
 13882      var current = _p.animation.current;
 13883      var queue = _p.animation.queue;
 13884      var ranAnis = false; // cancel all animations on display:none ele
 13885  
 13886      if (!isCore && ele.pstyle('display').value === 'none') {
 13887        // put all current and queue animations in this tick's current list
 13888        // and empty the lists for the element
 13889        current = current.splice(0, current.length).concat(queue.splice(0, queue.length)); // stop all animations
 13890  
 13891        for (var i = 0; i < current.length; i++) {
 13892          current[i].stop();
 13893        }
 13894      } // if nothing currently animating, get something from the queue
 13895  
 13896  
 13897      if (current.length === 0) {
 13898        var next = queue.shift();
 13899  
 13900        if (next) {
 13901          current.push(next);
 13902        }
 13903      }
 13904  
 13905      var callbacks = function callbacks(_callbacks) {
 13906        for (var j = _callbacks.length - 1; j >= 0; j--) {
 13907          var cb = _callbacks[j];
 13908          cb();
 13909        }
 13910  
 13911        _callbacks.splice(0, _callbacks.length);
 13912      }; // step and remove if done
 13913  
 13914  
 13915      for (var _i = current.length - 1; _i >= 0; _i--) {
 13916        var ani = current[_i];
 13917        var ani_p = ani._private;
 13918  
 13919        if (ani_p.stopped) {
 13920          current.splice(_i, 1);
 13921          ani_p.hooked = false;
 13922          ani_p.playing = false;
 13923          ani_p.started = false;
 13924          callbacks(ani_p.frames);
 13925          continue;
 13926        }
 13927  
 13928        if (!ani_p.playing && !ani_p.applying) {
 13929          continue;
 13930        } // an apply() while playing shouldn't do anything
 13931  
 13932  
 13933        if (ani_p.playing && ani_p.applying) {
 13934          ani_p.applying = false;
 13935        }
 13936  
 13937        if (!ani_p.started) {
 13938          startAnimation(ele, ani, now);
 13939        }
 13940  
 13941        step(ele, ani, now, isCore);
 13942  
 13943        if (ani_p.applying) {
 13944          ani_p.applying = false;
 13945        }
 13946  
 13947        callbacks(ani_p.frames);
 13948  
 13949        if (ani_p.step != null) {
 13950          ani_p.step(now);
 13951        }
 13952  
 13953        if (ani.completed()) {
 13954          current.splice(_i, 1);
 13955          ani_p.hooked = false;
 13956          ani_p.playing = false;
 13957          ani_p.started = false;
 13958          callbacks(ani_p.completes);
 13959        }
 13960  
 13961        ranAnis = true;
 13962      }
 13963  
 13964      if (!isCore && current.length === 0 && queue.length === 0) {
 13965        doneEles.push(ele);
 13966      }
 13967  
 13968      return ranAnis;
 13969    } // stepElement
 13970    // handle all eles
 13971  
 13972  
 13973    var ranEleAni = false;
 13974  
 13975    for (var e = 0; e < eles.length; e++) {
 13976      var ele = eles[e];
 13977      var handledThisEle = stepOne(ele);
 13978      ranEleAni = ranEleAni || handledThisEle;
 13979    } // each element
 13980  
 13981  
 13982    var ranCoreAni = stepOne(cy, true); // notify renderer
 13983  
 13984    if (ranEleAni || ranCoreAni) {
 13985      if (eles.length > 0) {
 13986        cy.notify('draw', eles);
 13987      } else {
 13988        cy.notify('draw');
 13989      }
 13990    } // remove elements from list of currently animating if its queues are empty
 13991  
 13992  
 13993    eles.unmerge(doneEles);
 13994    cy.emit('step');
 13995  } // stepAll
 13996  
 13997  var corefn$1 = {
 13998    // pull in animation functions
 13999    animate: define$3.animate(),
 14000    animation: define$3.animation(),
 14001    animated: define$3.animated(),
 14002    clearQueue: define$3.clearQueue(),
 14003    delay: define$3.delay(),
 14004    delayAnimation: define$3.delayAnimation(),
 14005    stop: define$3.stop(),
 14006    addToAnimationPool: function addToAnimationPool(eles) {
 14007      var cy = this;
 14008  
 14009      if (!cy.styleEnabled()) {
 14010        return;
 14011      } // save cycles when no style used
 14012  
 14013  
 14014      cy._private.aniEles.merge(eles);
 14015    },
 14016    stopAnimationLoop: function stopAnimationLoop() {
 14017      this._private.animationsRunning = false;
 14018    },
 14019    startAnimationLoop: function startAnimationLoop() {
 14020      var cy = this;
 14021      cy._private.animationsRunning = true;
 14022  
 14023      if (!cy.styleEnabled()) {
 14024        return;
 14025      } // save cycles when no style used
 14026      // NB the animation loop will exec in headless environments if style enabled
 14027      // and explicit cy.destroy() is necessary to stop the loop
 14028  
 14029  
 14030      function headlessStep() {
 14031        if (!cy._private.animationsRunning) {
 14032          return;
 14033        }
 14034  
 14035        requestAnimationFrame(function animationStep(now) {
 14036          stepAll(now, cy);
 14037          headlessStep();
 14038        });
 14039      }
 14040  
 14041      var renderer = cy.renderer();
 14042  
 14043      if (renderer && renderer.beforeRender) {
 14044        // let the renderer schedule animations
 14045        renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
 14046          stepAll(now, cy);
 14047        }, renderer.beforeRenderPriorities.animations);
 14048      } else {
 14049        // manage the animation loop ourselves
 14050        headlessStep(); // first call
 14051      }
 14052    }
 14053  };
 14054  
 14055  var emitterOptions$1 = {
 14056    qualifierCompare: function qualifierCompare(selector1, selector2) {
 14057      if (selector1 == null || selector2 == null) {
 14058        return selector1 == null && selector2 == null;
 14059      } else {
 14060        return selector1.sameText(selector2);
 14061      }
 14062    },
 14063    eventMatches: function eventMatches(cy, listener, eventObj) {
 14064      var selector = listener.qualifier;
 14065  
 14066      if (selector != null) {
 14067        return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
 14068      }
 14069  
 14070      return true;
 14071    },
 14072    addEventFields: function addEventFields(cy, evt) {
 14073      evt.cy = cy;
 14074      evt.target = cy;
 14075    },
 14076    callbackContext: function callbackContext(cy, listener, eventObj) {
 14077      return listener.qualifier != null ? eventObj.target : cy;
 14078    }
 14079  };
 14080  
 14081  var argSelector$1 = function argSelector(arg) {
 14082    if (string(arg)) {
 14083      return new Selector(arg);
 14084    } else {
 14085      return arg;
 14086    }
 14087  };
 14088  
 14089  var elesfn$v = {
 14090    createEmitter: function createEmitter() {
 14091      var _p = this._private;
 14092  
 14093      if (!_p.emitter) {
 14094        _p.emitter = new Emitter(emitterOptions$1, this);
 14095      }
 14096  
 14097      return this;
 14098    },
 14099    emitter: function emitter() {
 14100      return this._private.emitter;
 14101    },
 14102    on: function on(events, selector, callback) {
 14103      this.emitter().on(events, argSelector$1(selector), callback);
 14104      return this;
 14105    },
 14106    removeListener: function removeListener(events, selector, callback) {
 14107      this.emitter().removeListener(events, argSelector$1(selector), callback);
 14108      return this;
 14109    },
 14110    removeAllListeners: function removeAllListeners() {
 14111      this.emitter().removeAllListeners();
 14112      return this;
 14113    },
 14114    one: function one(events, selector, callback) {
 14115      this.emitter().one(events, argSelector$1(selector), callback);
 14116      return this;
 14117    },
 14118    once: function once(events, selector, callback) {
 14119      this.emitter().one(events, argSelector$1(selector), callback);
 14120      return this;
 14121    },
 14122    emit: function emit(events, extraParams) {
 14123      this.emitter().emit(events, extraParams);
 14124      return this;
 14125    },
 14126    emitAndNotify: function emitAndNotify(event, eles) {
 14127      this.emit(event);
 14128      this.notify(event, eles);
 14129      return this;
 14130    }
 14131  };
 14132  define$3.eventAliasesOn(elesfn$v);
 14133  
 14134  var corefn$2 = {
 14135    png: function png(options) {
 14136      var renderer = this._private.renderer;
 14137      options = options || {};
 14138      return renderer.png(options);
 14139    },
 14140    jpg: function jpg(options) {
 14141      var renderer = this._private.renderer;
 14142      options = options || {};
 14143      options.bg = options.bg || '#fff';
 14144      return renderer.jpg(options);
 14145    }
 14146  };
 14147  corefn$2.jpeg = corefn$2.jpg;
 14148  
 14149  var corefn$3 = {
 14150    layout: function layout(options) {
 14151      var cy = this;
 14152  
 14153      if (options == null) {
 14154        error('Layout options must be specified to make a layout');
 14155        return;
 14156      }
 14157  
 14158      if (options.name == null) {
 14159        error('A `name` must be specified to make a layout');
 14160        return;
 14161      }
 14162  
 14163      var name = options.name;
 14164      var Layout = cy.extension('layout', name);
 14165  
 14166      if (Layout == null) {
 14167        error('No such layout `' + name + '` found.  Did you forget to import it and `cytoscape.use()` it?');
 14168        return;
 14169      }
 14170  
 14171      var eles;
 14172  
 14173      if (string(options.eles)) {
 14174        eles = cy.$(options.eles);
 14175      } else {
 14176        eles = options.eles != null ? options.eles : cy.$();
 14177      }
 14178  
 14179      var layout = new Layout(extend({}, options, {
 14180        cy: cy,
 14181        eles: eles
 14182      }));
 14183      return layout;
 14184    }
 14185  };
 14186  corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
 14187  
 14188  var corefn$4 = {
 14189    notify: function notify(eventName, eventEles) {
 14190      var _p = this._private;
 14191  
 14192      if (this.batching()) {
 14193        _p.batchNotifications = _p.batchNotifications || {};
 14194        var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
 14195  
 14196        if (eventEles != null) {
 14197          eles.merge(eventEles);
 14198        }
 14199  
 14200        return; // notifications are disabled during batching
 14201      }
 14202  
 14203      if (!_p.notificationsEnabled) {
 14204        return;
 14205      } // exit on disabled
 14206  
 14207  
 14208      var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
 14209  
 14210      if (this.destroyed() || !renderer) {
 14211        return;
 14212      }
 14213  
 14214      renderer.notify(eventName, eventEles);
 14215    },
 14216    notifications: function notifications(bool) {
 14217      var p = this._private;
 14218  
 14219      if (bool === undefined) {
 14220        return p.notificationsEnabled;
 14221      } else {
 14222        p.notificationsEnabled = bool ? true : false;
 14223      }
 14224  
 14225      return this;
 14226    },
 14227    noNotifications: function noNotifications(callback) {
 14228      this.notifications(false);
 14229      callback();
 14230      this.notifications(true);
 14231    },
 14232    batching: function batching() {
 14233      return this._private.batchCount > 0;
 14234    },
 14235    startBatch: function startBatch() {
 14236      var _p = this._private;
 14237  
 14238      if (_p.batchCount == null) {
 14239        _p.batchCount = 0;
 14240      }
 14241  
 14242      if (_p.batchCount === 0) {
 14243        _p.batchStyleEles = this.collection();
 14244        _p.batchNotifications = {};
 14245      }
 14246  
 14247      _p.batchCount++;
 14248      return this;
 14249    },
 14250    endBatch: function endBatch() {
 14251      var _p = this._private;
 14252  
 14253      if (_p.batchCount === 0) {
 14254        return this;
 14255      }
 14256  
 14257      _p.batchCount--;
 14258  
 14259      if (_p.batchCount === 0) {
 14260        // update style for dirty eles
 14261        _p.batchStyleEles.updateStyle();
 14262  
 14263        var renderer = this.renderer(); // notify the renderer of queued eles and event types
 14264  
 14265        Object.keys(_p.batchNotifications).forEach(function (eventName) {
 14266          var eles = _p.batchNotifications[eventName];
 14267  
 14268          if (eles.empty()) {
 14269            renderer.notify(eventName);
 14270          } else {
 14271            renderer.notify(eventName, eles);
 14272          }
 14273        });
 14274      }
 14275  
 14276      return this;
 14277    },
 14278    batch: function batch(callback) {
 14279      this.startBatch();
 14280      callback();
 14281      this.endBatch();
 14282      return this;
 14283    },
 14284    // for backwards compatibility
 14285    batchData: function batchData(map) {
 14286      var cy = this;
 14287      return this.batch(function () {
 14288        var ids = Object.keys(map);
 14289  
 14290        for (var i = 0; i < ids.length; i++) {
 14291          var id = ids[i];
 14292          var data = map[id];
 14293          var ele = cy.getElementById(id);
 14294          ele.data(data);
 14295        }
 14296      });
 14297    }
 14298  };
 14299  
 14300  var rendererDefaults = defaults({
 14301    hideEdgesOnViewport: false,
 14302    textureOnViewport: false,
 14303    motionBlur: false,
 14304    motionBlurOpacity: 0.05,
 14305    pixelRatio: undefined,
 14306    desktopTapThreshold: 4,
 14307    touchTapThreshold: 8,
 14308    wheelSensitivity: 1,
 14309    debug: false,
 14310    showFps: false
 14311  });
 14312  var corefn$5 = {
 14313    renderTo: function renderTo(context, zoom, pan, pxRatio) {
 14314      var r = this._private.renderer;
 14315      r.renderTo(context, zoom, pan, pxRatio);
 14316      return this;
 14317    },
 14318    renderer: function renderer() {
 14319      return this._private.renderer;
 14320    },
 14321    forceRender: function forceRender() {
 14322      this.notify('draw');
 14323      return this;
 14324    },
 14325    resize: function resize() {
 14326      this.invalidateSize();
 14327      this.emitAndNotify('resize');
 14328      return this;
 14329    },
 14330    initRenderer: function initRenderer(options) {
 14331      var cy = this;
 14332      var RendererProto = cy.extension('renderer', options.name);
 14333  
 14334      if (RendererProto == null) {
 14335        error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
 14336        return;
 14337      }
 14338  
 14339      if (options.wheelSensitivity !== undefined) {
 14340        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.");
 14341      }
 14342  
 14343      var rOpts = rendererDefaults(options);
 14344      rOpts.cy = cy;
 14345      cy._private.renderer = new RendererProto(rOpts);
 14346      this.notify('init');
 14347    },
 14348    destroyRenderer: function destroyRenderer() {
 14349      var cy = this;
 14350      cy.notify('destroy'); // destroy the renderer
 14351  
 14352      var domEle = cy.container();
 14353  
 14354      if (domEle) {
 14355        domEle._cyreg = null;
 14356  
 14357        while (domEle.childNodes.length > 0) {
 14358          domEle.removeChild(domEle.childNodes[0]);
 14359        }
 14360      }
 14361  
 14362      cy._private.renderer = null; // to be extra safe, remove the ref
 14363  
 14364      cy.mutableElements().forEach(function (ele) {
 14365        var _p = ele._private;
 14366        _p.rscratch = {};
 14367        _p.rstyle = {};
 14368        _p.animation.current = [];
 14369        _p.animation.queue = [];
 14370      });
 14371    },
 14372    onRender: function onRender(fn) {
 14373      return this.on('render', fn);
 14374    },
 14375    offRender: function offRender(fn) {
 14376      return this.off('render', fn);
 14377    }
 14378  };
 14379  corefn$5.invalidateDimensions = corefn$5.resize;
 14380  
 14381  var corefn$6 = {
 14382    // get a collection
 14383    // - empty collection on no args
 14384    // - collection of elements in the graph on selector arg
 14385    // - guarantee a returned collection when elements or collection specified
 14386    collection: function collection(eles, opts) {
 14387      if (string(eles)) {
 14388        return this.$(eles);
 14389      } else if (elementOrCollection(eles)) {
 14390        return eles.collection();
 14391      } else if (array(eles)) {
 14392        return new Collection(this, eles, opts);
 14393      }
 14394  
 14395      return new Collection(this);
 14396    },
 14397    nodes: function nodes(selector) {
 14398      var nodes = this.$(function (ele) {
 14399        return ele.isNode();
 14400      });
 14401  
 14402      if (selector) {
 14403        return nodes.filter(selector);
 14404      }
 14405  
 14406      return nodes;
 14407    },
 14408    edges: function edges(selector) {
 14409      var edges = this.$(function (ele) {
 14410        return ele.isEdge();
 14411      });
 14412  
 14413      if (selector) {
 14414        return edges.filter(selector);
 14415      }
 14416  
 14417      return edges;
 14418    },
 14419    // search the graph like jQuery
 14420    $: function $(selector) {
 14421      var eles = this._private.elements;
 14422  
 14423      if (selector) {
 14424        return eles.filter(selector);
 14425      } else {
 14426        return eles.spawnSelf();
 14427      }
 14428    },
 14429    mutableElements: function mutableElements() {
 14430      return this._private.elements;
 14431    }
 14432  }; // aliases
 14433  
 14434  corefn$6.elements = corefn$6.filter = corefn$6.$;
 14435  
 14436  var styfn = {}; // keys for style blocks, e.g. ttfftt
 14437  
 14438  var TRUE = 't';
 14439  var FALSE = 'f'; // (potentially expensive calculation)
 14440  // apply the style to the element based on
 14441  // - its bypass
 14442  // - what selectors match it
 14443  
 14444  styfn.apply = function (eles) {
 14445    var self = this;
 14446    var _p = self._private;
 14447    var cy = _p.cy;
 14448    var updatedEles = cy.collection();
 14449  
 14450    if (_p.newStyle) {
 14451      // clear style caches
 14452      _p.contextStyles = {};
 14453      _p.propDiffs = {};
 14454      self.cleanElements(eles, true);
 14455    }
 14456  
 14457    for (var ie = 0; ie < eles.length; ie++) {
 14458      var ele = eles[ie];
 14459      var cxtMeta = self.getContextMeta(ele);
 14460  
 14461      if (cxtMeta.empty) {
 14462        continue;
 14463      }
 14464  
 14465      var cxtStyle = self.getContextStyle(cxtMeta);
 14466      var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
 14467  
 14468      if (!_p.newStyle) {
 14469        self.updateTransitions(ele, app.diffProps);
 14470      }
 14471  
 14472      var hintsDiff = self.updateStyleHints(ele);
 14473  
 14474      if (hintsDiff) {
 14475        updatedEles.merge(ele);
 14476      }
 14477    } // for elements
 14478  
 14479  
 14480    _p.newStyle = false;
 14481    return updatedEles;
 14482  };
 14483  
 14484  styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
 14485    var self = this;
 14486    var cache = self._private.propDiffs = self._private.propDiffs || {};
 14487    var dualCxtKey = oldCxtKey + '-' + newCxtKey;
 14488    var cachedVal = cache[dualCxtKey];
 14489  
 14490    if (cachedVal) {
 14491      return cachedVal;
 14492    }
 14493  
 14494    var diffProps = [];
 14495    var addedProp = {};
 14496  
 14497    for (var i = 0; i < self.length; i++) {
 14498      var cxt = self[i];
 14499      var oldHasCxt = oldCxtKey[i] === TRUE;
 14500      var newHasCxt = newCxtKey[i] === TRUE;
 14501      var cxtHasDiffed = oldHasCxt !== newHasCxt;
 14502      var cxtHasMappedProps = cxt.mappedProperties.length > 0;
 14503  
 14504      if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
 14505        var props = void 0;
 14506  
 14507        if (cxtHasDiffed && cxtHasMappedProps) {
 14508          props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
 14509        } else if (cxtHasDiffed) {
 14510          props = cxt.properties; // need to check them all
 14511        } else if (cxtHasMappedProps) {
 14512          props = cxt.mappedProperties; // only need to check mapped
 14513        }
 14514  
 14515        for (var j = 0; j < props.length; j++) {
 14516          var prop = props[j];
 14517          var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
 14518          // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
 14519          // is cached)
 14520  
 14521          var laterCxtOverrides = false;
 14522  
 14523          for (var k = i + 1; k < self.length; k++) {
 14524            var laterCxt = self[k];
 14525            var hasLaterCxt = newCxtKey[k] === TRUE;
 14526  
 14527            if (!hasLaterCxt) {
 14528              continue;
 14529            } // can't override unless the context is active
 14530  
 14531  
 14532            laterCxtOverrides = laterCxt.properties[prop.name] != null;
 14533  
 14534            if (laterCxtOverrides) {
 14535              break;
 14536            } // exit early as long as one later context overrides
 14537  
 14538          }
 14539  
 14540          if (!addedProp[name] && !laterCxtOverrides) {
 14541            addedProp[name] = true;
 14542            diffProps.push(name);
 14543          }
 14544        } // for props
 14545  
 14546      } // if
 14547  
 14548    } // for contexts
 14549  
 14550  
 14551    cache[dualCxtKey] = diffProps;
 14552    return diffProps;
 14553  };
 14554  
 14555  styfn.getContextMeta = function (ele) {
 14556    var self = this;
 14557    var cxtKey = '';
 14558    var diffProps;
 14559    var prevKey = ele._private.styleCxtKey || '';
 14560  
 14561    if (self._private.newStyle) {
 14562      prevKey = ''; // since we need to apply all style if a fresh stylesheet
 14563    } // get the cxt key
 14564  
 14565  
 14566    for (var i = 0; i < self.length; i++) {
 14567      var context = self[i];
 14568      var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
 14569  
 14570      if (contextSelectorMatches) {
 14571        cxtKey += TRUE;
 14572      } else {
 14573        cxtKey += FALSE;
 14574      }
 14575    } // for context
 14576  
 14577  
 14578    diffProps = self.getPropertiesDiff(prevKey, cxtKey);
 14579    ele._private.styleCxtKey = cxtKey;
 14580    return {
 14581      key: cxtKey,
 14582      diffPropNames: diffProps,
 14583      empty: diffProps.length === 0
 14584    };
 14585  }; // gets a computed ele style object based on matched contexts
 14586  
 14587  
 14588  styfn.getContextStyle = function (cxtMeta) {
 14589    var cxtKey = cxtMeta.key;
 14590    var self = this;
 14591    var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
 14592  
 14593    if (cxtStyles[cxtKey]) {
 14594      return cxtStyles[cxtKey];
 14595    }
 14596  
 14597    var style = {
 14598      _private: {
 14599        key: cxtKey
 14600      }
 14601    };
 14602  
 14603    for (var i = 0; i < self.length; i++) {
 14604      var cxt = self[i];
 14605      var hasCxt = cxtKey[i] === TRUE;
 14606  
 14607      if (!hasCxt) {
 14608        continue;
 14609      }
 14610  
 14611      for (var j = 0; j < cxt.properties.length; j++) {
 14612        var prop = cxt.properties[j];
 14613        style[prop.name] = prop;
 14614      }
 14615    }
 14616  
 14617    cxtStyles[cxtKey] = style;
 14618    return style;
 14619  };
 14620  
 14621  styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
 14622    var self = this;
 14623    var diffProps = cxtMeta.diffPropNames;
 14624    var retDiffProps = {};
 14625    var types = self.types;
 14626  
 14627    for (var i = 0; i < diffProps.length; i++) {
 14628      var diffPropName = diffProps[i];
 14629      var cxtProp = cxtStyle[diffPropName];
 14630      var eleProp = ele.pstyle(diffPropName);
 14631  
 14632      if (!cxtProp) {
 14633        // no context prop means delete
 14634        if (!eleProp) {
 14635          continue; // no existing prop means nothing needs to be removed
 14636          // nb affects initial application on mapped values like control-point-distances
 14637        } else if (eleProp.bypass) {
 14638          cxtProp = {
 14639            name: diffPropName,
 14640            deleteBypassed: true
 14641          };
 14642        } else {
 14643          cxtProp = {
 14644            name: diffPropName,
 14645            "delete": true
 14646          };
 14647        }
 14648      } // save cycles when the context prop doesn't need to be applied
 14649  
 14650  
 14651      if (eleProp === cxtProp) {
 14652        continue;
 14653      } // save cycles when a mapped context prop doesn't need to be applied
 14654  
 14655  
 14656      if (cxtProp.mapped === types.fn // context prop is function mapper
 14657      && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
 14658      && eleProp.mapping != null // ele prop is a concrete value from from a mapper
 14659      && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
 14660      ) {
 14661          // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
 14662          var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
 14663  
 14664          var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
 14665  
 14666          if (fnValue === mapping.prevFnValue) {
 14667            continue;
 14668          }
 14669        }
 14670  
 14671      var retDiffProp = retDiffProps[diffPropName] = {
 14672        prev: eleProp
 14673      };
 14674      self.applyParsedProperty(ele, cxtProp);
 14675      retDiffProp.next = ele.pstyle(diffPropName);
 14676  
 14677      if (retDiffProp.next && retDiffProp.next.bypass) {
 14678        retDiffProp.next = retDiffProp.next.bypassed;
 14679      }
 14680    }
 14681  
 14682    return {
 14683      diffProps: retDiffProps
 14684    };
 14685  };
 14686  
 14687  styfn.updateStyleHints = function (ele) {
 14688    var _p = ele._private;
 14689    var self = this;
 14690    var propNames = self.propertyGroupNames;
 14691    var propGrKeys = self.propertyGroupKeys;
 14692  
 14693    var propHash = function propHash(ele, propNames, seedKey) {
 14694      return self.getPropertiesHash(ele, propNames, seedKey);
 14695    };
 14696  
 14697    var oldStyleKey = _p.styleKey;
 14698  
 14699    if (ele.removed()) {
 14700      return false;
 14701    }
 14702  
 14703    var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
 14704    // but lazily -- only use non-default prop values to reduce the number of hashes
 14705    //
 14706  
 14707    var overriddenStyles = ele._private.style;
 14708    propNames = Object.keys(overriddenStyles);
 14709  
 14710    for (var i = 0; i < propGrKeys.length; i++) {
 14711      var grKey = propGrKeys[i];
 14712      _p.styleKeys[grKey] = 0;
 14713    }
 14714  
 14715    var updateGrKey = function updateGrKey(val, grKey) {
 14716      return _p.styleKeys[grKey] = hashInt(val, _p.styleKeys[grKey]);
 14717    };
 14718  
 14719    var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
 14720      for (var j = 0; j < strVal.length; j++) {
 14721        updateGrKey(strVal.charCodeAt(j), grKey);
 14722      }
 14723    }; // - hashing works on 32 bit ints b/c we use bitwise ops
 14724    // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
 14725    // - raise up small numbers so more significant digits are seen by hashing
 14726    // - make small numbers larger than a normal value to avoid collisions
 14727    // - works in practice and it's relatively cheap
 14728  
 14729  
 14730    var N = 2000000000;
 14731  
 14732    var cleanNum = function cleanNum(val) {
 14733      return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
 14734    };
 14735  
 14736    for (var _i = 0; _i < propNames.length; _i++) {
 14737      var name = propNames[_i];
 14738      var parsedProp = overriddenStyles[name];
 14739  
 14740      if (parsedProp == null) {
 14741        continue;
 14742      }
 14743  
 14744      var propInfo = this.properties[name];
 14745      var type = propInfo.type;
 14746      var _grKey = propInfo.groupKey;
 14747      var normalizedNumberVal = void 0;
 14748  
 14749      if (propInfo.hashOverride != null) {
 14750        normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
 14751      } else if (parsedProp.pfValue != null) {
 14752        normalizedNumberVal = parsedProp.pfValue;
 14753      } // might not be a number if it allows enums
 14754  
 14755  
 14756      var numberVal = propInfo.enums == null ? parsedProp.value : null;
 14757      var haveNormNum = normalizedNumberVal != null;
 14758      var haveUnitedNum = numberVal != null;
 14759      var haveNum = haveNormNum || haveUnitedNum;
 14760      var units = parsedProp.units; // numbers are cheaper to hash than strings
 14761      // 1 hash op vs n hash ops (for length n string)
 14762  
 14763      if (type.number && haveNum) {
 14764        var v = haveNormNum ? normalizedNumberVal : numberVal;
 14765  
 14766        if (type.multiple) {
 14767          for (var _i2 = 0; _i2 < v.length; _i2++) {
 14768            updateGrKey(cleanNum(v[_i2]), _grKey);
 14769          }
 14770        } else {
 14771          updateGrKey(cleanNum(v), _grKey);
 14772        }
 14773  
 14774        if (!haveNormNum && units != null) {
 14775          updateGrKeyWStr(units, _grKey);
 14776        }
 14777      } else {
 14778        updateGrKeyWStr(parsedProp.strValue, _grKey);
 14779      }
 14780    } // overall style key
 14781    //
 14782  
 14783  
 14784    var hash = 0;
 14785  
 14786    for (var _i3 = 0; _i3 < propGrKeys.length; _i3++) {
 14787      var _grKey2 = propGrKeys[_i3];
 14788      var grHash = _p.styleKeys[_grKey2];
 14789      hash = hashInt(grHash, hash);
 14790    }
 14791  
 14792    _p.styleKey = hash; // label dims
 14793    //
 14794  
 14795    var labelDimsKey = _p.labelDimsKey = _p.styleKeys.labelDimensions;
 14796    _p.labelKey = propHash(ele, ['label'], labelDimsKey);
 14797    _p.labelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.labelKey);
 14798  
 14799    if (!isNode) {
 14800      _p.sourceLabelKey = propHash(ele, ['source-label'], labelDimsKey);
 14801      _p.sourceLabelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.sourceLabelKey);
 14802      _p.targetLabelKey = propHash(ele, ['target-label'], labelDimsKey);
 14803      _p.targetLabelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.targetLabelKey);
 14804    } // node
 14805    //
 14806  
 14807  
 14808    if (isNode) {
 14809      var _p$styleKeys = _p.styleKeys,
 14810          nodeBody = _p$styleKeys.nodeBody,
 14811          nodeBorder = _p$styleKeys.nodeBorder,
 14812          backgroundImage = _p$styleKeys.backgroundImage,
 14813          compound = _p$styleKeys.compound,
 14814          pie = _p$styleKeys.pie;
 14815      _p.nodeKey = hashIntsArray([nodeBorder, backgroundImage, compound, pie], nodeBody);
 14816      _p.hasPie = pie != 0;
 14817    }
 14818  
 14819    return oldStyleKey !== _p.styleKey;
 14820  };
 14821  
 14822  styfn.clearStyleHints = function (ele) {
 14823    var _p = ele._private;
 14824    _p.styleKeys = {};
 14825    _p.styleKey = null;
 14826    _p.labelKey = null;
 14827    _p.labelStyleKey = null;
 14828    _p.sourceLabelKey = null;
 14829    _p.sourceLabelStyleKey = null;
 14830    _p.targetLabelKey = null;
 14831    _p.targetLabelStyleKey = null;
 14832    _p.nodeKey = null;
 14833    _p.hasPie = null;
 14834  }; // apply a property to the style (for internal use)
 14835  // returns whether application was successful
 14836  //
 14837  // now, this function flattens the property, and here's how:
 14838  //
 14839  // for parsedProp:{ bypass: true, deleteBypass: true }
 14840  // no property is generated, instead the bypass property in the
 14841  // element's style is replaced by what's pointed to by the `bypassed`
 14842  // field in the bypass property (i.e. restoring the property the
 14843  // bypass was overriding)
 14844  //
 14845  // for parsedProp:{ mapped: truthy }
 14846  // the generated flattenedProp:{ mapping: prop }
 14847  //
 14848  // for parsedProp:{ bypass: true }
 14849  // the generated flattenedProp:{ bypassed: parsedProp }
 14850  
 14851  
 14852  styfn.applyParsedProperty = function (ele, parsedProp) {
 14853    var self = this;
 14854    var prop = parsedProp;
 14855    var style = ele._private.style;
 14856    var flatProp;
 14857    var types = self.types;
 14858    var type = self.properties[prop.name].type;
 14859    var propIsBypass = prop.bypass;
 14860    var origProp = style[prop.name];
 14861    var origPropIsBypass = origProp && origProp.bypass;
 14862    var _p = ele._private;
 14863    var flatPropMapping = 'mapping';
 14864  
 14865    var getVal = function getVal(p) {
 14866      if (p == null) {
 14867        return null;
 14868      } else if (p.pfValue != null) {
 14869        return p.pfValue;
 14870      } else {
 14871        return p.value;
 14872      }
 14873    };
 14874  
 14875    var checkTriggers = function checkTriggers() {
 14876      var fromVal = getVal(origProp);
 14877      var toVal = getVal(prop);
 14878      self.checkTriggers(ele, prop.name, fromVal, toVal);
 14879    }; // edge sanity checks to prevent the client from making serious mistakes
 14880  
 14881  
 14882    if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
 14883    parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
 14884    parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
 14885      prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
 14886    }
 14887  
 14888    if (prop["delete"]) {
 14889      // delete the property and use the default value on falsey value
 14890      style[prop.name] = undefined;
 14891      checkTriggers();
 14892      return true;
 14893    }
 14894  
 14895    if (prop.deleteBypassed) {
 14896      // delete the property that the
 14897      if (!origProp) {
 14898        checkTriggers();
 14899        return true; // can't delete if no prop
 14900      } else if (origProp.bypass) {
 14901        // delete bypassed
 14902        origProp.bypassed = undefined;
 14903        checkTriggers();
 14904        return true;
 14905      } else {
 14906        return false; // we're unsuccessful deleting the bypassed
 14907      }
 14908    } // check if we need to delete the current bypass
 14909  
 14910  
 14911    if (prop.deleteBypass) {
 14912      // then this property is just here to indicate we need to delete
 14913      if (!origProp) {
 14914        checkTriggers();
 14915        return true; // property is already not defined
 14916      } else if (origProp.bypass) {
 14917        // then replace the bypass property with the original
 14918        // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
 14919        style[prop.name] = origProp.bypassed;
 14920        checkTriggers();
 14921        return true;
 14922      } else {
 14923        return false; // we're unsuccessful deleting the bypass
 14924      }
 14925    }
 14926  
 14927    var printMappingErr = function printMappingErr() {
 14928      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');
 14929    }; // put the property in the style objects
 14930  
 14931  
 14932    switch (prop.mapped) {
 14933      // flatten the property if mapped
 14934      case types.mapData:
 14935        {
 14936          // flatten the field (e.g. data.foo.bar)
 14937          var fields = prop.field.split('.');
 14938          var fieldVal = _p.data;
 14939  
 14940          for (var i = 0; i < fields.length && fieldVal; i++) {
 14941            var field = fields[i];
 14942            fieldVal = fieldVal[field];
 14943          }
 14944  
 14945          if (fieldVal == null) {
 14946            printMappingErr();
 14947            return false;
 14948          }
 14949  
 14950          var percent;
 14951  
 14952          if (!number(fieldVal)) {
 14953            // then don't apply and fall back on the existing style
 14954            warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
 14955            return false;
 14956          } else {
 14957            var fieldWidth = prop.fieldMax - prop.fieldMin;
 14958  
 14959            if (fieldWidth === 0) {
 14960              // safety check -- not strictly necessary as no props of zero range should be passed here
 14961              percent = 0;
 14962            } else {
 14963              percent = (fieldVal - prop.fieldMin) / fieldWidth;
 14964            }
 14965          } // make sure to bound percent value
 14966  
 14967  
 14968          if (percent < 0) {
 14969            percent = 0;
 14970          } else if (percent > 1) {
 14971            percent = 1;
 14972          }
 14973  
 14974          if (type.color) {
 14975            var r1 = prop.valueMin[0];
 14976            var r2 = prop.valueMax[0];
 14977            var g1 = prop.valueMin[1];
 14978            var g2 = prop.valueMax[1];
 14979            var b1 = prop.valueMin[2];
 14980            var b2 = prop.valueMax[2];
 14981            var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
 14982            var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
 14983            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)];
 14984            flatProp = {
 14985              // colours are simple, so just create the flat property instead of expensive string parsing
 14986              bypass: prop.bypass,
 14987              // we're a bypass if the mapping property is a bypass
 14988              name: prop.name,
 14989              value: clr,
 14990              strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
 14991            };
 14992          } else if (type.number) {
 14993            var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
 14994            flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
 14995          } else {
 14996            return false; // can only map to colours and numbers
 14997          }
 14998  
 14999          if (!flatProp) {
 15000            // if we can't flatten the property, then don't apply the property and fall back on the existing style
 15001            printMappingErr();
 15002            return false;
 15003          }
 15004  
 15005          flatProp.mapping = prop; // keep a reference to the mapping
 15006  
 15007          prop = flatProp; // the flattened (mapped) property is the one we want
 15008  
 15009          break;
 15010        }
 15011      // direct mapping
 15012  
 15013      case types.data:
 15014        {
 15015          // flatten the field (e.g. data.foo.bar)
 15016          var _fields = prop.field.split('.');
 15017  
 15018          var _fieldVal = _p.data;
 15019  
 15020          for (var _i4 = 0; _i4 < _fields.length && _fieldVal; _i4++) {
 15021            var _field = _fields[_i4];
 15022            _fieldVal = _fieldVal[_field];
 15023          }
 15024  
 15025          if (_fieldVal != null) {
 15026            flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
 15027          }
 15028  
 15029          if (!flatProp) {
 15030            // if we can't flatten the property, then don't apply and fall back on the existing style
 15031            printMappingErr();
 15032            return false;
 15033          }
 15034  
 15035          flatProp.mapping = prop; // keep a reference to the mapping
 15036  
 15037          prop = flatProp; // the flattened (mapped) property is the one we want
 15038  
 15039          break;
 15040        }
 15041  
 15042      case types.fn:
 15043        {
 15044          var fn = prop.value;
 15045          var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
 15046  
 15047          prop.prevFnValue = fnRetVal;
 15048  
 15049          if (fnRetVal == null) {
 15050            warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
 15051            return false;
 15052          }
 15053  
 15054          flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
 15055  
 15056          if (!flatProp) {
 15057            warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
 15058            return false;
 15059          }
 15060  
 15061          flatProp.mapping = copy(prop); // keep a reference to the mapping
 15062  
 15063          prop = flatProp; // the flattened (mapped) property is the one we want
 15064  
 15065          break;
 15066        }
 15067  
 15068      case undefined:
 15069        break;
 15070      // just set the property
 15071  
 15072      default:
 15073        return false;
 15074      // not a valid mapping
 15075    } // if the property is a bypass property, then link the resultant property to the original one
 15076  
 15077  
 15078    if (propIsBypass) {
 15079      if (origPropIsBypass) {
 15080        // then this bypass overrides the existing one
 15081        prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
 15082      } else {
 15083        // then link the orig prop to the new bypass
 15084        prop.bypassed = origProp;
 15085      }
 15086  
 15087      style[prop.name] = prop; // and set
 15088    } else {
 15089      // prop is not bypass
 15090      if (origPropIsBypass) {
 15091        // then keep the orig prop (since it's a bypass) and link to the new prop
 15092        origProp.bypassed = prop;
 15093      } else {
 15094        // then just replace the old prop with the new one
 15095        style[prop.name] = prop;
 15096      }
 15097    }
 15098  
 15099    checkTriggers();
 15100    return true;
 15101  };
 15102  
 15103  styfn.cleanElements = function (eles, keepBypasses) {
 15104    for (var i = 0; i < eles.length; i++) {
 15105      var ele = eles[i];
 15106      this.clearStyleHints(ele);
 15107      ele.dirtyCompoundBoundsCache();
 15108      ele.dirtyBoundingBoxCache();
 15109  
 15110      if (!keepBypasses) {
 15111        ele._private.style = {};
 15112      } else {
 15113        var style = ele._private.style;
 15114        var propNames = Object.keys(style);
 15115  
 15116        for (var j = 0; j < propNames.length; j++) {
 15117          var propName = propNames[j];
 15118          var eleProp = style[propName];
 15119  
 15120          if (eleProp != null) {
 15121            if (eleProp.bypass) {
 15122              eleProp.bypassed = null;
 15123            } else {
 15124              style[propName] = null;
 15125            }
 15126          }
 15127        }
 15128      }
 15129    }
 15130  }; // updates the visual style for all elements (useful for manual style modification after init)
 15131  
 15132  
 15133  styfn.update = function () {
 15134    var cy = this._private.cy;
 15135    var eles = cy.mutableElements();
 15136    eles.updateStyle();
 15137  }; // diffProps : { name => { prev, next } }
 15138  
 15139  
 15140  styfn.updateTransitions = function (ele, diffProps) {
 15141    var self = this;
 15142    var _p = ele._private;
 15143    var props = ele.pstyle('transition-property').value;
 15144    var duration = ele.pstyle('transition-duration').pfValue;
 15145    var delay = ele.pstyle('transition-delay').pfValue;
 15146  
 15147    if (props.length > 0 && duration > 0) {
 15148      var style = {}; // build up the style to animate towards
 15149  
 15150      var anyPrev = false;
 15151  
 15152      for (var i = 0; i < props.length; i++) {
 15153        var prop = props[i];
 15154        var styProp = ele.pstyle(prop);
 15155        var diffProp = diffProps[prop];
 15156  
 15157        if (!diffProp) {
 15158          continue;
 15159        }
 15160  
 15161        var prevProp = diffProp.prev;
 15162        var fromProp = prevProp;
 15163        var toProp = diffProp.next != null ? diffProp.next : styProp;
 15164        var diff = false;
 15165        var initVal = void 0;
 15166        var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
 15167  
 15168        if (!fromProp) {
 15169          continue;
 15170        } // consider px values
 15171  
 15172  
 15173        if (number(fromProp.pfValue) && number(toProp.pfValue)) {
 15174          diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
 15175  
 15176          initVal = fromProp.pfValue + initDt * diff; // consider numerical values
 15177        } else if (number(fromProp.value) && number(toProp.value)) {
 15178          diff = toProp.value - fromProp.value; // nonzero is truthy
 15179  
 15180          initVal = fromProp.value + initDt * diff; // consider colour values
 15181        } else if (array(fromProp.value) && array(toProp.value)) {
 15182          diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
 15183          initVal = fromProp.strValue;
 15184        } // the previous value is good for an animation only if it's different
 15185  
 15186  
 15187        if (diff) {
 15188          style[prop] = toProp.strValue; // to val
 15189  
 15190          this.applyBypass(ele, prop, initVal); // from val
 15191  
 15192          anyPrev = true;
 15193        }
 15194      } // end if props allow ani
 15195      // can't transition if there's nothing previous to transition from
 15196  
 15197  
 15198      if (!anyPrev) {
 15199        return;
 15200      }
 15201  
 15202      _p.transitioning = true;
 15203      new Promise$1(function (resolve) {
 15204        if (delay > 0) {
 15205          ele.delayAnimation(delay).play().promise().then(resolve);
 15206        } else {
 15207          resolve();
 15208        }
 15209      }).then(function () {
 15210        return ele.animation({
 15211          style: style,
 15212          duration: duration,
 15213          easing: ele.pstyle('transition-timing-function').value,
 15214          queue: false
 15215        }).play().promise();
 15216      }).then(function () {
 15217        // if( !isBypass ){
 15218        self.removeBypasses(ele, props);
 15219        ele.emitAndNotify('style'); // }
 15220  
 15221        _p.transitioning = false;
 15222      });
 15223    } else if (_p.transitioning) {
 15224      this.removeBypasses(ele, props);
 15225      ele.emitAndNotify('style');
 15226      _p.transitioning = false;
 15227    }
 15228  };
 15229  
 15230  styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
 15231    var prop = this.properties[name];
 15232    var triggerCheck = getTrigger(prop);
 15233  
 15234    if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
 15235      onTrigger(prop);
 15236    }
 15237  };
 15238  
 15239  styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
 15240    var _this = this;
 15241  
 15242    this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
 15243      return prop.triggersZOrder;
 15244    }, function () {
 15245      _this._private.cy.notify('zorder', ele);
 15246    });
 15247  };
 15248  
 15249  styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
 15250    this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
 15251      return prop.triggersBounds;
 15252    }, function (prop) {
 15253      ele.dirtyCompoundBoundsCache();
 15254      ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
 15255      // then dirty the pll edge bb cache as well
 15256  
 15257      if ( // only for beziers -- so performance of other edges isn't affected
 15258      (ele.pstyle('curve-style').value === 'bezier' // already a bezier
 15259      // was just now changed to or from a bezier:
 15260      || name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier')) && prop.triggersBoundsOfParallelBeziers) {
 15261        ele.parallelEdges().forEach(function (pllEdge) {
 15262          if (pllEdge.isBundledBezier()) {
 15263            pllEdge.dirtyBoundingBoxCache();
 15264          }
 15265        });
 15266      }
 15267    });
 15268  };
 15269  
 15270  styfn.checkTriggers = function (ele, name, fromValue, toValue) {
 15271    ele.dirtyStyleCache();
 15272    this.checkZOrderTrigger(ele, name, fromValue, toValue);
 15273    this.checkBoundsTrigger(ele, name, fromValue, toValue);
 15274  };
 15275  
 15276  var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
 15277  // returns true iff application was successful for at least 1 specified property
 15278  
 15279  styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
 15280    var self = this;
 15281    var props = [];
 15282    var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
 15283  
 15284    if (name === '*' || name === '**') {
 15285      // apply to all property names
 15286      if (value !== undefined) {
 15287        for (var i = 0; i < self.properties.length; i++) {
 15288          var prop = self.properties[i];
 15289          var _name = prop.name;
 15290          var parsedProp = this.parse(_name, value, true);
 15291  
 15292          if (parsedProp) {
 15293            props.push(parsedProp);
 15294          }
 15295        }
 15296      }
 15297    } else if (string(name)) {
 15298      // then parse the single property
 15299      var _parsedProp = this.parse(name, value, true);
 15300  
 15301      if (_parsedProp) {
 15302        props.push(_parsedProp);
 15303      }
 15304    } else if (plainObject(name)) {
 15305      // then parse each property
 15306      var specifiedProps = name;
 15307      updateTransitions = value;
 15308      var names = Object.keys(specifiedProps);
 15309  
 15310      for (var _i = 0; _i < names.length; _i++) {
 15311        var _name2 = names[_i];
 15312        var _value = specifiedProps[_name2];
 15313  
 15314        if (_value === undefined) {
 15315          // try camel case name too
 15316          _value = specifiedProps[dash2camel(_name2)];
 15317        }
 15318  
 15319        if (_value !== undefined) {
 15320          var _parsedProp2 = this.parse(_name2, _value, true);
 15321  
 15322          if (_parsedProp2) {
 15323            props.push(_parsedProp2);
 15324          }
 15325        }
 15326      }
 15327    } else {
 15328      // can't do anything without well defined properties
 15329      return false;
 15330    } // we've failed if there are no valid properties
 15331  
 15332  
 15333    if (props.length === 0) {
 15334      return false;
 15335    } // now, apply the bypass properties on the elements
 15336  
 15337  
 15338    var ret = false; // return true if at least one succesful bypass applied
 15339  
 15340    for (var _i2 = 0; _i2 < eles.length; _i2++) {
 15341      // for each ele
 15342      var ele = eles[_i2];
 15343      var diffProps = {};
 15344      var diffProp = void 0;
 15345  
 15346      for (var j = 0; j < props.length; j++) {
 15347        // for each prop
 15348        var _prop = props[j];
 15349  
 15350        if (updateTransitions) {
 15351          var prevProp = ele.pstyle(_prop.name);
 15352          diffProp = diffProps[_prop.name] = {
 15353            prev: prevProp
 15354          };
 15355        }
 15356  
 15357        ret = this.applyParsedProperty(ele, _prop) || ret;
 15358  
 15359        if (updateTransitions) {
 15360          diffProp.next = ele.pstyle(_prop.name);
 15361        }
 15362      } // for props
 15363  
 15364  
 15365      if (ret) {
 15366        this.updateStyleHints(ele);
 15367      }
 15368  
 15369      if (updateTransitions) {
 15370        this.updateTransitions(ele, diffProps, isBypass);
 15371      }
 15372    } // for eles
 15373  
 15374  
 15375    return ret;
 15376  }; // only useful in specific cases like animation
 15377  
 15378  
 15379  styfn$1.overrideBypass = function (eles, name, value) {
 15380    name = camel2dash(name);
 15381  
 15382    for (var i = 0; i < eles.length; i++) {
 15383      var ele = eles[i];
 15384      var prop = ele._private.style[name];
 15385      var type = this.properties[name].type;
 15386      var isColor = type.color;
 15387      var isMulti = type.mutiple;
 15388      var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
 15389  
 15390      if (!prop || !prop.bypass) {
 15391        // need a bypass if one doesn't exist
 15392        this.applyBypass(ele, name, value);
 15393      } else {
 15394        prop.value = value;
 15395  
 15396        if (prop.pfValue != null) {
 15397          prop.pfValue = value;
 15398        }
 15399  
 15400        if (isColor) {
 15401          prop.strValue = 'rgb(' + value.join(',') + ')';
 15402        } else if (isMulti) {
 15403          prop.strValue = value.join(' ');
 15404        } else {
 15405          prop.strValue = '' + value;
 15406        }
 15407  
 15408        this.updateStyleHints(ele);
 15409      }
 15410  
 15411      this.checkTriggers(ele, name, oldValue, value);
 15412    }
 15413  };
 15414  
 15415  styfn$1.removeAllBypasses = function (eles, updateTransitions) {
 15416    return this.removeBypasses(eles, this.propertyNames, updateTransitions);
 15417  };
 15418  
 15419  styfn$1.removeBypasses = function (eles, props, updateTransitions) {
 15420    var isBypass = true;
 15421  
 15422    for (var j = 0; j < eles.length; j++) {
 15423      var ele = eles[j];
 15424      var diffProps = {};
 15425  
 15426      for (var i = 0; i < props.length; i++) {
 15427        var name = props[i];
 15428        var prop = this.properties[name];
 15429        var prevProp = ele.pstyle(prop.name);
 15430  
 15431        if (!prevProp || !prevProp.bypass) {
 15432          // if a bypass doesn't exist for the prop, nothing needs to be removed
 15433          continue;
 15434        }
 15435  
 15436        var value = ''; // empty => remove bypass
 15437  
 15438        var parsedProp = this.parse(name, value, true);
 15439        var diffProp = diffProps[prop.name] = {
 15440          prev: prevProp
 15441        };
 15442        this.applyParsedProperty(ele, parsedProp);
 15443        diffProp.next = ele.pstyle(prop.name);
 15444      } // for props
 15445  
 15446  
 15447      this.updateStyleHints(ele);
 15448  
 15449      if (updateTransitions) {
 15450        this.updateTransitions(ele, diffProps, isBypass);
 15451      }
 15452    } // for eles
 15453  
 15454  };
 15455  
 15456  var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
 15457  
 15458  styfn$2.getEmSizeInPixels = function () {
 15459    var px = this.containerCss('font-size');
 15460  
 15461    if (px != null) {
 15462      return parseFloat(px);
 15463    } else {
 15464      return 1; // for headless
 15465    }
 15466  }; // gets css property from the core container
 15467  
 15468  
 15469  styfn$2.containerCss = function (propName) {
 15470    var cy = this._private.cy;
 15471    var domElement = cy.container();
 15472  
 15473    if (window$1 && domElement && window$1.getComputedStyle) {
 15474      return window$1.getComputedStyle(domElement).getPropertyValue(propName);
 15475    }
 15476  };
 15477  
 15478  var styfn$3 = {}; // gets the rendered style for an element
 15479  
 15480  styfn$3.getRenderedStyle = function (ele, prop) {
 15481    if (prop) {
 15482      return this.getStylePropertyValue(ele, prop, true);
 15483    } else {
 15484      return this.getRawStyle(ele, true);
 15485    }
 15486  }; // gets the raw style for an element
 15487  
 15488  
 15489  styfn$3.getRawStyle = function (ele, isRenderedVal) {
 15490    var self = this;
 15491    ele = ele[0]; // insure it's an element
 15492  
 15493    if (ele) {
 15494      var rstyle = {};
 15495  
 15496      for (var i = 0; i < self.properties.length; i++) {
 15497        var prop = self.properties[i];
 15498        var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
 15499  
 15500        if (val != null) {
 15501          rstyle[prop.name] = val;
 15502          rstyle[dash2camel(prop.name)] = val;
 15503        }
 15504      }
 15505  
 15506      return rstyle;
 15507    }
 15508  };
 15509  
 15510  styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
 15511    var pstyle = ele.pstyle(property)[subproperty][index];
 15512    return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
 15513  };
 15514  
 15515  styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
 15516    var self = this;
 15517    ele = ele[0]; // insure it's an element
 15518  
 15519    if (ele) {
 15520      var prop = self.properties[propName];
 15521  
 15522      if (prop.alias) {
 15523        prop = prop.pointsTo;
 15524      }
 15525  
 15526      var type = prop.type;
 15527      var styleProp = ele.pstyle(prop.name);
 15528  
 15529      if (styleProp) {
 15530        var value = styleProp.value,
 15531            units = styleProp.units,
 15532            strValue = styleProp.strValue;
 15533  
 15534        if (isRenderedVal && type.number && value != null && number(value)) {
 15535          var zoom = ele.cy().zoom();
 15536  
 15537          var getRenderedValue = function getRenderedValue(val) {
 15538            return val * zoom;
 15539          };
 15540  
 15541          var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
 15542            return getRenderedValue(val) + units;
 15543          };
 15544  
 15545          var isArrayValue = array(value);
 15546          var haveUnits = isArrayValue ? units.every(function (u) {
 15547            return u != null;
 15548          }) : units != null;
 15549  
 15550          if (haveUnits) {
 15551            if (isArrayValue) {
 15552              return value.map(function (v, i) {
 15553                return getValueStringWithUnits(v, units[i]);
 15554              }).join(' ');
 15555            } else {
 15556              return getValueStringWithUnits(value, units);
 15557            }
 15558          } else {
 15559            if (isArrayValue) {
 15560              return value.map(function (v) {
 15561                return string(v) ? v : '' + getRenderedValue(v);
 15562              }).join(' ');
 15563            } else {
 15564              return '' + getRenderedValue(value);
 15565            }
 15566          }
 15567        } else if (strValue != null) {
 15568          return strValue;
 15569        }
 15570      }
 15571  
 15572      return null;
 15573    }
 15574  };
 15575  
 15576  styfn$3.getAnimationStartStyle = function (ele, aniProps) {
 15577    var rstyle = {};
 15578  
 15579    for (var i = 0; i < aniProps.length; i++) {
 15580      var aniProp = aniProps[i];
 15581      var name = aniProp.name;
 15582      var styleProp = ele.pstyle(name);
 15583  
 15584      if (styleProp !== undefined) {
 15585        // then make a prop of it
 15586        if (plainObject(styleProp)) {
 15587          styleProp = this.parse(name, styleProp.strValue);
 15588        } else {
 15589          styleProp = this.parse(name, styleProp);
 15590        }
 15591      }
 15592  
 15593      if (styleProp) {
 15594        rstyle[name] = styleProp;
 15595      }
 15596    }
 15597  
 15598    return rstyle;
 15599  };
 15600  
 15601  styfn$3.getPropsList = function (propsObj) {
 15602    var self = this;
 15603    var rstyle = [];
 15604    var style = propsObj;
 15605    var props = self.properties;
 15606  
 15607    if (style) {
 15608      var names = Object.keys(style);
 15609  
 15610      for (var i = 0; i < names.length; i++) {
 15611        var name = names[i];
 15612        var val = style[name];
 15613        var prop = props[name] || props[camel2dash(name)];
 15614        var styleProp = this.parse(prop.name, val);
 15615  
 15616        if (styleProp) {
 15617          rstyle.push(styleProp);
 15618        }
 15619      }
 15620    }
 15621  
 15622    return rstyle;
 15623  };
 15624  
 15625  styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
 15626    var hash = seed;
 15627    var name, val, strVal, chVal;
 15628    var i, j;
 15629  
 15630    for (i = 0; i < propNames.length; i++) {
 15631      name = propNames[i];
 15632      val = ele.pstyle(name, false);
 15633  
 15634      if (val == null) {
 15635        continue;
 15636      } else if (val.pfValue != null) {
 15637        hash = hashInt(chVal, hash);
 15638      } else {
 15639        strVal = val.strValue;
 15640  
 15641        for (j = 0; j < strVal.length; j++) {
 15642          chVal = strVal.charCodeAt(j);
 15643          hash = hashInt(chVal, hash);
 15644        }
 15645      }
 15646    }
 15647  
 15648    return hash;
 15649  };
 15650  
 15651  styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
 15652  
 15653  var styfn$4 = {};
 15654  
 15655  styfn$4.appendFromJson = function (json) {
 15656    var style = this;
 15657  
 15658    for (var i = 0; i < json.length; i++) {
 15659      var context = json[i];
 15660      var selector = context.selector;
 15661      var props = context.style || context.css;
 15662      var names = Object.keys(props);
 15663      style.selector(selector); // apply selector
 15664  
 15665      for (var j = 0; j < names.length; j++) {
 15666        var name = names[j];
 15667        var value = props[name];
 15668        style.css(name, value); // apply property
 15669      }
 15670    }
 15671  
 15672    return style;
 15673  }; // accessible cy.style() function
 15674  
 15675  
 15676  styfn$4.fromJson = function (json) {
 15677    var style = this;
 15678    style.resetToDefault();
 15679    style.appendFromJson(json);
 15680    return style;
 15681  }; // get json from cy.style() api
 15682  
 15683  
 15684  styfn$4.json = function () {
 15685    var json = [];
 15686  
 15687    for (var i = this.defaultLength; i < this.length; i++) {
 15688      var cxt = this[i];
 15689      var selector = cxt.selector;
 15690      var props = cxt.properties;
 15691      var css = {};
 15692  
 15693      for (var j = 0; j < props.length; j++) {
 15694        var prop = props[j];
 15695        css[prop.name] = prop.strValue;
 15696      }
 15697  
 15698      json.push({
 15699        selector: !selector ? 'core' : selector.toString(),
 15700        style: css
 15701      });
 15702    }
 15703  
 15704    return json;
 15705  };
 15706  
 15707  var styfn$5 = {};
 15708  
 15709  styfn$5.appendFromString = function (string) {
 15710    var self = this;
 15711    var style = this;
 15712    var remaining = '' + string;
 15713    var selAndBlockStr;
 15714    var blockRem;
 15715    var propAndValStr; // remove comments from the style string
 15716  
 15717    remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
 15718  
 15719    function removeSelAndBlockFromRemaining() {
 15720      // remove the parsed selector and block from the remaining text to parse
 15721      if (remaining.length > selAndBlockStr.length) {
 15722        remaining = remaining.substr(selAndBlockStr.length);
 15723      } else {
 15724        remaining = '';
 15725      }
 15726    }
 15727  
 15728    function removePropAndValFromRem() {
 15729      // remove the parsed property and value from the remaining block text to parse
 15730      if (blockRem.length > propAndValStr.length) {
 15731        blockRem = blockRem.substr(propAndValStr.length);
 15732      } else {
 15733        blockRem = '';
 15734      }
 15735    }
 15736  
 15737    for (;;) {
 15738      var nothingLeftToParse = remaining.match(/^\s*$/);
 15739  
 15740      if (nothingLeftToParse) {
 15741        break;
 15742      }
 15743  
 15744      var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
 15745  
 15746      if (!selAndBlock) {
 15747        warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
 15748        break;
 15749      }
 15750  
 15751      selAndBlockStr = selAndBlock[0]; // parse the selector
 15752  
 15753      var selectorStr = selAndBlock[1];
 15754  
 15755      if (selectorStr !== 'core') {
 15756        var selector = new Selector(selectorStr);
 15757  
 15758        if (selector.invalid) {
 15759          warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
 15760  
 15761          removeSelAndBlockFromRemaining();
 15762          continue;
 15763        }
 15764      } // parse the block of properties and values
 15765  
 15766  
 15767      var blockStr = selAndBlock[2];
 15768      var invalidBlock = false;
 15769      blockRem = blockStr;
 15770      var props = [];
 15771  
 15772      for (;;) {
 15773        var _nothingLeftToParse = blockRem.match(/^\s*$/);
 15774  
 15775        if (_nothingLeftToParse) {
 15776          break;
 15777        }
 15778  
 15779        var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
 15780  
 15781        if (!propAndVal) {
 15782          warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
 15783          invalidBlock = true;
 15784          break;
 15785        }
 15786  
 15787        propAndValStr = propAndVal[0];
 15788        var propStr = propAndVal[1];
 15789        var valStr = propAndVal[2];
 15790        var prop = self.properties[propStr];
 15791  
 15792        if (!prop) {
 15793          warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
 15794  
 15795          removePropAndValFromRem();
 15796          continue;
 15797        }
 15798  
 15799        var parsedProp = style.parse(propStr, valStr);
 15800  
 15801        if (!parsedProp) {
 15802          warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
 15803  
 15804          removePropAndValFromRem();
 15805          continue;
 15806        }
 15807  
 15808        props.push({
 15809          name: propStr,
 15810          val: valStr
 15811        });
 15812        removePropAndValFromRem();
 15813      }
 15814  
 15815      if (invalidBlock) {
 15816        removeSelAndBlockFromRemaining();
 15817        break;
 15818      } // put the parsed block in the style
 15819  
 15820  
 15821      style.selector(selectorStr);
 15822  
 15823      for (var i = 0; i < props.length; i++) {
 15824        var _prop = props[i];
 15825        style.css(_prop.name, _prop.val);
 15826      }
 15827  
 15828      removeSelAndBlockFromRemaining();
 15829    }
 15830  
 15831    return style;
 15832  };
 15833  
 15834  styfn$5.fromString = function (string) {
 15835    var style = this;
 15836    style.resetToDefault();
 15837    style.appendFromString(string);
 15838    return style;
 15839  };
 15840  
 15841  var styfn$6 = {};
 15842  
 15843  (function () {
 15844    var number = number$1;
 15845    var rgba = rgbaNoBackRefs;
 15846    var hsla = hslaNoBackRefs;
 15847    var hex3$1 = hex3;
 15848    var hex6$1 = hex6;
 15849  
 15850    var data = function data(prefix) {
 15851      return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
 15852    };
 15853  
 15854    var mapData = function mapData(prefix) {
 15855      var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
 15856      return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
 15857    };
 15858  
 15859    var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
 15860  
 15861    styfn$6.types = {
 15862      time: {
 15863        number: true,
 15864        min: 0,
 15865        units: 's|ms',
 15866        implicitUnits: 'ms'
 15867      },
 15868      percent: {
 15869        number: true,
 15870        min: 0,
 15871        max: 100,
 15872        units: '%',
 15873        implicitUnits: '%'
 15874      },
 15875      percentages: {
 15876        number: true,
 15877        min: 0,
 15878        max: 100,
 15879        units: '%',
 15880        implicitUnits: '%',
 15881        multiple: true
 15882      },
 15883      zeroOneNumber: {
 15884        number: true,
 15885        min: 0,
 15886        max: 1,
 15887        unitless: true
 15888      },
 15889      zeroOneNumbers: {
 15890        number: true,
 15891        min: 0,
 15892        max: 1,
 15893        unitless: true,
 15894        multiple: true
 15895      },
 15896      nOneOneNumber: {
 15897        number: true,
 15898        min: -1,
 15899        max: 1,
 15900        unitless: true
 15901      },
 15902      nonNegativeInt: {
 15903        number: true,
 15904        min: 0,
 15905        integer: true,
 15906        unitless: true
 15907      },
 15908      position: {
 15909        enums: ['parent', 'origin']
 15910      },
 15911      nodeSize: {
 15912        number: true,
 15913        min: 0,
 15914        enums: ['label']
 15915      },
 15916      number: {
 15917        number: true,
 15918        unitless: true
 15919      },
 15920      numbers: {
 15921        number: true,
 15922        unitless: true,
 15923        multiple: true
 15924      },
 15925      positiveNumber: {
 15926        number: true,
 15927        unitless: true,
 15928        min: 0,
 15929        strictMin: true
 15930      },
 15931      size: {
 15932        number: true,
 15933        min: 0
 15934      },
 15935      bidirectionalSize: {
 15936        number: true
 15937      },
 15938      // allows negative
 15939      bidirectionalSizes: {
 15940        number: true,
 15941        multiple: true
 15942      },
 15943      // allows negative
 15944      sizeMaybePercent: {
 15945        number: true,
 15946        min: 0,
 15947        allowPercent: true
 15948      },
 15949      axisDirection: {
 15950        enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
 15951      },
 15952      paddingRelativeTo: {
 15953        enums: ['width', 'height', 'average', 'min', 'max']
 15954      },
 15955      bgWH: {
 15956        number: true,
 15957        min: 0,
 15958        allowPercent: true,
 15959        enums: ['auto'],
 15960        multiple: true
 15961      },
 15962      bgPos: {
 15963        number: true,
 15964        allowPercent: true,
 15965        multiple: true
 15966      },
 15967      bgRelativeTo: {
 15968        enums: ['inner', 'include-padding'],
 15969        multiple: true
 15970      },
 15971      bgRepeat: {
 15972        enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
 15973        multiple: true
 15974      },
 15975      bgFit: {
 15976        enums: ['none', 'contain', 'cover'],
 15977        multiple: true
 15978      },
 15979      bgCrossOrigin: {
 15980        enums: ['anonymous', 'use-credentials'],
 15981        multiple: true
 15982      },
 15983      bgClip: {
 15984        enums: ['none', 'node'],
 15985        multiple: true
 15986      },
 15987      color: {
 15988        color: true
 15989      },
 15990      colors: {
 15991        color: true,
 15992        multiple: true
 15993      },
 15994      fill: {
 15995        enums: ['solid', 'linear-gradient', 'radial-gradient']
 15996      },
 15997      bool: {
 15998        enums: ['yes', 'no']
 15999      },
 16000      lineStyle: {
 16001        enums: ['solid', 'dotted', 'dashed']
 16002      },
 16003      lineCap: {
 16004        enums: ['butt', 'round', 'square']
 16005      },
 16006      borderStyle: {
 16007        enums: ['solid', 'dotted', 'dashed', 'double']
 16008      },
 16009      curveStyle: {
 16010        enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'taxi']
 16011      },
 16012      fontFamily: {
 16013        regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
 16014      },
 16015      fontStyle: {
 16016        enums: ['italic', 'normal', 'oblique']
 16017      },
 16018      fontWeight: {
 16019        enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
 16020      },
 16021      textDecoration: {
 16022        enums: ['none', 'underline', 'overline', 'line-through']
 16023      },
 16024      textTransform: {
 16025        enums: ['none', 'uppercase', 'lowercase']
 16026      },
 16027      textWrap: {
 16028        enums: ['none', 'wrap', 'ellipsis']
 16029      },
 16030      textOverflowWrap: {
 16031        enums: ['whitespace', 'anywhere']
 16032      },
 16033      textBackgroundShape: {
 16034        enums: ['rectangle', 'roundrectangle', 'round-rectangle']
 16035      },
 16036      nodeShape: {
 16037        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']
 16038      },
 16039      compoundIncludeLabels: {
 16040        enums: ['include', 'exclude']
 16041      },
 16042      arrowShape: {
 16043        enums: ['tee', 'triangle', 'triangle-tee', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
 16044      },
 16045      arrowFill: {
 16046        enums: ['filled', 'hollow']
 16047      },
 16048      display: {
 16049        enums: ['element', 'none']
 16050      },
 16051      visibility: {
 16052        enums: ['hidden', 'visible']
 16053      },
 16054      zCompoundDepth: {
 16055        enums: ['bottom', 'orphan', 'auto', 'top']
 16056      },
 16057      zIndexCompare: {
 16058        enums: ['auto', 'manual']
 16059      },
 16060      valign: {
 16061        enums: ['top', 'center', 'bottom']
 16062      },
 16063      halign: {
 16064        enums: ['left', 'center', 'right']
 16065      },
 16066      justification: {
 16067        enums: ['left', 'center', 'right', 'auto']
 16068      },
 16069      text: {
 16070        string: true
 16071      },
 16072      data: {
 16073        mapping: true,
 16074        regex: data('data')
 16075      },
 16076      layoutData: {
 16077        mapping: true,
 16078        regex: data('layoutData')
 16079      },
 16080      scratch: {
 16081        mapping: true,
 16082        regex: data('scratch')
 16083      },
 16084      mapData: {
 16085        mapping: true,
 16086        regex: mapData('mapData')
 16087      },
 16088      mapLayoutData: {
 16089        mapping: true,
 16090        regex: mapData('mapLayoutData')
 16091      },
 16092      mapScratch: {
 16093        mapping: true,
 16094        regex: mapData('mapScratch')
 16095      },
 16096      fn: {
 16097        mapping: true,
 16098        fn: true
 16099      },
 16100      url: {
 16101        regexes: urlRegexes,
 16102        singleRegexMatchValue: true
 16103      },
 16104      urls: {
 16105        regexes: urlRegexes,
 16106        singleRegexMatchValue: true,
 16107        multiple: true
 16108      },
 16109      propList: {
 16110        propList: true
 16111      },
 16112      angle: {
 16113        number: true,
 16114        units: 'deg|rad',
 16115        implicitUnits: 'rad'
 16116      },
 16117      textRotation: {
 16118        number: true,
 16119        units: 'deg|rad',
 16120        implicitUnits: 'rad',
 16121        enums: ['none', 'autorotate']
 16122      },
 16123      polygonPointList: {
 16124        number: true,
 16125        multiple: true,
 16126        evenMultiple: true,
 16127        min: -1,
 16128        max: 1,
 16129        unitless: true
 16130      },
 16131      edgeDistances: {
 16132        enums: ['intersection', 'node-position']
 16133      },
 16134      edgeEndpoint: {
 16135        number: true,
 16136        multiple: true,
 16137        units: '%|px|em|deg|rad',
 16138        implicitUnits: 'px',
 16139        enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
 16140        singleEnum: true,
 16141        validate: function validate(valArr, unitsArr) {
 16142          switch (valArr.length) {
 16143            case 2:
 16144              // can be % or px only
 16145              return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
 16146  
 16147            case 1:
 16148              // can be enum, deg, or rad only
 16149              return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
 16150  
 16151            default:
 16152              return false;
 16153          }
 16154        }
 16155      },
 16156      easing: {
 16157        regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
 16158        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']
 16159      },
 16160      gradientDirection: {
 16161        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']
 16162      },
 16163      boundsExpansion: {
 16164        number: true,
 16165        multiple: true,
 16166        min: 0,
 16167        validate: function validate(valArr) {
 16168          var length = valArr.length;
 16169          return length === 1 || length === 2 || length === 4;
 16170        }
 16171      }
 16172    };
 16173    var diff = {
 16174      zeroNonZero: function zeroNonZero(val1, val2) {
 16175        if ((val1 == null || val2 == null) && val1 !== val2) {
 16176          return true; // null cases could represent any value
 16177        }
 16178  
 16179        if (val1 == 0 && val2 != 0) {
 16180          return true;
 16181        } else if (val1 != 0 && val2 == 0) {
 16182          return true;
 16183        } else {
 16184          return false;
 16185        }
 16186      },
 16187      any: function any(val1, val2) {
 16188        return val1 != val2;
 16189      }
 16190    }; // define visual style properties
 16191    //
 16192    // - n.b. adding a new group of props may require updates to updateStyleHints()
 16193    // - adding new props to an existing group gets handled automatically
 16194  
 16195    var t = styfn$6.types;
 16196    var mainLabel = [{
 16197      name: 'label',
 16198      type: t.text,
 16199      triggersBounds: diff.any
 16200    }, {
 16201      name: 'text-rotation',
 16202      type: t.textRotation,
 16203      triggersBounds: diff.any
 16204    }, {
 16205      name: 'text-margin-x',
 16206      type: t.bidirectionalSize,
 16207      triggersBounds: diff.any
 16208    }, {
 16209      name: 'text-margin-y',
 16210      type: t.bidirectionalSize,
 16211      triggersBounds: diff.any
 16212    }];
 16213    var sourceLabel = [{
 16214      name: 'source-label',
 16215      type: t.text,
 16216      triggersBounds: diff.any
 16217    }, {
 16218      name: 'source-text-rotation',
 16219      type: t.textRotation,
 16220      triggersBounds: diff.any
 16221    }, {
 16222      name: 'source-text-margin-x',
 16223      type: t.bidirectionalSize,
 16224      triggersBounds: diff.any
 16225    }, {
 16226      name: 'source-text-margin-y',
 16227      type: t.bidirectionalSize,
 16228      triggersBounds: diff.any
 16229    }, {
 16230      name: 'source-text-offset',
 16231      type: t.size,
 16232      triggersBounds: diff.any
 16233    }];
 16234    var targetLabel = [{
 16235      name: 'target-label',
 16236      type: t.text,
 16237      triggersBounds: diff.any
 16238    }, {
 16239      name: 'target-text-rotation',
 16240      type: t.textRotation,
 16241      triggersBounds: diff.any
 16242    }, {
 16243      name: 'target-text-margin-x',
 16244      type: t.bidirectionalSize,
 16245      triggersBounds: diff.any
 16246    }, {
 16247      name: 'target-text-margin-y',
 16248      type: t.bidirectionalSize,
 16249      triggersBounds: diff.any
 16250    }, {
 16251      name: 'target-text-offset',
 16252      type: t.size,
 16253      triggersBounds: diff.any
 16254    }];
 16255    var labelDimensions = [{
 16256      name: 'font-family',
 16257      type: t.fontFamily,
 16258      triggersBounds: diff.any
 16259    }, {
 16260      name: 'font-style',
 16261      type: t.fontStyle,
 16262      triggersBounds: diff.any
 16263    }, {
 16264      name: 'font-weight',
 16265      type: t.fontWeight,
 16266      triggersBounds: diff.any
 16267    }, {
 16268      name: 'font-size',
 16269      type: t.size,
 16270      triggersBounds: diff.any
 16271    }, {
 16272      name: 'text-transform',
 16273      type: t.textTransform,
 16274      triggersBounds: diff.any
 16275    }, {
 16276      name: 'text-wrap',
 16277      type: t.textWrap,
 16278      triggersBounds: diff.any
 16279    }, {
 16280      name: 'text-overflow-wrap',
 16281      type: t.textOverflowWrap,
 16282      triggersBounds: diff.any
 16283    }, {
 16284      name: 'text-max-width',
 16285      type: t.size,
 16286      triggersBounds: diff.any
 16287    }, {
 16288      name: 'text-outline-width',
 16289      type: t.size,
 16290      triggersBounds: diff.any
 16291    }, {
 16292      name: 'line-height',
 16293      type: t.positiveNumber,
 16294      triggersBounds: diff.any
 16295    }];
 16296    var commonLabel = [{
 16297      name: 'text-valign',
 16298      type: t.valign,
 16299      triggersBounds: diff.any
 16300    }, {
 16301      name: 'text-halign',
 16302      type: t.halign,
 16303      triggersBounds: diff.any
 16304    }, {
 16305      name: 'color',
 16306      type: t.color
 16307    }, {
 16308      name: 'text-outline-color',
 16309      type: t.color
 16310    }, {
 16311      name: 'text-outline-opacity',
 16312      type: t.zeroOneNumber
 16313    }, {
 16314      name: 'text-background-color',
 16315      type: t.color
 16316    }, {
 16317      name: 'text-background-opacity',
 16318      type: t.zeroOneNumber
 16319    }, {
 16320      name: 'text-background-padding',
 16321      type: t.size,
 16322      triggersBounds: diff.any
 16323    }, {
 16324      name: 'text-border-opacity',
 16325      type: t.zeroOneNumber
 16326    }, {
 16327      name: 'text-border-color',
 16328      type: t.color
 16329    }, {
 16330      name: 'text-border-width',
 16331      type: t.size,
 16332      triggersBounds: diff.any
 16333    }, {
 16334      name: 'text-border-style',
 16335      type: t.borderStyle,
 16336      triggersBounds: diff.any
 16337    }, {
 16338      name: 'text-background-shape',
 16339      type: t.textBackgroundShape,
 16340      triggersBounds: diff.any
 16341    }, {
 16342      name: 'text-justification',
 16343      type: t.justification
 16344    }];
 16345    var behavior = [{
 16346      name: 'events',
 16347      type: t.bool
 16348    }, {
 16349      name: 'text-events',
 16350      type: t.bool
 16351    }];
 16352    var visibility = [{
 16353      name: 'display',
 16354      type: t.display,
 16355      triggersZOrder: diff.any,
 16356      triggersBounds: diff.any,
 16357      triggersBoundsOfParallelBeziers: true
 16358    }, {
 16359      name: 'visibility',
 16360      type: t.visibility,
 16361      triggersZOrder: diff.any
 16362    }, {
 16363      name: 'opacity',
 16364      type: t.zeroOneNumber,
 16365      triggersZOrder: diff.zeroNonZero
 16366    }, {
 16367      name: 'text-opacity',
 16368      type: t.zeroOneNumber
 16369    }, {
 16370      name: 'min-zoomed-font-size',
 16371      type: t.size
 16372    }, {
 16373      name: 'z-compound-depth',
 16374      type: t.zCompoundDepth,
 16375      triggersZOrder: diff.any
 16376    }, {
 16377      name: 'z-index-compare',
 16378      type: t.zIndexCompare,
 16379      triggersZOrder: diff.any
 16380    }, {
 16381      name: 'z-index',
 16382      type: t.nonNegativeInt,
 16383      triggersZOrder: diff.any
 16384    }];
 16385    var overlay = [{
 16386      name: 'overlay-padding',
 16387      type: t.size,
 16388      triggersBounds: diff.any
 16389    }, {
 16390      name: 'overlay-color',
 16391      type: t.color
 16392    }, {
 16393      name: 'overlay-opacity',
 16394      type: t.zeroOneNumber,
 16395      triggersBounds: diff.zeroNonZero
 16396    }];
 16397    var transition = [{
 16398      name: 'transition-property',
 16399      type: t.propList
 16400    }, {
 16401      name: 'transition-duration',
 16402      type: t.time
 16403    }, {
 16404      name: 'transition-delay',
 16405      type: t.time
 16406    }, {
 16407      name: 'transition-timing-function',
 16408      type: t.easing
 16409    }];
 16410  
 16411    var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
 16412      if (parsedProp.value === 'label') {
 16413        return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
 16414      } else {
 16415        return parsedProp.pfValue;
 16416      }
 16417    };
 16418  
 16419    var nodeBody = [{
 16420      name: 'height',
 16421      type: t.nodeSize,
 16422      triggersBounds: diff.any,
 16423      hashOverride: nodeSizeHashOverride
 16424    }, {
 16425      name: 'width',
 16426      type: t.nodeSize,
 16427      triggersBounds: diff.any,
 16428      hashOverride: nodeSizeHashOverride
 16429    }, {
 16430      name: 'shape',
 16431      type: t.nodeShape,
 16432      triggersBounds: diff.any
 16433    }, {
 16434      name: 'shape-polygon-points',
 16435      type: t.polygonPointList,
 16436      triggersBounds: diff.any
 16437    }, {
 16438      name: 'background-color',
 16439      type: t.color
 16440    }, {
 16441      name: 'background-fill',
 16442      type: t.fill
 16443    }, {
 16444      name: 'background-opacity',
 16445      type: t.zeroOneNumber
 16446    }, {
 16447      name: 'background-blacken',
 16448      type: t.nOneOneNumber
 16449    }, {
 16450      name: 'background-gradient-stop-colors',
 16451      type: t.colors
 16452    }, {
 16453      name: 'background-gradient-stop-positions',
 16454      type: t.percentages
 16455    }, {
 16456      name: 'background-gradient-direction',
 16457      type: t.gradientDirection
 16458    }, {
 16459      name: 'padding',
 16460      type: t.sizeMaybePercent,
 16461      triggersBounds: diff.any
 16462    }, {
 16463      name: 'padding-relative-to',
 16464      type: t.paddingRelativeTo,
 16465      triggersBounds: diff.any
 16466    }, {
 16467      name: 'bounds-expansion',
 16468      type: t.boundsExpansion,
 16469      triggersBounds: diff.any
 16470    }];
 16471    var nodeBorder = [{
 16472      name: 'border-color',
 16473      type: t.color
 16474    }, {
 16475      name: 'border-opacity',
 16476      type: t.zeroOneNumber
 16477    }, {
 16478      name: 'border-width',
 16479      type: t.size,
 16480      triggersBounds: diff.any
 16481    }, {
 16482      name: 'border-style',
 16483      type: t.borderStyle
 16484    }];
 16485    var backgroundImage = [{
 16486      name: 'background-image',
 16487      type: t.urls
 16488    }, {
 16489      name: 'background-image-crossorigin',
 16490      type: t.bgCrossOrigin
 16491    }, {
 16492      name: 'background-image-opacity',
 16493      type: t.zeroOneNumbers
 16494    }, {
 16495      name: 'background-position-x',
 16496      type: t.bgPos
 16497    }, {
 16498      name: 'background-position-y',
 16499      type: t.bgPos
 16500    }, {
 16501      name: 'background-width-relative-to',
 16502      type: t.bgRelativeTo
 16503    }, {
 16504      name: 'background-height-relative-to',
 16505      type: t.bgRelativeTo
 16506    }, {
 16507      name: 'background-repeat',
 16508      type: t.bgRepeat
 16509    }, {
 16510      name: 'background-fit',
 16511      type: t.bgFit
 16512    }, {
 16513      name: 'background-clip',
 16514      type: t.bgClip
 16515    }, {
 16516      name: 'background-width',
 16517      type: t.bgWH
 16518    }, {
 16519      name: 'background-height',
 16520      type: t.bgWH
 16521    }, {
 16522      name: 'background-offset-x',
 16523      type: t.bgPos
 16524    }, {
 16525      name: 'background-offset-y',
 16526      type: t.bgPos
 16527    }];
 16528    var compound = [{
 16529      name: 'position',
 16530      type: t.position,
 16531      triggersBounds: diff.any
 16532    }, {
 16533      name: 'compound-sizing-wrt-labels',
 16534      type: t.compoundIncludeLabels,
 16535      triggersBounds: diff.any
 16536    }, {
 16537      name: 'min-width',
 16538      type: t.size,
 16539      triggersBounds: diff.any
 16540    }, {
 16541      name: 'min-width-bias-left',
 16542      type: t.sizeMaybePercent,
 16543      triggersBounds: diff.any
 16544    }, {
 16545      name: 'min-width-bias-right',
 16546      type: t.sizeMaybePercent,
 16547      triggersBounds: diff.any
 16548    }, {
 16549      name: 'min-height',
 16550      type: t.size,
 16551      triggersBounds: diff.any
 16552    }, {
 16553      name: 'min-height-bias-top',
 16554      type: t.sizeMaybePercent,
 16555      triggersBounds: diff.any
 16556    }, {
 16557      name: 'min-height-bias-bottom',
 16558      type: t.sizeMaybePercent,
 16559      triggersBounds: diff.any
 16560    }];
 16561    var edgeLine = [{
 16562      name: 'line-style',
 16563      type: t.lineStyle
 16564    }, {
 16565      name: 'line-color',
 16566      type: t.color
 16567    }, {
 16568      name: 'line-fill',
 16569      type: t.fill
 16570    }, {
 16571      name: 'line-cap',
 16572      type: t.lineCap
 16573    }, {
 16574      name: 'line-dash-pattern',
 16575      type: t.numbers
 16576    }, {
 16577      name: 'line-dash-offset',
 16578      type: t.number
 16579    }, {
 16580      name: 'line-gradient-stop-colors',
 16581      type: t.colors
 16582    }, {
 16583      name: 'line-gradient-stop-positions',
 16584      type: t.percentages
 16585    }, {
 16586      name: 'curve-style',
 16587      type: t.curveStyle,
 16588      triggersBounds: diff.any,
 16589      triggersBoundsOfParallelBeziers: true
 16590    }, {
 16591      name: 'haystack-radius',
 16592      type: t.zeroOneNumber,
 16593      triggersBounds: diff.any
 16594    }, {
 16595      name: 'source-endpoint',
 16596      type: t.edgeEndpoint,
 16597      triggersBounds: diff.any
 16598    }, {
 16599      name: 'target-endpoint',
 16600      type: t.edgeEndpoint,
 16601      triggersBounds: diff.any
 16602    }, {
 16603      name: 'control-point-step-size',
 16604      type: t.size,
 16605      triggersBounds: diff.any
 16606    }, {
 16607      name: 'control-point-distances',
 16608      type: t.bidirectionalSizes,
 16609      triggersBounds: diff.any
 16610    }, {
 16611      name: 'control-point-weights',
 16612      type: t.numbers,
 16613      triggersBounds: diff.any
 16614    }, {
 16615      name: 'segment-distances',
 16616      type: t.bidirectionalSizes,
 16617      triggersBounds: diff.any
 16618    }, {
 16619      name: 'segment-weights',
 16620      type: t.numbers,
 16621      triggersBounds: diff.any
 16622    }, {
 16623      name: 'taxi-turn',
 16624      type: t.sizeMaybePercent,
 16625      triggersBounds: diff.any
 16626    }, {
 16627      name: 'taxi-turn-min-distance',
 16628      type: t.size,
 16629      triggersBounds: diff.any
 16630    }, {
 16631      name: 'taxi-direction',
 16632      type: t.axisDirection,
 16633      triggersBounds: diff.any
 16634    }, {
 16635      name: 'edge-distances',
 16636      type: t.edgeDistances,
 16637      triggersBounds: diff.any
 16638    }, {
 16639      name: 'arrow-scale',
 16640      type: t.positiveNumber,
 16641      triggersBounds: diff.any
 16642    }, {
 16643      name: 'loop-direction',
 16644      type: t.angle,
 16645      triggersBounds: diff.any
 16646    }, {
 16647      name: 'loop-sweep',
 16648      type: t.angle,
 16649      triggersBounds: diff.any
 16650    }, {
 16651      name: 'source-distance-from-node',
 16652      type: t.size,
 16653      triggersBounds: diff.any
 16654    }, {
 16655      name: 'target-distance-from-node',
 16656      type: t.size,
 16657      triggersBounds: diff.any
 16658    }];
 16659    var ghost = [{
 16660      name: 'ghost',
 16661      type: t.bool,
 16662      triggersBounds: diff.any
 16663    }, {
 16664      name: 'ghost-offset-x',
 16665      type: t.bidirectionalSize,
 16666      triggersBounds: diff.any
 16667    }, {
 16668      name: 'ghost-offset-y',
 16669      type: t.bidirectionalSize,
 16670      triggersBounds: diff.any
 16671    }, {
 16672      name: 'ghost-opacity',
 16673      type: t.zeroOneNumber
 16674    }];
 16675    var core = [{
 16676      name: 'selection-box-color',
 16677      type: t.color
 16678    }, {
 16679      name: 'selection-box-opacity',
 16680      type: t.zeroOneNumber
 16681    }, {
 16682      name: 'selection-box-border-color',
 16683      type: t.color
 16684    }, {
 16685      name: 'selection-box-border-width',
 16686      type: t.size
 16687    }, {
 16688      name: 'active-bg-color',
 16689      type: t.color
 16690    }, {
 16691      name: 'active-bg-opacity',
 16692      type: t.zeroOneNumber
 16693    }, {
 16694      name: 'active-bg-size',
 16695      type: t.size
 16696    }, {
 16697      name: 'outside-texture-bg-color',
 16698      type: t.color
 16699    }, {
 16700      name: 'outside-texture-bg-opacity',
 16701      type: t.zeroOneNumber
 16702    }]; // pie backgrounds for nodes
 16703  
 16704    var pie = [];
 16705    styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
 16706  
 16707    pie.push({
 16708      name: 'pie-size',
 16709      type: t.sizeMaybePercent
 16710    });
 16711  
 16712    for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
 16713      pie.push({
 16714        name: 'pie-' + i + '-background-color',
 16715        type: t.color
 16716      });
 16717      pie.push({
 16718        name: 'pie-' + i + '-background-size',
 16719        type: t.percent
 16720      });
 16721      pie.push({
 16722        name: 'pie-' + i + '-background-opacity',
 16723        type: t.zeroOneNumber
 16724      });
 16725    } // edge arrows
 16726  
 16727  
 16728    var edgeArrow = [];
 16729    var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
 16730    [{
 16731      name: 'arrow-shape',
 16732      type: t.arrowShape,
 16733      triggersBounds: diff.any
 16734    }, {
 16735      name: 'arrow-color',
 16736      type: t.color
 16737    }, {
 16738      name: 'arrow-fill',
 16739      type: t.arrowFill
 16740    }].forEach(function (prop) {
 16741      arrowPrefixes.forEach(function (prefix) {
 16742        var name = prefix + '-' + prop.name;
 16743        var type = prop.type,
 16744            triggersBounds = prop.triggersBounds;
 16745        edgeArrow.push({
 16746          name: name,
 16747          type: type,
 16748          triggersBounds: triggersBounds
 16749        });
 16750      });
 16751    }, {});
 16752    var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
 16753    var propGroups = styfn$6.propertyGroups = {
 16754      // common to all eles
 16755      behavior: behavior,
 16756      transition: transition,
 16757      visibility: visibility,
 16758      overlay: overlay,
 16759      ghost: ghost,
 16760      // labels
 16761      commonLabel: commonLabel,
 16762      labelDimensions: labelDimensions,
 16763      mainLabel: mainLabel,
 16764      sourceLabel: sourceLabel,
 16765      targetLabel: targetLabel,
 16766      // node props
 16767      nodeBody: nodeBody,
 16768      nodeBorder: nodeBorder,
 16769      backgroundImage: backgroundImage,
 16770      pie: pie,
 16771      compound: compound,
 16772      // edge props
 16773      edgeLine: edgeLine,
 16774      edgeArrow: edgeArrow,
 16775      core: core
 16776    };
 16777    var propGroupNames = styfn$6.propertyGroupNames = {};
 16778    var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
 16779    propGroupKeys.forEach(function (key) {
 16780      propGroupNames[key] = propGroups[key].map(function (prop) {
 16781        return prop.name;
 16782      });
 16783      propGroups[key].forEach(function (prop) {
 16784        return prop.groupKey = key;
 16785      });
 16786    }); // define aliases
 16787  
 16788    var aliases = styfn$6.aliases = [{
 16789      name: 'content',
 16790      pointsTo: 'label'
 16791    }, {
 16792      name: 'control-point-distance',
 16793      pointsTo: 'control-point-distances'
 16794    }, {
 16795      name: 'control-point-weight',
 16796      pointsTo: 'control-point-weights'
 16797    }, {
 16798      name: 'edge-text-rotation',
 16799      pointsTo: 'text-rotation'
 16800    }, {
 16801      name: 'padding-left',
 16802      pointsTo: 'padding'
 16803    }, {
 16804      name: 'padding-right',
 16805      pointsTo: 'padding'
 16806    }, {
 16807      name: 'padding-top',
 16808      pointsTo: 'padding'
 16809    }, {
 16810      name: 'padding-bottom',
 16811      pointsTo: 'padding'
 16812    }]; // list of property names
 16813  
 16814    styfn$6.propertyNames = props.map(function (p) {
 16815      return p.name;
 16816    }); // allow access of properties by name ( e.g. style.properties.height )
 16817  
 16818    for (var _i = 0; _i < props.length; _i++) {
 16819      var prop = props[_i];
 16820      props[prop.name] = prop; // allow lookup by name
 16821    } // map aliases
 16822  
 16823  
 16824    for (var _i2 = 0; _i2 < aliases.length; _i2++) {
 16825      var alias = aliases[_i2];
 16826      var pointsToProp = props[alias.pointsTo];
 16827      var aliasProp = {
 16828        name: alias.name,
 16829        alias: true,
 16830        pointsTo: pointsToProp
 16831      }; // add alias prop for parsing
 16832  
 16833      props.push(aliasProp);
 16834      props[alias.name] = aliasProp; // allow lookup by name
 16835    }
 16836  })();
 16837  
 16838  styfn$6.getDefaultProperty = function (name) {
 16839    return this.getDefaultProperties()[name];
 16840  };
 16841  
 16842  styfn$6.getDefaultProperties = function () {
 16843    var _p = this._private;
 16844  
 16845    if (_p.defaultProperties != null) {
 16846      return _p.defaultProperties;
 16847    }
 16848  
 16849    var rawProps = extend({
 16850      // core props
 16851      'selection-box-color': '#ddd',
 16852      'selection-box-opacity': 0.65,
 16853      'selection-box-border-color': '#aaa',
 16854      'selection-box-border-width': 1,
 16855      'active-bg-color': 'black',
 16856      'active-bg-opacity': 0.15,
 16857      'active-bg-size': 30,
 16858      'outside-texture-bg-color': '#000',
 16859      'outside-texture-bg-opacity': 0.125,
 16860      // common node/edge props
 16861      'events': 'yes',
 16862      'text-events': 'no',
 16863      'text-valign': 'top',
 16864      'text-halign': 'center',
 16865      'text-justification': 'auto',
 16866      'line-height': 1,
 16867      'color': '#000',
 16868      'text-outline-color': '#000',
 16869      'text-outline-width': 0,
 16870      'text-outline-opacity': 1,
 16871      'text-opacity': 1,
 16872      'text-decoration': 'none',
 16873      'text-transform': 'none',
 16874      'text-wrap': 'none',
 16875      'text-overflow-wrap': 'whitespace',
 16876      'text-max-width': 9999,
 16877      'text-background-color': '#000',
 16878      'text-background-opacity': 0,
 16879      'text-background-shape': 'rectangle',
 16880      'text-background-padding': 0,
 16881      'text-border-opacity': 0,
 16882      'text-border-width': 0,
 16883      'text-border-style': 'solid',
 16884      'text-border-color': '#000',
 16885      'font-family': 'Helvetica Neue, Helvetica, sans-serif',
 16886      'font-style': 'normal',
 16887      'font-weight': 'normal',
 16888      'font-size': 16,
 16889      'min-zoomed-font-size': 0,
 16890      'text-rotation': 'none',
 16891      'source-text-rotation': 'none',
 16892      'target-text-rotation': 'none',
 16893      'visibility': 'visible',
 16894      'display': 'element',
 16895      'opacity': 1,
 16896      'z-compound-depth': 'auto',
 16897      'z-index-compare': 'auto',
 16898      'z-index': 0,
 16899      'label': '',
 16900      'text-margin-x': 0,
 16901      'text-margin-y': 0,
 16902      'source-label': '',
 16903      'source-text-offset': 0,
 16904      'source-text-margin-x': 0,
 16905      'source-text-margin-y': 0,
 16906      'target-label': '',
 16907      'target-text-offset': 0,
 16908      'target-text-margin-x': 0,
 16909      'target-text-margin-y': 0,
 16910      'overlay-opacity': 0,
 16911      'overlay-color': '#000',
 16912      'overlay-padding': 10,
 16913      'transition-property': 'none',
 16914      'transition-duration': 0,
 16915      'transition-delay': 0,
 16916      'transition-timing-function': 'linear',
 16917      // node props
 16918      'background-blacken': 0,
 16919      'background-color': '#999',
 16920      'background-fill': 'solid',
 16921      'background-opacity': 1,
 16922      'background-image': 'none',
 16923      'background-image-crossorigin': 'anonymous',
 16924      'background-image-opacity': 1,
 16925      'background-position-x': '50%',
 16926      'background-position-y': '50%',
 16927      'background-offset-x': 0,
 16928      'background-offset-y': 0,
 16929      'background-width-relative-to': 'include-padding',
 16930      'background-height-relative-to': 'include-padding',
 16931      'background-repeat': 'no-repeat',
 16932      'background-fit': 'none',
 16933      'background-clip': 'node',
 16934      'background-width': 'auto',
 16935      'background-height': 'auto',
 16936      'border-color': '#000',
 16937      'border-opacity': 1,
 16938      'border-width': 0,
 16939      'border-style': 'solid',
 16940      'height': 30,
 16941      'width': 30,
 16942      'shape': 'ellipse',
 16943      'shape-polygon-points': '-1, -1,   1, -1,   1, 1,   -1, 1',
 16944      'bounds-expansion': 0,
 16945      // node gradient
 16946      'background-gradient-direction': 'to-bottom',
 16947      'background-gradient-stop-colors': '#999',
 16948      'background-gradient-stop-positions': '0%',
 16949      // ghost props
 16950      'ghost': 'no',
 16951      'ghost-offset-y': 0,
 16952      'ghost-offset-x': 0,
 16953      'ghost-opacity': 0,
 16954      // compound props
 16955      'padding': 0,
 16956      'padding-relative-to': 'width',
 16957      'position': 'origin',
 16958      'compound-sizing-wrt-labels': 'include',
 16959      'min-width': 0,
 16960      'min-width-bias-left': 0,
 16961      'min-width-bias-right': 0,
 16962      'min-height': 0,
 16963      'min-height-bias-top': 0,
 16964      'min-height-bias-bottom': 0
 16965    }, {
 16966      // node pie bg
 16967      'pie-size': '100%'
 16968    }, [{
 16969      name: 'pie-{{i}}-background-color',
 16970      value: 'black'
 16971    }, {
 16972      name: 'pie-{{i}}-background-size',
 16973      value: '0%'
 16974    }, {
 16975      name: 'pie-{{i}}-background-opacity',
 16976      value: 1
 16977    }].reduce(function (css, prop) {
 16978      for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
 16979        var name = prop.name.replace('{{i}}', i);
 16980        var val = prop.value;
 16981        css[name] = val;
 16982      }
 16983  
 16984      return css;
 16985    }, {}), {
 16986      // edge props
 16987      'line-style': 'solid',
 16988      'line-color': '#999',
 16989      'line-fill': 'solid',
 16990      'line-cap': 'butt',
 16991      'line-gradient-stop-colors': '#999',
 16992      'line-gradient-stop-positions': '0%',
 16993      'control-point-step-size': 40,
 16994      'control-point-weights': 0.5,
 16995      'segment-weights': 0.5,
 16996      'segment-distances': 20,
 16997      'taxi-turn': '50%',
 16998      'taxi-turn-min-distance': 10,
 16999      'taxi-direction': 'auto',
 17000      'edge-distances': 'intersection',
 17001      'curve-style': 'haystack',
 17002      'haystack-radius': 0,
 17003      'arrow-scale': 1,
 17004      'loop-direction': '-45deg',
 17005      'loop-sweep': '-90deg',
 17006      'source-distance-from-node': 0,
 17007      'target-distance-from-node': 0,
 17008      'source-endpoint': 'outside-to-node',
 17009      'target-endpoint': 'outside-to-node',
 17010      'line-dash-pattern': [6, 3],
 17011      'line-dash-offset': 0
 17012    }, [{
 17013      name: 'arrow-shape',
 17014      value: 'none'
 17015    }, {
 17016      name: 'arrow-color',
 17017      value: '#999'
 17018    }, {
 17019      name: 'arrow-fill',
 17020      value: 'filled'
 17021    }].reduce(function (css, prop) {
 17022      styfn$6.arrowPrefixes.forEach(function (prefix) {
 17023        var name = prefix + '-' + prop.name;
 17024        var val = prop.value;
 17025        css[name] = val;
 17026      });
 17027      return css;
 17028    }, {}));
 17029    var parsedProps = {};
 17030  
 17031    for (var i = 0; i < this.properties.length; i++) {
 17032      var prop = this.properties[i];
 17033  
 17034      if (prop.pointsTo) {
 17035        continue;
 17036      }
 17037  
 17038      var name = prop.name;
 17039      var val = rawProps[name];
 17040      var parsedProp = this.parse(name, val);
 17041      parsedProps[name] = parsedProp;
 17042    }
 17043  
 17044    _p.defaultProperties = parsedProps;
 17045    return _p.defaultProperties;
 17046  };
 17047  
 17048  styfn$6.addDefaultStylesheet = function () {
 17049    this.selector(':parent').css({
 17050      'shape': 'rectangle',
 17051      'padding': 10,
 17052      'background-color': '#eee',
 17053      'border-color': '#ccc',
 17054      'border-width': 1
 17055    }).selector('edge').css({
 17056      'width': 3
 17057    }).selector(':loop').css({
 17058      'curve-style': 'bezier'
 17059    }).selector('edge:compound').css({
 17060      'curve-style': 'bezier',
 17061      'source-endpoint': 'outside-to-line',
 17062      'target-endpoint': 'outside-to-line'
 17063    }).selector(':selected').css({
 17064      'background-color': '#0169D9',
 17065      'line-color': '#0169D9',
 17066      'source-arrow-color': '#0169D9',
 17067      'target-arrow-color': '#0169D9',
 17068      'mid-source-arrow-color': '#0169D9',
 17069      'mid-target-arrow-color': '#0169D9'
 17070    }).selector(':parent:selected').css({
 17071      'background-color': '#CCE1F9',
 17072      'border-color': '#aec8e5'
 17073    }).selector(':active').css({
 17074      'overlay-color': 'black',
 17075      'overlay-padding': 10,
 17076      'overlay-opacity': 0.25
 17077    });
 17078    this.defaultLength = this.length;
 17079  };
 17080  
 17081  var styfn$7 = {}; // a caching layer for property parsing
 17082  
 17083  styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
 17084    var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
 17085  
 17086    if (fn(value)) {
 17087      return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
 17088    }
 17089  
 17090    var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
 17091    var bypassKey = propIsBypass ? 't' : 'f';
 17092    var valueKey = '' + value;
 17093    var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
 17094    var propCache = self.propCache = self.propCache || [];
 17095    var ret;
 17096  
 17097    if (!(ret = propCache[argHash])) {
 17098      ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
 17099    } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
 17100    // - mappings can't be shared b/c mappings are per-element
 17101  
 17102  
 17103    if (propIsBypass || propIsFlat === 'mapping') {
 17104      // need a copy since props are mutated later in their lifecycles
 17105      ret = copy(ret);
 17106  
 17107      if (ret) {
 17108        ret.value = copy(ret.value); // because it could be an array, e.g. colour
 17109      }
 17110    }
 17111  
 17112    return ret;
 17113  };
 17114  
 17115  styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
 17116    var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
 17117  
 17118    if (!prop && value != null) {
 17119      warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
 17120    }
 17121  
 17122    return prop;
 17123  }; // parse a property; return null on invalid; return parsed property otherwise
 17124  // fields :
 17125  // - name : the name of the property
 17126  // - value : the parsed, native-typed value of the property
 17127  // - strValue : a string value that represents the property value in valid css
 17128  // - bypass : true iff the property is a bypass property
 17129  
 17130  
 17131  styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
 17132    var self = this;
 17133    name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
 17134  
 17135    var property = self.properties[name];
 17136    var passedValue = value;
 17137    var types = self.types;
 17138  
 17139    if (!property) {
 17140      return null;
 17141    } // return null on property of unknown name
 17142  
 17143  
 17144    if (value === undefined) {
 17145      return null;
 17146    } // can't assign undefined
 17147    // the property may be an alias
 17148  
 17149  
 17150    if (property.alias) {
 17151      property = property.pointsTo;
 17152      name = property.name;
 17153    }
 17154  
 17155    var valueIsString = string(value);
 17156  
 17157    if (valueIsString) {
 17158      // trim the value to make parsing easier
 17159      value = value.trim();
 17160    }
 17161  
 17162    var type = property.type;
 17163  
 17164    if (!type) {
 17165      return null;
 17166    } // no type, no luck
 17167    // check if bypass is null or empty string (i.e. indication to delete bypass property)
 17168  
 17169  
 17170    if (propIsBypass && (value === '' || value === null)) {
 17171      return {
 17172        name: name,
 17173        value: value,
 17174        bypass: true,
 17175        deleteBypass: true
 17176      };
 17177    } // check if value is a function used as a mapper
 17178  
 17179  
 17180    if (fn(value)) {
 17181      return {
 17182        name: name,
 17183        value: value,
 17184        strValue: 'fn',
 17185        mapped: types.fn,
 17186        bypass: propIsBypass
 17187      };
 17188    } // check if value is mapped
 17189  
 17190  
 17191    var data, mapData;
 17192  
 17193    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))) {
 17194      if (propIsBypass) {
 17195        return false;
 17196      } // mappers not allowed in bypass
 17197  
 17198  
 17199      var mapped = types.data;
 17200      return {
 17201        name: name,
 17202        value: data,
 17203        strValue: '' + value,
 17204        mapped: mapped,
 17205        field: data[1],
 17206        bypass: propIsBypass
 17207      };
 17208    } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
 17209      if (propIsBypass) {
 17210        return false;
 17211      } // mappers not allowed in bypass
 17212  
 17213  
 17214      if (type.multiple) {
 17215        return false;
 17216      } // impossible to map to num
 17217  
 17218  
 17219      var _mapped = types.mapData; // we can map only if the type is a colour or a number
 17220  
 17221      if (!(type.color || type.number)) {
 17222        return false;
 17223      }
 17224  
 17225      var valueMin = this.parse(name, mapData[4]); // parse to validate
 17226  
 17227      if (!valueMin || valueMin.mapped) {
 17228        return false;
 17229      } // can't be invalid or mapped
 17230  
 17231  
 17232      var valueMax = this.parse(name, mapData[5]); // parse to validate
 17233  
 17234      if (!valueMax || valueMax.mapped) {
 17235        return false;
 17236      } // can't be invalid or mapped
 17237      // check if valueMin and valueMax are the same
 17238  
 17239  
 17240      if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
 17241        warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
 17242        return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
 17243      } else if (type.color) {
 17244        var c1 = valueMin.value;
 17245        var c2 = valueMax.value;
 17246        var same = c1[0] === c2[0] // red
 17247        && c1[1] === c2[1] // green
 17248        && c1[2] === c2[2] // blue
 17249        && ( // optional alpha
 17250        c1[3] === c2[3] // same alpha outright
 17251        || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
 17252        c2[3] == null || c2[3] === 1) // full opacity for colour 2?
 17253        );
 17254  
 17255        if (same) {
 17256          return false;
 17257        } // can't make a mapper without a range
 17258  
 17259      }
 17260  
 17261      return {
 17262        name: name,
 17263        value: mapData,
 17264        strValue: '' + value,
 17265        mapped: _mapped,
 17266        field: mapData[1],
 17267        fieldMin: parseFloat(mapData[2]),
 17268        // min & max are numeric
 17269        fieldMax: parseFloat(mapData[3]),
 17270        valueMin: valueMin.value,
 17271        valueMax: valueMax.value,
 17272        bypass: propIsBypass
 17273      };
 17274    }
 17275  
 17276    if (type.multiple && propIsFlat !== 'multiple') {
 17277      var vals;
 17278  
 17279      if (valueIsString) {
 17280        vals = value.split(/\s+/);
 17281      } else if (array(value)) {
 17282        vals = value;
 17283      } else {
 17284        vals = [value];
 17285      }
 17286  
 17287      if (type.evenMultiple && vals.length % 2 !== 0) {
 17288        return null;
 17289      }
 17290  
 17291      var valArr = [];
 17292      var unitsArr = [];
 17293      var pfValArr = [];
 17294      var strVal = '';
 17295      var hasEnum = false;
 17296  
 17297      for (var i = 0; i < vals.length; i++) {
 17298        var p = self.parse(name, vals[i], propIsBypass, 'multiple');
 17299        hasEnum = hasEnum || string(p.value);
 17300        valArr.push(p.value);
 17301        pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
 17302        unitsArr.push(p.units);
 17303        strVal += (i > 0 ? ' ' : '') + p.strValue;
 17304      }
 17305  
 17306      if (type.validate && !type.validate(valArr, unitsArr)) {
 17307        return null;
 17308      }
 17309  
 17310      if (type.singleEnum && hasEnum) {
 17311        if (valArr.length === 1 && string(valArr[0])) {
 17312          return {
 17313            name: name,
 17314            value: valArr[0],
 17315            strValue: valArr[0],
 17316            bypass: propIsBypass
 17317          };
 17318        } else {
 17319          return null;
 17320        }
 17321      }
 17322  
 17323      return {
 17324        name: name,
 17325        value: valArr,
 17326        pfValue: pfValArr,
 17327        strValue: strVal,
 17328        bypass: propIsBypass,
 17329        units: unitsArr
 17330      };
 17331    } // several types also allow enums
 17332  
 17333  
 17334    var checkEnums = function checkEnums() {
 17335      for (var _i = 0; _i < type.enums.length; _i++) {
 17336        var en = type.enums[_i];
 17337  
 17338        if (en === value) {
 17339          return {
 17340            name: name,
 17341            value: value,
 17342            strValue: '' + value,
 17343            bypass: propIsBypass
 17344          };
 17345        }
 17346      }
 17347  
 17348      return null;
 17349    }; // check the type and return the appropriate object
 17350  
 17351  
 17352    if (type.number) {
 17353      var units;
 17354      var implicitUnits = 'px'; // not set => px
 17355  
 17356      if (type.units) {
 17357        // use specified units if set
 17358        units = type.units;
 17359      }
 17360  
 17361      if (type.implicitUnits) {
 17362        implicitUnits = type.implicitUnits;
 17363      }
 17364  
 17365      if (!type.unitless) {
 17366        if (valueIsString) {
 17367          var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
 17368  
 17369          if (units) {
 17370            unitsRegex = units;
 17371          } // only allow explicit units if so set
 17372  
 17373  
 17374          var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
 17375  
 17376          if (match) {
 17377            value = match[1];
 17378            units = match[2] || implicitUnits;
 17379          }
 17380        } else if (!units || type.implicitUnits) {
 17381          units = implicitUnits; // implicitly px if unspecified
 17382        }
 17383      }
 17384  
 17385      value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
 17386  
 17387      if (isNaN(value) && type.enums === undefined) {
 17388        return null;
 17389      } // check if this number type also accepts special keywords in place of numbers
 17390      // (i.e. `left`, `auto`, etc)
 17391  
 17392  
 17393      if (isNaN(value) && type.enums !== undefined) {
 17394        value = passedValue;
 17395        return checkEnums();
 17396      } // check if value must be an integer
 17397  
 17398  
 17399      if (type.integer && !integer(value)) {
 17400        return null;
 17401      } // check value is within range
 17402  
 17403  
 17404      if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
 17405        return null;
 17406      }
 17407  
 17408      var ret = {
 17409        name: name,
 17410        value: value,
 17411        strValue: '' + value + (units ? units : ''),
 17412        units: units,
 17413        bypass: propIsBypass
 17414      }; // normalise value in pixels
 17415  
 17416      if (type.unitless || units !== 'px' && units !== 'em') {
 17417        ret.pfValue = value;
 17418      } else {
 17419        ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
 17420      } // normalise value in ms
 17421  
 17422  
 17423      if (units === 'ms' || units === 's') {
 17424        ret.pfValue = units === 'ms' ? value : 1000 * value;
 17425      } // normalise value in rad
 17426  
 17427  
 17428      if (units === 'deg' || units === 'rad') {
 17429        ret.pfValue = units === 'rad' ? value : deg2rad(value);
 17430      } // normalize value in %
 17431  
 17432  
 17433      if (units === '%') {
 17434        ret.pfValue = value / 100;
 17435      }
 17436  
 17437      return ret;
 17438    } else if (type.propList) {
 17439      var props = [];
 17440      var propsStr = '' + value;
 17441  
 17442      if (propsStr === 'none') ; else {
 17443        // go over each prop
 17444        var propsSplit = propsStr.split(/\s*,\s*|\s+/);
 17445  
 17446        for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
 17447          var propName = propsSplit[_i2].trim();
 17448  
 17449          if (self.properties[propName]) {
 17450            props.push(propName);
 17451          } else {
 17452            warn('`' + propName + '` is not a valid property name');
 17453          }
 17454        }
 17455  
 17456        if (props.length === 0) {
 17457          return null;
 17458        }
 17459      }
 17460  
 17461      return {
 17462        name: name,
 17463        value: props,
 17464        strValue: props.length === 0 ? 'none' : props.join(' '),
 17465        bypass: propIsBypass
 17466      };
 17467    } else if (type.color) {
 17468      var tuple = color2tuple(value);
 17469  
 17470      if (!tuple) {
 17471        return null;
 17472      }
 17473  
 17474      return {
 17475        name: name,
 17476        value: tuple,
 17477        pfValue: tuple,
 17478        strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
 17479        // n.b. no spaces b/c of multiple support
 17480        bypass: propIsBypass
 17481      };
 17482    } else if (type.regex || type.regexes) {
 17483      // first check enums
 17484      if (type.enums) {
 17485        var enumProp = checkEnums();
 17486  
 17487        if (enumProp) {
 17488          return enumProp;
 17489        }
 17490      }
 17491  
 17492      var regexes = type.regexes ? type.regexes : [type.regex];
 17493  
 17494      for (var _i3 = 0; _i3 < regexes.length; _i3++) {
 17495        var regex = new RegExp(regexes[_i3]); // make a regex from the type string
 17496  
 17497        var m = regex.exec(value);
 17498  
 17499        if (m) {
 17500          // regex matches
 17501          return {
 17502            name: name,
 17503            value: type.singleRegexMatchValue ? m[1] : m,
 17504            strValue: '' + value,
 17505            bypass: propIsBypass
 17506          };
 17507        }
 17508      }
 17509  
 17510      return null; // didn't match any
 17511    } else if (type.string) {
 17512      // just return
 17513      return {
 17514        name: name,
 17515        value: '' + value,
 17516        strValue: '' + value,
 17517        bypass: propIsBypass
 17518      };
 17519    } else if (type.enums) {
 17520      // check enums last because it's a combo type in others
 17521      return checkEnums();
 17522    } else {
 17523      return null; // not a type we can handle
 17524    }
 17525  };
 17526  
 17527  var Style = function Style(cy) {
 17528    if (!(this instanceof Style)) {
 17529      return new Style(cy);
 17530    }
 17531  
 17532    if (!core(cy)) {
 17533      error('A style must have a core reference');
 17534      return;
 17535    }
 17536  
 17537    this._private = {
 17538      cy: cy,
 17539      coreStyle: {}
 17540    };
 17541    this.length = 0;
 17542    this.resetToDefault();
 17543  };
 17544  
 17545  var styfn$8 = Style.prototype;
 17546  
 17547  styfn$8.instanceString = function () {
 17548    return 'style';
 17549  }; // remove all contexts
 17550  
 17551  
 17552  styfn$8.clear = function () {
 17553    for (var i = 0; i < this.length; i++) {
 17554      this[i] = undefined;
 17555    }
 17556  
 17557    this.length = 0;
 17558    var _p = this._private;
 17559    _p.newStyle = true;
 17560    return this; // chaining
 17561  };
 17562  
 17563  styfn$8.resetToDefault = function () {
 17564    this.clear();
 17565    this.addDefaultStylesheet();
 17566    return this;
 17567  }; // builds a style object for the 'core' selector
 17568  
 17569  
 17570  styfn$8.core = function (propName) {
 17571    return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
 17572  }; // create a new context from the specified selector string and switch to that context
 17573  
 17574  
 17575  styfn$8.selector = function (selectorStr) {
 17576    // 'core' is a special case and does not need a selector
 17577    var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
 17578    var i = this.length++; // new context means new index
 17579  
 17580    this[i] = {
 17581      selector: selector,
 17582      properties: [],
 17583      mappedProperties: [],
 17584      index: i
 17585    };
 17586    return this; // chaining
 17587  }; // add one or many css rules to the current context
 17588  
 17589  
 17590  styfn$8.css = function () {
 17591    var self = this;
 17592    var args = arguments;
 17593  
 17594    if (args.length === 1) {
 17595      var map = args[0];
 17596  
 17597      for (var i = 0; i < self.properties.length; i++) {
 17598        var prop = self.properties[i];
 17599        var mapVal = map[prop.name];
 17600  
 17601        if (mapVal === undefined) {
 17602          mapVal = map[dash2camel(prop.name)];
 17603        }
 17604  
 17605        if (mapVal !== undefined) {
 17606          this.cssRule(prop.name, mapVal);
 17607        }
 17608      }
 17609    } else if (args.length === 2) {
 17610      this.cssRule(args[0], args[1]);
 17611    } // do nothing if args are invalid
 17612  
 17613  
 17614    return this; // chaining
 17615  };
 17616  
 17617  styfn$8.style = styfn$8.css; // add a single css rule to the current context
 17618  
 17619  styfn$8.cssRule = function (name, value) {
 17620    // name-value pair
 17621    var property = this.parse(name, value); // add property to current context if valid
 17622  
 17623    if (property) {
 17624      var i = this.length - 1;
 17625      this[i].properties.push(property);
 17626      this[i].properties[property.name] = property; // allow access by name as well
 17627  
 17628      if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
 17629        this._private.hasPie = true;
 17630      }
 17631  
 17632      if (property.mapped) {
 17633        this[i].mappedProperties.push(property);
 17634      } // add to core style if necessary
 17635  
 17636  
 17637      var currentSelectorIsCore = !this[i].selector;
 17638  
 17639      if (currentSelectorIsCore) {
 17640        this._private.coreStyle[property.name] = property;
 17641      }
 17642    }
 17643  
 17644    return this; // chaining
 17645  };
 17646  
 17647  styfn$8.append = function (style) {
 17648    if (stylesheet(style)) {
 17649      style.appendToStyle(this);
 17650    } else if (array(style)) {
 17651      this.appendFromJson(style);
 17652    } else if (string(style)) {
 17653      this.appendFromString(style);
 17654    } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
 17655  
 17656  
 17657    return this;
 17658  }; // static function
 17659  
 17660  
 17661  Style.fromJson = function (cy, json) {
 17662    var style = new Style(cy);
 17663    style.fromJson(json);
 17664    return style;
 17665  };
 17666  
 17667  Style.fromString = function (cy, string) {
 17668    return new Style(cy).fromString(string);
 17669  };
 17670  
 17671  [styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
 17672    extend(styfn$8, props);
 17673  });
 17674  Style.types = styfn$8.types;
 17675  Style.properties = styfn$8.properties;
 17676  Style.propertyGroups = styfn$8.propertyGroups;
 17677  Style.propertyGroupNames = styfn$8.propertyGroupNames;
 17678  Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
 17679  
 17680  var corefn$7 = {
 17681    style: function style(newStyle) {
 17682      if (newStyle) {
 17683        var s = this.setStyle(newStyle);
 17684        s.update();
 17685      }
 17686  
 17687      return this._private.style;
 17688    },
 17689    setStyle: function setStyle(style) {
 17690      var _p = this._private;
 17691  
 17692      if (stylesheet(style)) {
 17693        _p.style = style.generateStyle(this);
 17694      } else if (array(style)) {
 17695        _p.style = Style.fromJson(this, style);
 17696      } else if (string(style)) {
 17697        _p.style = Style.fromString(this, style);
 17698      } else {
 17699        _p.style = Style(this);
 17700      }
 17701  
 17702      return _p.style;
 17703    }
 17704  };
 17705  
 17706  var defaultSelectionType = 'single';
 17707  var corefn$8 = {
 17708    autolock: function autolock(bool) {
 17709      if (bool !== undefined) {
 17710        this._private.autolock = bool ? true : false;
 17711      } else {
 17712        return this._private.autolock;
 17713      }
 17714  
 17715      return this; // chaining
 17716    },
 17717    autoungrabify: function autoungrabify(bool) {
 17718      if (bool !== undefined) {
 17719        this._private.autoungrabify = bool ? true : false;
 17720      } else {
 17721        return this._private.autoungrabify;
 17722      }
 17723  
 17724      return this; // chaining
 17725    },
 17726    autounselectify: function autounselectify(bool) {
 17727      if (bool !== undefined) {
 17728        this._private.autounselectify = bool ? true : false;
 17729      } else {
 17730        return this._private.autounselectify;
 17731      }
 17732  
 17733      return this; // chaining
 17734    },
 17735    selectionType: function selectionType(selType) {
 17736      var _p = this._private;
 17737  
 17738      if (_p.selectionType == null) {
 17739        _p.selectionType = defaultSelectionType;
 17740      }
 17741  
 17742      if (selType !== undefined) {
 17743        if (selType === 'additive' || selType === 'single') {
 17744          _p.selectionType = selType;
 17745        }
 17746      } else {
 17747        return _p.selectionType;
 17748      }
 17749  
 17750      return this;
 17751    },
 17752    panningEnabled: function panningEnabled(bool) {
 17753      if (bool !== undefined) {
 17754        this._private.panningEnabled = bool ? true : false;
 17755      } else {
 17756        return this._private.panningEnabled;
 17757      }
 17758  
 17759      return this; // chaining
 17760    },
 17761    userPanningEnabled: function userPanningEnabled(bool) {
 17762      if (bool !== undefined) {
 17763        this._private.userPanningEnabled = bool ? true : false;
 17764      } else {
 17765        return this._private.userPanningEnabled;
 17766      }
 17767  
 17768      return this; // chaining
 17769    },
 17770    zoomingEnabled: function zoomingEnabled(bool) {
 17771      if (bool !== undefined) {
 17772        this._private.zoomingEnabled = bool ? true : false;
 17773      } else {
 17774        return this._private.zoomingEnabled;
 17775      }
 17776  
 17777      return this; // chaining
 17778    },
 17779    userZoomingEnabled: function userZoomingEnabled(bool) {
 17780      if (bool !== undefined) {
 17781        this._private.userZoomingEnabled = bool ? true : false;
 17782      } else {
 17783        return this._private.userZoomingEnabled;
 17784      }
 17785  
 17786      return this; // chaining
 17787    },
 17788    boxSelectionEnabled: function boxSelectionEnabled(bool) {
 17789      if (bool !== undefined) {
 17790        this._private.boxSelectionEnabled = bool ? true : false;
 17791      } else {
 17792        return this._private.boxSelectionEnabled;
 17793      }
 17794  
 17795      return this; // chaining
 17796    },
 17797    pan: function pan() {
 17798      var args = arguments;
 17799      var pan = this._private.pan;
 17800      var dim, val, dims, x, y;
 17801  
 17802      switch (args.length) {
 17803        case 0:
 17804          // .pan()
 17805          return pan;
 17806  
 17807        case 1:
 17808          if (string(args[0])) {
 17809            // .pan('x')
 17810            dim = args[0];
 17811            return pan[dim];
 17812          } else if (plainObject(args[0])) {
 17813            // .pan({ x: 0, y: 100 })
 17814            if (!this._private.panningEnabled) {
 17815              return this;
 17816            }
 17817  
 17818            dims = args[0];
 17819            x = dims.x;
 17820            y = dims.y;
 17821  
 17822            if (number(x)) {
 17823              pan.x = x;
 17824            }
 17825  
 17826            if (number(y)) {
 17827              pan.y = y;
 17828            }
 17829  
 17830            this.emit('pan viewport');
 17831          }
 17832  
 17833          break;
 17834  
 17835        case 2:
 17836          // .pan('x', 100)
 17837          if (!this._private.panningEnabled) {
 17838            return this;
 17839          }
 17840  
 17841          dim = args[0];
 17842          val = args[1];
 17843  
 17844          if ((dim === 'x' || dim === 'y') && number(val)) {
 17845            pan[dim] = val;
 17846          }
 17847  
 17848          this.emit('pan viewport');
 17849          break;
 17850        // invalid
 17851      }
 17852  
 17853      this.notify('viewport');
 17854      return this; // chaining
 17855    },
 17856    panBy: function panBy(arg0, arg1) {
 17857      var args = arguments;
 17858      var pan = this._private.pan;
 17859      var dim, val, dims, x, y;
 17860  
 17861      if (!this._private.panningEnabled) {
 17862        return this;
 17863      }
 17864  
 17865      switch (args.length) {
 17866        case 1:
 17867          if (plainObject(arg0)) {
 17868            // .panBy({ x: 0, y: 100 })
 17869            dims = args[0];
 17870            x = dims.x;
 17871            y = dims.y;
 17872  
 17873            if (number(x)) {
 17874              pan.x += x;
 17875            }
 17876  
 17877            if (number(y)) {
 17878              pan.y += y;
 17879            }
 17880  
 17881            this.emit('pan viewport');
 17882          }
 17883  
 17884          break;
 17885  
 17886        case 2:
 17887          // .panBy('x', 100)
 17888          dim = arg0;
 17889          val = arg1;
 17890  
 17891          if ((dim === 'x' || dim === 'y') && number(val)) {
 17892            pan[dim] += val;
 17893          }
 17894  
 17895          this.emit('pan viewport');
 17896          break;
 17897        // invalid
 17898      }
 17899  
 17900      this.notify('viewport');
 17901      return this; // chaining
 17902    },
 17903    fit: function fit(elements, padding) {
 17904      var viewportState = this.getFitViewport(elements, padding);
 17905  
 17906      if (viewportState) {
 17907        var _p = this._private;
 17908        _p.zoom = viewportState.zoom;
 17909        _p.pan = viewportState.pan;
 17910        this.emit('pan zoom viewport');
 17911        this.notify('viewport');
 17912      }
 17913  
 17914      return this; // chaining
 17915    },
 17916    getFitViewport: function getFitViewport(elements, padding) {
 17917      if (number(elements) && padding === undefined) {
 17918        // elements is optional
 17919        padding = elements;
 17920        elements = undefined;
 17921      }
 17922  
 17923      if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
 17924        return;
 17925      }
 17926  
 17927      var bb;
 17928  
 17929      if (string(elements)) {
 17930        var sel = elements;
 17931        elements = this.$(sel);
 17932      } else if (boundingBox(elements)) {
 17933        // assume bb
 17934        var bbe = elements;
 17935        bb = {
 17936          x1: bbe.x1,
 17937          y1: bbe.y1,
 17938          x2: bbe.x2,
 17939          y2: bbe.y2
 17940        };
 17941        bb.w = bb.x2 - bb.x1;
 17942        bb.h = bb.y2 - bb.y1;
 17943      } else if (!elementOrCollection(elements)) {
 17944        elements = this.mutableElements();
 17945      }
 17946  
 17947      if (elementOrCollection(elements) && elements.empty()) {
 17948        return;
 17949      } // can't fit to nothing
 17950  
 17951  
 17952      bb = bb || elements.boundingBox();
 17953      var w = this.width();
 17954      var h = this.height();
 17955      var zoom;
 17956      padding = number(padding) ? padding : 0;
 17957  
 17958      if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
 17959        zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
 17960  
 17961        zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
 17962        zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
 17963        var pan = {
 17964          // now pan to middle
 17965          x: (w - zoom * (bb.x1 + bb.x2)) / 2,
 17966          y: (h - zoom * (bb.y1 + bb.y2)) / 2
 17967        };
 17968        return {
 17969          zoom: zoom,
 17970          pan: pan
 17971        };
 17972      }
 17973  
 17974      return;
 17975    },
 17976    zoomRange: function zoomRange(min, max) {
 17977      var _p = this._private;
 17978  
 17979      if (max == null) {
 17980        var opts = min;
 17981        min = opts.min;
 17982        max = opts.max;
 17983      }
 17984  
 17985      if (number(min) && number(max) && min <= max) {
 17986        _p.minZoom = min;
 17987        _p.maxZoom = max;
 17988      } else if (number(min) && max === undefined && min <= _p.maxZoom) {
 17989        _p.minZoom = min;
 17990      } else if (number(max) && min === undefined && max >= _p.minZoom) {
 17991        _p.maxZoom = max;
 17992      }
 17993  
 17994      return this;
 17995    },
 17996    minZoom: function minZoom(zoom) {
 17997      if (zoom === undefined) {
 17998        return this._private.minZoom;
 17999      } else {
 18000        return this.zoomRange({
 18001          min: zoom
 18002        });
 18003      }
 18004    },
 18005    maxZoom: function maxZoom(zoom) {
 18006      if (zoom === undefined) {
 18007        return this._private.maxZoom;
 18008      } else {
 18009        return this.zoomRange({
 18010          max: zoom
 18011        });
 18012      }
 18013    },
 18014    getZoomedViewport: function getZoomedViewport(params) {
 18015      var _p = this._private;
 18016      var currentPan = _p.pan;
 18017      var currentZoom = _p.zoom;
 18018      var pos; // in rendered px
 18019  
 18020      var zoom;
 18021      var bail = false;
 18022  
 18023      if (!_p.zoomingEnabled) {
 18024        // zooming disabled
 18025        bail = true;
 18026      }
 18027  
 18028      if (number(params)) {
 18029        // then set the zoom
 18030        zoom = params;
 18031      } else if (plainObject(params)) {
 18032        // then zoom about a point
 18033        zoom = params.level;
 18034  
 18035        if (params.position != null) {
 18036          pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
 18037        } else if (params.renderedPosition != null) {
 18038          pos = params.renderedPosition;
 18039        }
 18040  
 18041        if (pos != null && !_p.panningEnabled) {
 18042          // panning disabled
 18043          bail = true;
 18044        }
 18045      } // crop zoom
 18046  
 18047  
 18048      zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
 18049      zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
 18050  
 18051      if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
 18052        return null;
 18053      }
 18054  
 18055      if (pos != null) {
 18056        // set zoom about position
 18057        var pan1 = currentPan;
 18058        var zoom1 = currentZoom;
 18059        var zoom2 = zoom;
 18060        var pan2 = {
 18061          x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
 18062          y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
 18063        };
 18064        return {
 18065          zoomed: true,
 18066          panned: true,
 18067          zoom: zoom2,
 18068          pan: pan2
 18069        };
 18070      } else {
 18071        // just set the zoom
 18072        return {
 18073          zoomed: true,
 18074          panned: false,
 18075          zoom: zoom,
 18076          pan: currentPan
 18077        };
 18078      }
 18079    },
 18080    zoom: function zoom(params) {
 18081      if (params === undefined) {
 18082        // get
 18083        return this._private.zoom;
 18084      } else {
 18085        // set
 18086        var vp = this.getZoomedViewport(params);
 18087        var _p = this._private;
 18088  
 18089        if (vp == null || !vp.zoomed) {
 18090          return this;
 18091        }
 18092  
 18093        _p.zoom = vp.zoom;
 18094  
 18095        if (vp.panned) {
 18096          _p.pan.x = vp.pan.x;
 18097          _p.pan.y = vp.pan.y;
 18098        }
 18099  
 18100        this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
 18101        this.notify('viewport');
 18102        return this; // chaining
 18103      }
 18104    },
 18105    viewport: function viewport(opts) {
 18106      var _p = this._private;
 18107      var zoomDefd = true;
 18108      var panDefd = true;
 18109      var events = []; // to trigger
 18110  
 18111      var zoomFailed = false;
 18112      var panFailed = false;
 18113  
 18114      if (!opts) {
 18115        return this;
 18116      }
 18117  
 18118      if (!number(opts.zoom)) {
 18119        zoomDefd = false;
 18120      }
 18121  
 18122      if (!plainObject(opts.pan)) {
 18123        panDefd = false;
 18124      }
 18125  
 18126      if (!zoomDefd && !panDefd) {
 18127        return this;
 18128      }
 18129  
 18130      if (zoomDefd) {
 18131        var z = opts.zoom;
 18132  
 18133        if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
 18134          zoomFailed = true;
 18135        } else {
 18136          _p.zoom = z;
 18137          events.push('zoom');
 18138        }
 18139      }
 18140  
 18141      if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
 18142        var p = opts.pan;
 18143  
 18144        if (number(p.x)) {
 18145          _p.pan.x = p.x;
 18146          panFailed = false;
 18147        }
 18148  
 18149        if (number(p.y)) {
 18150          _p.pan.y = p.y;
 18151          panFailed = false;
 18152        }
 18153  
 18154        if (!panFailed) {
 18155          events.push('pan');
 18156        }
 18157      }
 18158  
 18159      if (events.length > 0) {
 18160        events.push('viewport');
 18161        this.emit(events.join(' '));
 18162        this.notify('viewport');
 18163      }
 18164  
 18165      return this; // chaining
 18166    },
 18167    center: function center(elements) {
 18168      var pan = this.getCenterPan(elements);
 18169  
 18170      if (pan) {
 18171        this._private.pan = pan;
 18172        this.emit('pan viewport');
 18173        this.notify('viewport');
 18174      }
 18175  
 18176      return this; // chaining
 18177    },
 18178    getCenterPan: function getCenterPan(elements, zoom) {
 18179      if (!this._private.panningEnabled) {
 18180        return;
 18181      }
 18182  
 18183      if (string(elements)) {
 18184        var selector = elements;
 18185        elements = this.mutableElements().filter(selector);
 18186      } else if (!elementOrCollection(elements)) {
 18187        elements = this.mutableElements();
 18188      }
 18189  
 18190      if (elements.length === 0) {
 18191        return;
 18192      } // can't centre pan to nothing
 18193  
 18194  
 18195      var bb = elements.boundingBox();
 18196      var w = this.width();
 18197      var h = this.height();
 18198      zoom = zoom === undefined ? this._private.zoom : zoom;
 18199      var pan = {
 18200        // middle
 18201        x: (w - zoom * (bb.x1 + bb.x2)) / 2,
 18202        y: (h - zoom * (bb.y1 + bb.y2)) / 2
 18203      };
 18204      return pan;
 18205    },
 18206    reset: function reset() {
 18207      if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
 18208        return this;
 18209      }
 18210  
 18211      this.viewport({
 18212        pan: {
 18213          x: 0,
 18214          y: 0
 18215        },
 18216        zoom: 1
 18217      });
 18218      return this; // chaining
 18219    },
 18220    invalidateSize: function invalidateSize() {
 18221      this._private.sizeCache = null;
 18222    },
 18223    size: function size() {
 18224      var _p = this._private;
 18225      var container = _p.container;
 18226      return _p.sizeCache = _p.sizeCache || (container ? function () {
 18227        var style = window$1.getComputedStyle(container);
 18228  
 18229        var val = function val(name) {
 18230          return parseFloat(style.getPropertyValue(name));
 18231        };
 18232  
 18233        return {
 18234          width: container.clientWidth - val('padding-left') - val('padding-right'),
 18235          height: container.clientHeight - val('padding-top') - val('padding-bottom')
 18236        };
 18237      }() : {
 18238        // fallback if no container (not 0 b/c can be used for dividing etc)
 18239        width: 1,
 18240        height: 1
 18241      });
 18242    },
 18243    width: function width() {
 18244      return this.size().width;
 18245    },
 18246    height: function height() {
 18247      return this.size().height;
 18248    },
 18249    extent: function extent() {
 18250      var pan = this._private.pan;
 18251      var zoom = this._private.zoom;
 18252      var rb = this.renderedExtent();
 18253      var b = {
 18254        x1: (rb.x1 - pan.x) / zoom,
 18255        x2: (rb.x2 - pan.x) / zoom,
 18256        y1: (rb.y1 - pan.y) / zoom,
 18257        y2: (rb.y2 - pan.y) / zoom
 18258      };
 18259      b.w = b.x2 - b.x1;
 18260      b.h = b.y2 - b.y1;
 18261      return b;
 18262    },
 18263    renderedExtent: function renderedExtent() {
 18264      var width = this.width();
 18265      var height = this.height();
 18266      return {
 18267        x1: 0,
 18268        y1: 0,
 18269        x2: width,
 18270        y2: height,
 18271        w: width,
 18272        h: height
 18273      };
 18274    }
 18275  }; // aliases
 18276  
 18277  corefn$8.centre = corefn$8.center; // backwards compatibility
 18278  
 18279  corefn$8.autolockNodes = corefn$8.autolock;
 18280  corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
 18281  
 18282  var fn$6 = {
 18283    data: define$3.data({
 18284      field: 'data',
 18285      bindingEvent: 'data',
 18286      allowBinding: true,
 18287      allowSetting: true,
 18288      settingEvent: 'data',
 18289      settingTriggersEvent: true,
 18290      triggerFnName: 'trigger',
 18291      allowGetting: true
 18292    }),
 18293    removeData: define$3.removeData({
 18294      field: 'data',
 18295      event: 'data',
 18296      triggerFnName: 'trigger',
 18297      triggerEvent: true
 18298    }),
 18299    scratch: define$3.data({
 18300      field: 'scratch',
 18301      bindingEvent: 'scratch',
 18302      allowBinding: true,
 18303      allowSetting: true,
 18304      settingEvent: 'scratch',
 18305      settingTriggersEvent: true,
 18306      triggerFnName: 'trigger',
 18307      allowGetting: true
 18308    }),
 18309    removeScratch: define$3.removeData({
 18310      field: 'scratch',
 18311      event: 'scratch',
 18312      triggerFnName: 'trigger',
 18313      triggerEvent: true
 18314    })
 18315  }; // aliases
 18316  
 18317  fn$6.attr = fn$6.data;
 18318  fn$6.removeAttr = fn$6.removeData;
 18319  
 18320  var Core = function Core(opts) {
 18321    var cy = this;
 18322    opts = extend({}, opts);
 18323    var container = opts.container; // allow for passing a wrapped jquery object
 18324    // e.g. cytoscape({ container: $('#cy') })
 18325  
 18326    if (container && !htmlElement(container) && htmlElement(container[0])) {
 18327      container = container[0];
 18328    }
 18329  
 18330    var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
 18331  
 18332    reg = reg || {};
 18333  
 18334    if (reg && reg.cy) {
 18335      reg.cy.destroy();
 18336      reg = {}; // old instance => replace reg completely
 18337    }
 18338  
 18339    var readies = reg.readies = reg.readies || [];
 18340  
 18341    if (container) {
 18342      container._cyreg = reg;
 18343    } // make sure container assoc'd reg points to this cy
 18344  
 18345  
 18346    reg.cy = cy;
 18347    var head = window$1 !== undefined && container !== undefined && !opts.headless;
 18348    var options = opts;
 18349    options.layout = extend({
 18350      name: head ? 'grid' : 'null'
 18351    }, options.layout);
 18352    options.renderer = extend({
 18353      name: head ? 'canvas' : 'null'
 18354    }, options.renderer);
 18355  
 18356    var defVal = function defVal(def, val, altVal) {
 18357      if (val !== undefined) {
 18358        return val;
 18359      } else if (altVal !== undefined) {
 18360        return altVal;
 18361      } else {
 18362        return def;
 18363      }
 18364    };
 18365  
 18366    var _p = this._private = {
 18367      container: container,
 18368      // html dom ele container
 18369      ready: false,
 18370      // whether ready has been triggered
 18371      options: options,
 18372      // cached options
 18373      elements: new Collection(this),
 18374      // elements in the graph
 18375      listeners: [],
 18376      // list of listeners
 18377      aniEles: new Collection(this),
 18378      // elements being animated
 18379      data: {},
 18380      // data for the core
 18381      scratch: {},
 18382      // scratch object for core
 18383      layout: null,
 18384      renderer: null,
 18385      destroyed: false,
 18386      // whether destroy was called
 18387      notificationsEnabled: true,
 18388      // whether notifications are sent to the renderer
 18389      minZoom: 1e-50,
 18390      maxZoom: 1e50,
 18391      zoomingEnabled: defVal(true, options.zoomingEnabled),
 18392      userZoomingEnabled: defVal(true, options.userZoomingEnabled),
 18393      panningEnabled: defVal(true, options.panningEnabled),
 18394      userPanningEnabled: defVal(true, options.userPanningEnabled),
 18395      boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
 18396      autolock: defVal(false, options.autolock, options.autolockNodes),
 18397      autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
 18398      autounselectify: defVal(false, options.autounselectify),
 18399      styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
 18400      zoom: number(options.zoom) ? options.zoom : 1,
 18401      pan: {
 18402        x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
 18403        y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
 18404      },
 18405      animation: {
 18406        // object for currently-running animations
 18407        current: [],
 18408        queue: []
 18409      },
 18410      hasCompoundNodes: false
 18411    };
 18412  
 18413    this.createEmitter(); // set selection type
 18414  
 18415    this.selectionType(options.selectionType); // init zoom bounds
 18416  
 18417    this.zoomRange({
 18418      min: options.minZoom,
 18419      max: options.maxZoom
 18420    });
 18421  
 18422    var loadExtData = function loadExtData(extData, next) {
 18423      var anyIsPromise = extData.some(promise);
 18424  
 18425      if (anyIsPromise) {
 18426        return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
 18427      } else {
 18428        next(extData); // exec synchronously for convenience
 18429      }
 18430    }; // start with the default stylesheet so we have something before loading an external stylesheet
 18431  
 18432  
 18433    if (_p.styleEnabled) {
 18434      cy.setStyle([]);
 18435    } // create the renderer
 18436  
 18437  
 18438    var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
 18439  
 18440    cy.initRenderer(rendererOptions);
 18441  
 18442    var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
 18443      cy.notifications(false); // remove old elements
 18444  
 18445      var oldEles = cy.mutableElements();
 18446  
 18447      if (oldEles.length > 0) {
 18448        oldEles.remove();
 18449      }
 18450  
 18451      if (elements != null) {
 18452        if (plainObject(elements) || array(elements)) {
 18453          cy.add(elements);
 18454        }
 18455      }
 18456  
 18457      cy.one('layoutready', function (e) {
 18458        cy.notifications(true);
 18459        cy.emit(e); // we missed this event by turning notifications off, so pass it on
 18460  
 18461        cy.one('load', onload);
 18462        cy.emitAndNotify('load');
 18463      }).one('layoutstop', function () {
 18464        cy.one('done', ondone);
 18465        cy.emit('done');
 18466      });
 18467      var layoutOpts = extend({}, cy._private.options.layout);
 18468      layoutOpts.eles = cy.elements();
 18469      cy.layout(layoutOpts).run();
 18470    };
 18471  
 18472    loadExtData([options.style, options.elements], function (thens) {
 18473      var initStyle = thens[0];
 18474      var initEles = thens[1]; // init style
 18475  
 18476      if (_p.styleEnabled) {
 18477        cy.style().append(initStyle);
 18478      } // initial load
 18479  
 18480  
 18481      setElesAndLayout(initEles, function () {
 18482        // onready
 18483        cy.startAnimationLoop();
 18484        _p.ready = true; // if a ready callback is specified as an option, the bind it
 18485  
 18486        if (fn(options.ready)) {
 18487          cy.on('ready', options.ready);
 18488        } // bind all the ready handlers registered before creating this instance
 18489  
 18490  
 18491        for (var i = 0; i < readies.length; i++) {
 18492          var fn$1 = readies[i];
 18493          cy.on('ready', fn$1);
 18494        }
 18495  
 18496        if (reg) {
 18497          reg.readies = [];
 18498        } // 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
 18499  
 18500  
 18501        cy.emit('ready');
 18502      }, options.done);
 18503    });
 18504  };
 18505  
 18506  var corefn$9 = Core.prototype; // short alias
 18507  
 18508  extend(corefn$9, {
 18509    instanceString: function instanceString() {
 18510      return 'core';
 18511    },
 18512    isReady: function isReady() {
 18513      return this._private.ready;
 18514    },
 18515    destroyed: function destroyed() {
 18516      return this._private.destroyed;
 18517    },
 18518    ready: function ready(fn) {
 18519      if (this.isReady()) {
 18520        this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
 18521      } else {
 18522        this.on('ready', fn);
 18523      }
 18524  
 18525      return this;
 18526    },
 18527    destroy: function destroy() {
 18528      var cy = this;
 18529      if (cy.destroyed()) return;
 18530      cy.stopAnimationLoop();
 18531      cy.destroyRenderer();
 18532      this.emit('destroy');
 18533      cy._private.destroyed = true;
 18534      return cy;
 18535    },
 18536    hasElementWithId: function hasElementWithId(id) {
 18537      return this._private.elements.hasElementWithId(id);
 18538    },
 18539    getElementById: function getElementById(id) {
 18540      return this._private.elements.getElementById(id);
 18541    },
 18542    hasCompoundNodes: function hasCompoundNodes() {
 18543      return this._private.hasCompoundNodes;
 18544    },
 18545    headless: function headless() {
 18546      return this._private.renderer.isHeadless();
 18547    },
 18548    styleEnabled: function styleEnabled() {
 18549      return this._private.styleEnabled;
 18550    },
 18551    addToPool: function addToPool(eles) {
 18552      this._private.elements.merge(eles);
 18553  
 18554      return this; // chaining
 18555    },
 18556    removeFromPool: function removeFromPool(eles) {
 18557      this._private.elements.unmerge(eles);
 18558  
 18559      return this;
 18560    },
 18561    container: function container() {
 18562      return this._private.container || null;
 18563    },
 18564    mount: function mount(container) {
 18565      if (container == null) {
 18566        return;
 18567      }
 18568  
 18569      var cy = this;
 18570      var _p = cy._private;
 18571      var options = _p.options;
 18572  
 18573      if (!htmlElement(container) && htmlElement(container[0])) {
 18574        container = container[0];
 18575      }
 18576  
 18577      cy.stopAnimationLoop();
 18578      cy.destroyRenderer();
 18579      _p.container = container;
 18580      _p.styleEnabled = true;
 18581      cy.invalidateSize();
 18582      cy.initRenderer(extend({}, options, options.renderer, {
 18583        // allow custom renderer name to be re-used, otherwise use canvas
 18584        name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
 18585      }));
 18586      cy.startAnimationLoop();
 18587      cy.style(options.style);
 18588      cy.emit('mount');
 18589      return cy;
 18590    },
 18591    unmount: function unmount() {
 18592      var cy = this;
 18593      cy.stopAnimationLoop();
 18594      cy.destroyRenderer();
 18595      cy.initRenderer({
 18596        name: 'null'
 18597      });
 18598      cy.emit('unmount');
 18599      return cy;
 18600    },
 18601    options: function options() {
 18602      return copy(this._private.options);
 18603    },
 18604    json: function json(obj) {
 18605      var cy = this;
 18606      var _p = cy._private;
 18607      var eles = cy.mutableElements();
 18608  
 18609      var getFreshRef = function getFreshRef(ele) {
 18610        return cy.getElementById(ele.id());
 18611      };
 18612  
 18613      if (plainObject(obj)) {
 18614        // set
 18615        cy.startBatch();
 18616  
 18617        if (obj.elements) {
 18618          var idInJson = {};
 18619  
 18620          var updateEles = function updateEles(jsons, gr) {
 18621            var toAdd = [];
 18622            var toMod = [];
 18623  
 18624            for (var i = 0; i < jsons.length; i++) {
 18625              var json = jsons[i];
 18626              var id = '' + json.data.id; // id must be string
 18627  
 18628              var ele = cy.getElementById(id);
 18629              idInJson[id] = true;
 18630  
 18631              if (ele.length !== 0) {
 18632                // existing element should be updated
 18633                toMod.push({
 18634                  ele: ele,
 18635                  json: json
 18636                });
 18637              } else {
 18638                // otherwise should be added
 18639                if (gr) {
 18640                  json.group = gr;
 18641                  toAdd.push(json);
 18642                } else {
 18643                  toAdd.push(json);
 18644                }
 18645              }
 18646            }
 18647  
 18648            cy.add(toAdd);
 18649  
 18650            for (var _i = 0; _i < toMod.length; _i++) {
 18651              var _toMod$_i = toMod[_i],
 18652                  _ele = _toMod$_i.ele,
 18653                  _json = _toMod$_i.json;
 18654  
 18655              _ele.json(_json);
 18656            }
 18657          };
 18658  
 18659          if (array(obj.elements)) {
 18660            // elements: []
 18661            updateEles(obj.elements);
 18662          } else {
 18663            // elements: { nodes: [], edges: [] }
 18664            var grs = ['nodes', 'edges'];
 18665  
 18666            for (var i = 0; i < grs.length; i++) {
 18667              var gr = grs[i];
 18668              var elements = obj.elements[gr];
 18669  
 18670              if (array(elements)) {
 18671                updateEles(elements, gr);
 18672              }
 18673            }
 18674          }
 18675  
 18676          var parentsToRemove = cy.collection();
 18677          eles.filter(function (ele) {
 18678            return !idInJson[ele.id()];
 18679          }).forEach(function (ele) {
 18680            if (ele.isParent()) {
 18681              parentsToRemove.merge(ele);
 18682            } else {
 18683              ele.remove();
 18684            }
 18685          }); // so that children are not removed w/parent
 18686  
 18687          parentsToRemove.forEach(function (ele) {
 18688            return ele.children().move({
 18689              parent: null
 18690            });
 18691          }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
 18692  
 18693          parentsToRemove.forEach(function (ele) {
 18694            return getFreshRef(ele).remove();
 18695          });
 18696        }
 18697  
 18698        if (obj.style) {
 18699          cy.style(obj.style);
 18700        }
 18701  
 18702        if (obj.zoom != null && obj.zoom !== _p.zoom) {
 18703          cy.zoom(obj.zoom);
 18704        }
 18705  
 18706        if (obj.pan) {
 18707          if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
 18708            cy.pan(obj.pan);
 18709          }
 18710        }
 18711  
 18712        if (obj.data) {
 18713          cy.data(obj.data);
 18714        }
 18715  
 18716        var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify'];
 18717  
 18718        for (var _i2 = 0; _i2 < fields.length; _i2++) {
 18719          var f = fields[_i2];
 18720  
 18721          if (obj[f] != null) {
 18722            cy[f](obj[f]);
 18723          }
 18724        }
 18725  
 18726        cy.endBatch();
 18727        return this; // chaining
 18728      } else {
 18729        // get
 18730        var flat = !!obj;
 18731        var json = {};
 18732  
 18733        if (flat) {
 18734          json.elements = this.elements().map(function (ele) {
 18735            return ele.json();
 18736          });
 18737        } else {
 18738          json.elements = {};
 18739          eles.forEach(function (ele) {
 18740            var group = ele.group();
 18741  
 18742            if (!json.elements[group]) {
 18743              json.elements[group] = [];
 18744            }
 18745  
 18746            json.elements[group].push(ele.json());
 18747          });
 18748        }
 18749  
 18750        if (this._private.styleEnabled) {
 18751          json.style = cy.style().json();
 18752        }
 18753  
 18754        json.data = copy(cy.data());
 18755        var options = _p.options;
 18756        json.zoomingEnabled = _p.zoomingEnabled;
 18757        json.userZoomingEnabled = _p.userZoomingEnabled;
 18758        json.zoom = _p.zoom;
 18759        json.minZoom = _p.minZoom;
 18760        json.maxZoom = _p.maxZoom;
 18761        json.panningEnabled = _p.panningEnabled;
 18762        json.userPanningEnabled = _p.userPanningEnabled;
 18763        json.pan = copy(_p.pan);
 18764        json.boxSelectionEnabled = _p.boxSelectionEnabled;
 18765        json.renderer = copy(options.renderer);
 18766        json.hideEdgesOnViewport = options.hideEdgesOnViewport;
 18767        json.textureOnViewport = options.textureOnViewport;
 18768        json.wheelSensitivity = options.wheelSensitivity;
 18769        json.motionBlur = options.motionBlur;
 18770        return json;
 18771      }
 18772    }
 18773  });
 18774  corefn$9.$id = corefn$9.getElementById;
 18775  [corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
 18776    extend(corefn$9, props);
 18777  });
 18778  
 18779  /* eslint-disable no-unused-vars */
 18780  
 18781  var defaults$9 = {
 18782    fit: true,
 18783    // whether to fit the viewport to the graph
 18784    directed: false,
 18785    // whether the tree is directed downwards (or edges can point in any direction if false)
 18786    padding: 30,
 18787    // padding on fit
 18788    circle: false,
 18789    // put depths in concentric circles if true, put depths top down if false
 18790    grid: false,
 18791    // whether to create an even grid into which the DAG is placed (circle:false only)
 18792    spacingFactor: 1.75,
 18793    // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
 18794    boundingBox: undefined,
 18795    // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 18796    avoidOverlap: true,
 18797    // prevents node overlap, may overflow boundingBox if not enough space
 18798    nodeDimensionsIncludeLabels: false,
 18799    // Excludes the label when calculating node bounding boxes for the layout algorithm
 18800    roots: undefined,
 18801    // the roots of the trees
 18802    maximal: false,
 18803    // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
 18804    animate: false,
 18805    // whether to transition the node positions
 18806    animationDuration: 500,
 18807    // duration of animation in ms if enabled
 18808    animationEasing: undefined,
 18809    // easing of animation if enabled,
 18810    animateFilter: function animateFilter(node, i) {
 18811      return true;
 18812    },
 18813    // 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
 18814    ready: undefined,
 18815    // callback on layoutready
 18816    stop: undefined,
 18817    // callback on layoutstop
 18818    transform: function transform(node, position) {
 18819      return position;
 18820    } // transform a given node position. Useful for changing flow direction in discrete layouts
 18821  
 18822  };
 18823  /* eslint-enable */
 18824  
 18825  var getInfo = function getInfo(ele) {
 18826    return ele.scratch('breadthfirst');
 18827  };
 18828  
 18829  var setInfo = function setInfo(ele, obj) {
 18830    return ele.scratch('breadthfirst', obj);
 18831  };
 18832  
 18833  function BreadthFirstLayout(options) {
 18834    this.options = extend({}, defaults$9, options);
 18835  }
 18836  
 18837  BreadthFirstLayout.prototype.run = function () {
 18838    var params = this.options;
 18839    var options = params;
 18840    var cy = params.cy;
 18841    var eles = options.eles;
 18842    var nodes = eles.nodes().filter(function (n) {
 18843      return !n.isParent();
 18844    });
 18845    var graph = eles;
 18846    var directed = options.directed;
 18847    var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
 18848  
 18849    var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
 18850      x1: 0,
 18851      y1: 0,
 18852      w: cy.width(),
 18853      h: cy.height()
 18854    });
 18855    var roots;
 18856  
 18857    if (elementOrCollection(options.roots)) {
 18858      roots = options.roots;
 18859    } else if (array(options.roots)) {
 18860      var rootsArray = [];
 18861  
 18862      for (var i = 0; i < options.roots.length; i++) {
 18863        var id = options.roots[i];
 18864        var ele = cy.getElementById(id);
 18865        rootsArray.push(ele);
 18866      }
 18867  
 18868      roots = cy.collection(rootsArray);
 18869    } else if (string(options.roots)) {
 18870      roots = cy.$(options.roots);
 18871    } else {
 18872      if (directed) {
 18873        roots = nodes.roots();
 18874      } else {
 18875        var components = eles.components();
 18876        roots = cy.collection();
 18877  
 18878        var _loop = function _loop(_i) {
 18879          var comp = components[_i];
 18880          var maxDegree = comp.maxDegree(false);
 18881          var compRoots = comp.filter(function (ele) {
 18882            return ele.degree(false) === maxDegree;
 18883          });
 18884          roots = roots.add(compRoots);
 18885        };
 18886  
 18887        for (var _i = 0; _i < components.length; _i++) {
 18888          _loop(_i);
 18889        }
 18890      }
 18891    }
 18892  
 18893    var depths = [];
 18894    var foundByBfs = {};
 18895  
 18896    var addToDepth = function addToDepth(ele, d) {
 18897      if (depths[d] == null) {
 18898        depths[d] = [];
 18899      }
 18900  
 18901      var i = depths[d].length;
 18902      depths[d].push(ele);
 18903      setInfo(ele, {
 18904        index: i,
 18905        depth: d
 18906      });
 18907    };
 18908  
 18909    var changeDepth = function changeDepth(ele, newDepth) {
 18910      var _getInfo = getInfo(ele),
 18911          depth = _getInfo.depth,
 18912          index = _getInfo.index;
 18913  
 18914      depths[depth][index] = null;
 18915      addToDepth(ele, newDepth);
 18916    }; // find the depths of the nodes
 18917  
 18918  
 18919    graph.bfs({
 18920      roots: roots,
 18921      directed: options.directed,
 18922      visit: function visit(node, edge, pNode, i, depth) {
 18923        var ele = node[0];
 18924        var id = ele.id();
 18925        addToDepth(ele, depth);
 18926        foundByBfs[id] = true;
 18927      }
 18928    }); // check for nodes not found by bfs
 18929  
 18930    var orphanNodes = [];
 18931  
 18932    for (var _i2 = 0; _i2 < nodes.length; _i2++) {
 18933      var _ele = nodes[_i2];
 18934  
 18935      if (foundByBfs[_ele.id()]) {
 18936        continue;
 18937      } else {
 18938        orphanNodes.push(_ele);
 18939      }
 18940    } // assign the nodes a depth and index
 18941  
 18942  
 18943    var assignDepthsAt = function assignDepthsAt(i) {
 18944      var eles = depths[i];
 18945  
 18946      for (var j = 0; j < eles.length; j++) {
 18947        var _ele2 = eles[j];
 18948  
 18949        if (_ele2 == null) {
 18950          eles.splice(j, 1);
 18951          j--;
 18952          continue;
 18953        }
 18954  
 18955        setInfo(_ele2, {
 18956          depth: i,
 18957          index: j
 18958        });
 18959      }
 18960    };
 18961  
 18962    var assignDepths = function assignDepths() {
 18963      for (var _i3 = 0; _i3 < depths.length; _i3++) {
 18964        assignDepthsAt(_i3);
 18965      }
 18966    };
 18967  
 18968    var adjustMaximally = function adjustMaximally(ele, shifted) {
 18969      var eInfo = getInfo(ele);
 18970      var incomers = ele.incomers().filter(function (el) {
 18971        return el.isNode() && eles.has(el);
 18972      });
 18973      var maxDepth = -1;
 18974      var id = ele.id();
 18975  
 18976      for (var k = 0; k < incomers.length; k++) {
 18977        var incmr = incomers[k];
 18978        var iInfo = getInfo(incmr);
 18979        maxDepth = Math.max(maxDepth, iInfo.depth);
 18980      }
 18981  
 18982      if (eInfo.depth <= maxDepth) {
 18983        if (shifted[id]) {
 18984          return null;
 18985        }
 18986  
 18987        changeDepth(ele, maxDepth + 1);
 18988        shifted[id] = true;
 18989        return true;
 18990      }
 18991  
 18992      return false;
 18993    }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
 18994  
 18995  
 18996    if (directed && maximal) {
 18997      var Q = [];
 18998      var shifted = {};
 18999  
 19000      var enqueue = function enqueue(n) {
 19001        return Q.push(n);
 19002      };
 19003  
 19004      var dequeue = function dequeue() {
 19005        return Q.shift();
 19006      };
 19007  
 19008      nodes.forEach(function (n) {
 19009        return Q.push(n);
 19010      });
 19011  
 19012      while (Q.length > 0) {
 19013        var _ele3 = dequeue();
 19014  
 19015        var didShift = adjustMaximally(_ele3, shifted);
 19016  
 19017        if (didShift) {
 19018          _ele3.outgoers().filter(function (el) {
 19019            return el.isNode() && eles.has(el);
 19020          }).forEach(enqueue);
 19021        } else if (didShift === null) {
 19022          warn('Detected double maximal shift for node `' + _ele3.id() + '`.  Bailing maximal adjustment due to cycle.  Use `options.maximal: true` only on DAGs.');
 19023          break; // exit on failure
 19024        }
 19025      }
 19026    }
 19027  
 19028    assignDepths(); // clear holes
 19029    // find min distance we need to leave between nodes
 19030  
 19031    var minDistance = 0;
 19032  
 19033    if (options.avoidOverlap) {
 19034      for (var _i4 = 0; _i4 < nodes.length; _i4++) {
 19035        var n = nodes[_i4];
 19036        var nbb = n.layoutDimensions(options);
 19037        var w = nbb.w;
 19038        var h = nbb.h;
 19039        minDistance = Math.max(minDistance, w, h);
 19040      }
 19041    } // get the weighted percent for an element based on its connectivity to other levels
 19042  
 19043  
 19044    var cachedWeightedPercent = {};
 19045  
 19046    var getWeightedPercent = function getWeightedPercent(ele) {
 19047      if (cachedWeightedPercent[ele.id()]) {
 19048        return cachedWeightedPercent[ele.id()];
 19049      }
 19050  
 19051      var eleDepth = getInfo(ele).depth;
 19052      var neighbors = ele.neighborhood();
 19053      var percent = 0;
 19054      var samples = 0;
 19055  
 19056      for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
 19057        var neighbor = neighbors[_i5];
 19058  
 19059        if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
 19060          continue;
 19061        }
 19062  
 19063        var bf = getInfo(neighbor);
 19064        var index = bf.index;
 19065        var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
 19066  
 19067        if (index == null || depth == null) {
 19068          continue;
 19069        }
 19070  
 19071        var nDepth = depths[depth].length;
 19072  
 19073        if (depth < eleDepth) {
 19074          // only get influenced by elements above
 19075          percent += index / nDepth;
 19076          samples++;
 19077        }
 19078      }
 19079  
 19080      samples = Math.max(1, samples);
 19081      percent = percent / samples;
 19082  
 19083      if (samples === 0) {
 19084        // put lone nodes at the start
 19085        percent = 0;
 19086      }
 19087  
 19088      cachedWeightedPercent[ele.id()] = percent;
 19089      return percent;
 19090    }; // rearrange the indices in each depth level based on connectivity
 19091  
 19092  
 19093    var sortFn = function sortFn(a, b) {
 19094      var apct = getWeightedPercent(a);
 19095      var bpct = getWeightedPercent(b);
 19096      var diff = apct - bpct;
 19097  
 19098      if (diff === 0) {
 19099        return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
 19100      } else {
 19101        return diff;
 19102      }
 19103    }; // sort each level to make connected nodes closer
 19104  
 19105  
 19106    for (var _i6 = 0; _i6 < depths.length; _i6++) {
 19107      depths[_i6].sort(sortFn);
 19108  
 19109      assignDepthsAt(_i6);
 19110    } // assign orphan nodes to a new top-level depth
 19111  
 19112  
 19113    var orphanDepth = [];
 19114  
 19115    for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
 19116      orphanDepth.push(orphanNodes[_i7]);
 19117    }
 19118  
 19119    depths.unshift(orphanDepth);
 19120    assignDepths();
 19121    var biggestDepthSize = 0;
 19122  
 19123    for (var _i8 = 0; _i8 < depths.length; _i8++) {
 19124      biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
 19125    }
 19126  
 19127    var center = {
 19128      x: bb.x1 + bb.w / 2,
 19129      y: bb.x1 + bb.h / 2
 19130    };
 19131    var maxDepthSize = depths.reduce(function (max, eles) {
 19132      return Math.max(max, eles.length);
 19133    }, 0);
 19134  
 19135    var getPosition = function getPosition(ele) {
 19136      var _getInfo2 = getInfo(ele),
 19137          depth = _getInfo2.depth,
 19138          index = _getInfo2.index;
 19139  
 19140      var depthSize = depths[depth].length;
 19141      var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
 19142      var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
 19143      var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
 19144      radiusStepSize = Math.max(radiusStepSize, minDistance);
 19145  
 19146      if (!options.circle) {
 19147        var epos = {
 19148          x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
 19149          y: (depth + 1) * distanceY
 19150        };
 19151        return epos;
 19152      } else {
 19153        var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
 19154        var theta = 2 * Math.PI / depths[depth].length * index;
 19155  
 19156        if (depth === 0 && depths[0].length === 1) {
 19157          radius = 1;
 19158        }
 19159  
 19160        return {
 19161          x: center.x + radius * Math.cos(theta),
 19162          y: center.y + radius * Math.sin(theta)
 19163        };
 19164      }
 19165    };
 19166  
 19167    nodes.layoutPositions(this, options, getPosition);
 19168    return this; // chaining
 19169  };
 19170  
 19171  var defaults$a = {
 19172    fit: true,
 19173    // whether to fit the viewport to the graph
 19174    padding: 30,
 19175    // the padding on fit
 19176    boundingBox: undefined,
 19177    // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 19178    avoidOverlap: true,
 19179    // prevents node overlap, may overflow boundingBox and radius if not enough space
 19180    nodeDimensionsIncludeLabels: false,
 19181    // Excludes the label when calculating node bounding boxes for the layout algorithm
 19182    spacingFactor: undefined,
 19183    // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
 19184    radius: undefined,
 19185    // the radius of the circle
 19186    startAngle: 3 / 2 * Math.PI,
 19187    // where nodes start in radians
 19188    sweep: undefined,
 19189    // how many radians should be between the first and last node (defaults to full circle)
 19190    clockwise: true,
 19191    // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
 19192    sort: undefined,
 19193    // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
 19194    animate: false,
 19195    // whether to transition the node positions
 19196    animationDuration: 500,
 19197    // duration of animation in ms if enabled
 19198    animationEasing: undefined,
 19199    // easing of animation if enabled
 19200    animateFilter: function animateFilter(node, i) {
 19201      return true;
 19202    },
 19203    // 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
 19204    ready: undefined,
 19205    // callback on layoutready
 19206    stop: undefined,
 19207    // callback on layoutstop
 19208    transform: function transform(node, position) {
 19209      return position;
 19210    } // transform a given node position. Useful for changing flow direction in discrete layouts 
 19211  
 19212  };
 19213  
 19214  function CircleLayout(options) {
 19215    this.options = extend({}, defaults$a, options);
 19216  }
 19217  
 19218  CircleLayout.prototype.run = function () {
 19219    var params = this.options;
 19220    var options = params;
 19221    var cy = params.cy;
 19222    var eles = options.eles;
 19223    var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
 19224    var nodes = eles.nodes().not(':parent');
 19225  
 19226    if (options.sort) {
 19227      nodes = nodes.sort(options.sort);
 19228    }
 19229  
 19230    var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
 19231      x1: 0,
 19232      y1: 0,
 19233      w: cy.width(),
 19234      h: cy.height()
 19235    });
 19236    var center = {
 19237      x: bb.x1 + bb.w / 2,
 19238      y: bb.y1 + bb.h / 2
 19239    };
 19240    var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
 19241    var dTheta = sweep / Math.max(1, nodes.length - 1);
 19242    var r;
 19243    var minDistance = 0;
 19244  
 19245    for (var i = 0; i < nodes.length; i++) {
 19246      var n = nodes[i];
 19247      var nbb = n.layoutDimensions(options);
 19248      var w = nbb.w;
 19249      var h = nbb.h;
 19250      minDistance = Math.max(minDistance, w, h);
 19251    }
 19252  
 19253    if (number(options.radius)) {
 19254      r = options.radius;
 19255    } else if (nodes.length <= 1) {
 19256      r = 0;
 19257    } else {
 19258      r = Math.min(bb.h, bb.w) / 2 - minDistance;
 19259    } // calculate the radius
 19260  
 19261  
 19262    if (nodes.length > 1 && options.avoidOverlap) {
 19263      // but only if more than one node (can't overlap)
 19264      minDistance *= 1.75; // just to have some nice spacing
 19265  
 19266      var dcos = Math.cos(dTheta) - Math.cos(0);
 19267      var dsin = Math.sin(dTheta) - Math.sin(0);
 19268      var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
 19269  
 19270      r = Math.max(rMin, r);
 19271    }
 19272  
 19273    var getPos = function getPos(ele, i) {
 19274      var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
 19275      var rx = r * Math.cos(theta);
 19276      var ry = r * Math.sin(theta);
 19277      var pos = {
 19278        x: center.x + rx,
 19279        y: center.y + ry
 19280      };
 19281      return pos;
 19282    };
 19283  
 19284    nodes.layoutPositions(this, options, getPos);
 19285    return this; // chaining
 19286  };
 19287  
 19288  var defaults$b = {
 19289    fit: true,
 19290    // whether to fit the viewport to the graph
 19291    padding: 30,
 19292    // the padding on fit
 19293    startAngle: 3 / 2 * Math.PI,
 19294    // where nodes start in radians
 19295    sweep: undefined,
 19296    // how many radians should be between the first and last node (defaults to full circle)
 19297    clockwise: true,
 19298    // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
 19299    equidistant: false,
 19300    // whether levels have an equal radial distance betwen them, may cause bounding box overflow
 19301    minNodeSpacing: 10,
 19302    // min spacing between outside of nodes (used for radius adjustment)
 19303    boundingBox: undefined,
 19304    // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 19305    avoidOverlap: true,
 19306    // prevents node overlap, may overflow boundingBox if not enough space
 19307    nodeDimensionsIncludeLabels: false,
 19308    // Excludes the label when calculating node bounding boxes for the layout algorithm
 19309    height: undefined,
 19310    // height of layout area (overrides container height)
 19311    width: undefined,
 19312    // width of layout area (overrides container width)
 19313    spacingFactor: undefined,
 19314    // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
 19315    concentric: function concentric(node) {
 19316      // returns numeric value for each node, placing higher nodes in levels towards the centre
 19317      return node.degree();
 19318    },
 19319    levelWidth: function levelWidth(nodes) {
 19320      // the letiation of concentric values in each level
 19321      return nodes.maxDegree() / 4;
 19322    },
 19323    animate: false,
 19324    // whether to transition the node positions
 19325    animationDuration: 500,
 19326    // duration of animation in ms if enabled
 19327    animationEasing: undefined,
 19328    // easing of animation if enabled
 19329    animateFilter: function animateFilter(node, i) {
 19330      return true;
 19331    },
 19332    // 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
 19333    ready: undefined,
 19334    // callback on layoutready
 19335    stop: undefined,
 19336    // callback on layoutstop
 19337    transform: function transform(node, position) {
 19338      return position;
 19339    } // transform a given node position. Useful for changing flow direction in discrete layouts
 19340  
 19341  };
 19342  
 19343  function ConcentricLayout(options) {
 19344    this.options = extend({}, defaults$b, options);
 19345  }
 19346  
 19347  ConcentricLayout.prototype.run = function () {
 19348    var params = this.options;
 19349    var options = params;
 19350    var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
 19351    var cy = params.cy;
 19352    var eles = options.eles;
 19353    var nodes = eles.nodes().not(':parent');
 19354    var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
 19355      x1: 0,
 19356      y1: 0,
 19357      w: cy.width(),
 19358      h: cy.height()
 19359    });
 19360    var center = {
 19361      x: bb.x1 + bb.w / 2,
 19362      y: bb.y1 + bb.h / 2
 19363    };
 19364    var nodeValues = []; // { node, value }
 19365  
 19366    var maxNodeSize = 0;
 19367  
 19368    for (var i = 0; i < nodes.length; i++) {
 19369      var node = nodes[i];
 19370      var value = void 0; // calculate the node value
 19371  
 19372      value = options.concentric(node);
 19373      nodeValues.push({
 19374        value: value,
 19375        node: node
 19376      }); // for style mapping
 19377  
 19378      node._private.scratch.concentric = value;
 19379    } // in case we used the `concentric` in style
 19380  
 19381  
 19382    nodes.updateStyle(); // calculate max size now based on potentially updated mappers
 19383  
 19384    for (var _i = 0; _i < nodes.length; _i++) {
 19385      var _node = nodes[_i];
 19386  
 19387      var nbb = _node.layoutDimensions(options);
 19388  
 19389      maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
 19390    } // sort node values in descreasing order
 19391  
 19392  
 19393    nodeValues.sort(function (a, b) {
 19394      return b.value - a.value;
 19395    });
 19396    var levelWidth = options.levelWidth(nodes); // put the values into levels
 19397  
 19398    var levels = [[]];
 19399    var currentLevel = levels[0];
 19400  
 19401    for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
 19402      var val = nodeValues[_i2];
 19403  
 19404      if (currentLevel.length > 0) {
 19405        var diff = Math.abs(currentLevel[0].value - val.value);
 19406  
 19407        if (diff >= levelWidth) {
 19408          currentLevel = [];
 19409          levels.push(currentLevel);
 19410        }
 19411      }
 19412  
 19413      currentLevel.push(val);
 19414    } // create positions from levels
 19415  
 19416  
 19417    var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
 19418  
 19419    if (!options.avoidOverlap) {
 19420      // then strictly constrain to bb
 19421      var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
 19422      var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
 19423      var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
 19424      minDist = Math.min(minDist, rStep);
 19425    } // find the metrics for each level
 19426  
 19427  
 19428    var r = 0;
 19429  
 19430    for (var _i3 = 0; _i3 < levels.length; _i3++) {
 19431      var level = levels[_i3];
 19432      var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
 19433      var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
 19434  
 19435      if (level.length > 1 && options.avoidOverlap) {
 19436        // but only if more than one node (can't overlap)
 19437        var dcos = Math.cos(dTheta) - Math.cos(0);
 19438        var dsin = Math.sin(dTheta) - Math.sin(0);
 19439        var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
 19440  
 19441        r = Math.max(rMin, r);
 19442      }
 19443  
 19444      level.r = r;
 19445      r += minDist;
 19446    }
 19447  
 19448    if (options.equidistant) {
 19449      var rDeltaMax = 0;
 19450      var _r = 0;
 19451  
 19452      for (var _i4 = 0; _i4 < levels.length; _i4++) {
 19453        var _level = levels[_i4];
 19454        var rDelta = _level.r - _r;
 19455        rDeltaMax = Math.max(rDeltaMax, rDelta);
 19456      }
 19457  
 19458      _r = 0;
 19459  
 19460      for (var _i5 = 0; _i5 < levels.length; _i5++) {
 19461        var _level2 = levels[_i5];
 19462  
 19463        if (_i5 === 0) {
 19464          _r = _level2.r;
 19465        }
 19466  
 19467        _level2.r = _r;
 19468        _r += rDeltaMax;
 19469      }
 19470    } // calculate the node positions
 19471  
 19472  
 19473    var pos = {}; // id => position
 19474  
 19475    for (var _i6 = 0; _i6 < levels.length; _i6++) {
 19476      var _level3 = levels[_i6];
 19477      var _dTheta = _level3.dTheta;
 19478      var _r2 = _level3.r;
 19479  
 19480      for (var j = 0; j < _level3.length; j++) {
 19481        var _val = _level3[j];
 19482        var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
 19483        var p = {
 19484          x: center.x + _r2 * Math.cos(theta),
 19485          y: center.y + _r2 * Math.sin(theta)
 19486        };
 19487        pos[_val.node.id()] = p;
 19488      }
 19489    } // position the nodes
 19490  
 19491  
 19492    nodes.layoutPositions(this, options, function (ele) {
 19493      var id = ele.id();
 19494      return pos[id];
 19495    });
 19496    return this; // chaining
 19497  };
 19498  
 19499  /*
 19500  The CoSE layout was written by Gerardo Huck.
 19501  https://www.linkedin.com/in/gerardohuck/
 19502  
 19503  Based on the following article:
 19504  http://dl.acm.org/citation.cfm?id=1498047
 19505  
 19506  Modifications tracked on Github.
 19507  */
 19508  var DEBUG;
 19509  /**
 19510   * @brief :  default layout options
 19511   */
 19512  
 19513  var defaults$c = {
 19514    // Called on `layoutready`
 19515    ready: function ready() {},
 19516    // Called on `layoutstop`
 19517    stop: function stop() {},
 19518    // Whether to animate while running the layout
 19519    // true : Animate continuously as the layout is running
 19520    // false : Just show the end result
 19521    // 'end' : Animate with the end result, from the initial positions to the end positions
 19522    animate: true,
 19523    // Easing of the animation for animate:'end'
 19524    animationEasing: undefined,
 19525    // The duration of the animation for animate:'end'
 19526    animationDuration: undefined,
 19527    // A function that determines whether the node should be animated
 19528    // All nodes animated by default on animate enabled
 19529    // Non-animated nodes are positioned immediately when the layout starts
 19530    animateFilter: function animateFilter(node, i) {
 19531      return true;
 19532    },
 19533    // The layout animates only after this many milliseconds for animate:true
 19534    // (prevents flashing on fast runs)
 19535    animationThreshold: 250,
 19536    // Number of iterations between consecutive screen positions update
 19537    refresh: 20,
 19538    // Whether to fit the network view after when done
 19539    fit: true,
 19540    // Padding on fit
 19541    padding: 30,
 19542    // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 19543    boundingBox: undefined,
 19544    // Excludes the label when calculating node bounding boxes for the layout algorithm
 19545    nodeDimensionsIncludeLabels: false,
 19546    // Randomize the initial positions of the nodes (true) or use existing positions (false)
 19547    randomize: false,
 19548    // Extra spacing between components in non-compound graphs
 19549    componentSpacing: 40,
 19550    // Node repulsion (non overlapping) multiplier
 19551    nodeRepulsion: function nodeRepulsion(node) {
 19552      return 2048;
 19553    },
 19554    // Node repulsion (overlapping) multiplier
 19555    nodeOverlap: 4,
 19556    // Ideal edge (non nested) length
 19557    idealEdgeLength: function idealEdgeLength(edge) {
 19558      return 32;
 19559    },
 19560    // Divisor to compute edge forces
 19561    edgeElasticity: function edgeElasticity(edge) {
 19562      return 32;
 19563    },
 19564    // Nesting factor (multiplier) to compute ideal edge length for nested edges
 19565    nestingFactor: 1.2,
 19566    // Gravity force (constant)
 19567    gravity: 1,
 19568    // Maximum number of iterations to perform
 19569    numIter: 1000,
 19570    // Initial temperature (maximum node displacement)
 19571    initialTemp: 1000,
 19572    // Cooling factor (how the temperature is reduced between consecutive iterations
 19573    coolingFactor: 0.99,
 19574    // Lower temperature threshold (below this point the layout will end)
 19575    minTemp: 1.0
 19576  };
 19577  /**
 19578   * @brief       : constructor
 19579   * @arg options : object containing layout options
 19580   */
 19581  
 19582  function CoseLayout(options) {
 19583    this.options = extend({}, defaults$c, options);
 19584    this.options.layout = this;
 19585  }
 19586  /**
 19587   * @brief : runs the layout
 19588   */
 19589  
 19590  
 19591  CoseLayout.prototype.run = function () {
 19592    var options = this.options;
 19593    var cy = options.cy;
 19594    var layout = this;
 19595    layout.stopped = false;
 19596  
 19597    if (options.animate === true || options.animate === false) {
 19598      layout.emit({
 19599        type: 'layoutstart',
 19600        layout: layout
 19601      });
 19602    } // Set DEBUG - Global variable
 19603  
 19604  
 19605    if (true === options.debug) {
 19606      DEBUG = true;
 19607    } else {
 19608      DEBUG = false;
 19609    } // Initialize layout info
 19610  
 19611  
 19612    var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
 19613  
 19614    if (DEBUG) {
 19615      printLayoutInfo(layoutInfo);
 19616    } // If required, randomize node positions
 19617  
 19618  
 19619    if (options.randomize) {
 19620      randomizePositions(layoutInfo);
 19621    }
 19622  
 19623    var startTime = performanceNow();
 19624  
 19625    var refresh = function refresh() {
 19626      refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
 19627  
 19628      if (true === options.fit) {
 19629        cy.fit(options.padding);
 19630      }
 19631    };
 19632  
 19633    var mainLoop = function mainLoop(i) {
 19634      if (layout.stopped || i >= options.numIter) {
 19635        // logDebug("Layout manually stopped. Stopping computation in step " + i);
 19636        return false;
 19637      } // Do one step in the phisical simulation
 19638  
 19639  
 19640      step$1(layoutInfo, options); // Update temperature
 19641  
 19642      layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
 19643  
 19644      if (layoutInfo.temperature < options.minTemp) {
 19645        // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
 19646        return false;
 19647      }
 19648  
 19649      return true;
 19650    };
 19651  
 19652    var done = function done() {
 19653      if (options.animate === true || options.animate === false) {
 19654        refresh(); // Layout has finished
 19655  
 19656        layout.one('layoutstop', options.stop);
 19657        layout.emit({
 19658          type: 'layoutstop',
 19659          layout: layout
 19660        });
 19661      } else {
 19662        var nodes = options.eles.nodes();
 19663        var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
 19664        nodes.layoutPositions(layout, options, getScaledPos);
 19665      }
 19666    };
 19667  
 19668    var i = 0;
 19669    var loopRet = true;
 19670  
 19671    if (options.animate === true) {
 19672      var frame = function frame() {
 19673        var f = 0;
 19674  
 19675        while (loopRet && f < options.refresh) {
 19676          loopRet = mainLoop(i);
 19677          i++;
 19678          f++;
 19679        }
 19680  
 19681        if (!loopRet) {
 19682          // it's done
 19683          separateComponents(layoutInfo, options);
 19684          done();
 19685        } else {
 19686          var now = performanceNow();
 19687  
 19688          if (now - startTime >= options.animationThreshold) {
 19689            refresh();
 19690          }
 19691  
 19692          requestAnimationFrame(frame);
 19693        }
 19694      };
 19695  
 19696      frame();
 19697    } else {
 19698      while (loopRet) {
 19699        loopRet = mainLoop(i);
 19700        i++;
 19701      }
 19702  
 19703      separateComponents(layoutInfo, options);
 19704      done();
 19705    }
 19706  
 19707    return this; // chaining
 19708  };
 19709  /**
 19710   * @brief : called on continuous layouts to stop them before they finish
 19711   */
 19712  
 19713  
 19714  CoseLayout.prototype.stop = function () {
 19715    this.stopped = true;
 19716  
 19717    if (this.thread) {
 19718      this.thread.stop();
 19719    }
 19720  
 19721    this.emit('layoutstop');
 19722    return this; // chaining
 19723  };
 19724  
 19725  CoseLayout.prototype.destroy = function () {
 19726    if (this.thread) {
 19727      this.thread.stop();
 19728    }
 19729  
 19730    return this; // chaining
 19731  };
 19732  /**
 19733   * @brief     : Creates an object which is contains all the data
 19734   *              used in the layout process
 19735   * @arg cy    : cytoscape.js object
 19736   * @return    : layoutInfo object initialized
 19737   */
 19738  
 19739  
 19740  var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
 19741    // Shortcut
 19742    var edges = options.eles.edges();
 19743    var nodes = options.eles.nodes();
 19744    var layoutInfo = {
 19745      isCompound: cy.hasCompoundNodes(),
 19746      layoutNodes: [],
 19747      idToIndex: {},
 19748      nodeSize: nodes.size(),
 19749      graphSet: [],
 19750      indexToGraph: [],
 19751      layoutEdges: [],
 19752      edgeSize: edges.size(),
 19753      temperature: options.initialTemp,
 19754      clientWidth: cy.width(),
 19755      clientHeight: cy.width(),
 19756      boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
 19757        x1: 0,
 19758        y1: 0,
 19759        w: cy.width(),
 19760        h: cy.height()
 19761      })
 19762    };
 19763    var components = options.eles.components();
 19764    var id2cmptId = {};
 19765  
 19766    for (var i = 0; i < components.length; i++) {
 19767      var component = components[i];
 19768  
 19769      for (var j = 0; j < component.length; j++) {
 19770        var node = component[j];
 19771        id2cmptId[node.id()] = i;
 19772      }
 19773    } // Iterate over all nodes, creating layout nodes
 19774  
 19775  
 19776    for (var i = 0; i < layoutInfo.nodeSize; i++) {
 19777      var n = nodes[i];
 19778      var nbb = n.layoutDimensions(options);
 19779      var tempNode = {};
 19780      tempNode.isLocked = n.locked();
 19781      tempNode.id = n.data('id');
 19782      tempNode.parentId = n.data('parent');
 19783      tempNode.cmptId = id2cmptId[n.id()];
 19784      tempNode.children = [];
 19785      tempNode.positionX = n.position('x');
 19786      tempNode.positionY = n.position('y');
 19787      tempNode.offsetX = 0;
 19788      tempNode.offsetY = 0;
 19789      tempNode.height = nbb.w;
 19790      tempNode.width = nbb.h;
 19791      tempNode.maxX = tempNode.positionX + tempNode.width / 2;
 19792      tempNode.minX = tempNode.positionX - tempNode.width / 2;
 19793      tempNode.maxY = tempNode.positionY + tempNode.height / 2;
 19794      tempNode.minY = tempNode.positionY - tempNode.height / 2;
 19795      tempNode.padLeft = parseFloat(n.style('padding'));
 19796      tempNode.padRight = parseFloat(n.style('padding'));
 19797      tempNode.padTop = parseFloat(n.style('padding'));
 19798      tempNode.padBottom = parseFloat(n.style('padding')); // forces
 19799  
 19800      tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
 19801  
 19802      layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
 19803  
 19804      layoutInfo.idToIndex[tempNode.id] = i;
 19805    } // Inline implementation of a queue, used for traversing the graph in BFS order
 19806  
 19807  
 19808    var queue = [];
 19809    var start = 0; // Points to the start the queue
 19810  
 19811    var end = -1; // Points to the end of the queue
 19812  
 19813    var tempGraph = []; // Second pass to add child information and
 19814    // initialize queue for hierarchical traversal
 19815  
 19816    for (var i = 0; i < layoutInfo.nodeSize; i++) {
 19817      var n = layoutInfo.layoutNodes[i];
 19818      var p_id = n.parentId; // Check if node n has a parent node
 19819  
 19820      if (null != p_id) {
 19821        // Add node Id to parent's list of children
 19822        layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
 19823      } else {
 19824        // If a node doesn't have a parent, then it's in the root graph
 19825        queue[++end] = n.id;
 19826        tempGraph.push(n.id);
 19827      }
 19828    } // Add root graph to graphSet
 19829  
 19830  
 19831    layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
 19832  
 19833    while (start <= end) {
 19834      // Get the node to visit and remove it from queue
 19835      var node_id = queue[start++];
 19836      var node_ix = layoutInfo.idToIndex[node_id];
 19837      var node = layoutInfo.layoutNodes[node_ix];
 19838      var children = node.children;
 19839  
 19840      if (children.length > 0) {
 19841        // Add children nodes as a new graph to graph set
 19842        layoutInfo.graphSet.push(children); // Add children to que queue to be visited
 19843  
 19844        for (var i = 0; i < children.length; i++) {
 19845          queue[++end] = children[i];
 19846        }
 19847      }
 19848    } // Create indexToGraph map
 19849  
 19850  
 19851    for (var i = 0; i < layoutInfo.graphSet.length; i++) {
 19852      var graph = layoutInfo.graphSet[i];
 19853  
 19854      for (var j = 0; j < graph.length; j++) {
 19855        var index = layoutInfo.idToIndex[graph[j]];
 19856        layoutInfo.indexToGraph[index] = i;
 19857      }
 19858    } // Iterate over all edges, creating Layout Edges
 19859  
 19860  
 19861    for (var i = 0; i < layoutInfo.edgeSize; i++) {
 19862      var e = edges[i];
 19863      var tempEdge = {};
 19864      tempEdge.id = e.data('id');
 19865      tempEdge.sourceId = e.data('source');
 19866      tempEdge.targetId = e.data('target'); // Compute ideal length
 19867  
 19868      var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
 19869      var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
 19870  
 19871      var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
 19872      var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
 19873      var sourceGraph = layoutInfo.indexToGraph[sourceIx];
 19874      var targetGraph = layoutInfo.indexToGraph[targetIx];
 19875  
 19876      if (sourceGraph != targetGraph) {
 19877        // Find lowest common graph ancestor
 19878        var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
 19879  
 19880        var lcaGraph = layoutInfo.graphSet[lca];
 19881        var depth = 0; // Source depth
 19882  
 19883        var tempNode = layoutInfo.layoutNodes[sourceIx];
 19884  
 19885        while (-1 === lcaGraph.indexOf(tempNode.id)) {
 19886          tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
 19887          depth++;
 19888        } // Target depth
 19889  
 19890  
 19891        tempNode = layoutInfo.layoutNodes[targetIx];
 19892  
 19893        while (-1 === lcaGraph.indexOf(tempNode.id)) {
 19894          tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
 19895          depth++;
 19896        } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
 19897        //  ". Index: " + lca + " Contents: " + lcaGraph.toString() +
 19898        //  ". Depth: " + depth);
 19899        // Update idealLength
 19900  
 19901  
 19902        idealLength *= depth * options.nestingFactor;
 19903      }
 19904  
 19905      tempEdge.idealLength = idealLength;
 19906      tempEdge.elasticity = elasticity;
 19907      layoutInfo.layoutEdges.push(tempEdge);
 19908    } // Finally, return layoutInfo object
 19909  
 19910  
 19911    return layoutInfo;
 19912  };
 19913  /**
 19914   * @brief : This function finds the index of the lowest common
 19915   *          graph ancestor between 2 nodes in the subtree
 19916   *          (from the graph hierarchy induced tree) whose
 19917   *          root is graphIx
 19918   *
 19919   * @arg node1: node1's ID
 19920   * @arg node2: node2's ID
 19921   * @arg layoutInfo: layoutInfo object
 19922   *
 19923   */
 19924  
 19925  
 19926  var findLCA = function findLCA(node1, node2, layoutInfo) {
 19927    // Find their common ancester, starting from the root graph
 19928    var res = findLCA_aux(node1, node2, 0, layoutInfo);
 19929  
 19930    if (2 > res.count) {
 19931      // If aux function couldn't find the common ancester,
 19932      // then it is the root graph
 19933      return 0;
 19934    } else {
 19935      return res.graph;
 19936    }
 19937  };
 19938  /**
 19939   * @brief          : Auxiliary function used for LCA computation
 19940   *
 19941   * @arg node1      : node1's ID
 19942   * @arg node2      : node2's ID
 19943   * @arg graphIx    : subgraph index
 19944   * @arg layoutInfo : layoutInfo object
 19945   *
 19946   * @return         : object of the form {count: X, graph: Y}, where:
 19947   *                   X is the number of ancesters (max: 2) found in
 19948   *                   graphIx (and it's subgraphs),
 19949   *                   Y is the graph index of the lowest graph containing
 19950   *                   all X nodes
 19951   */
 19952  
 19953  
 19954  var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
 19955    var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
 19956  
 19957    if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
 19958      return {
 19959        count: 2,
 19960        graph: graphIx
 19961      };
 19962    } // Make recursive calls for all subgraphs
 19963  
 19964  
 19965    var c = 0;
 19966  
 19967    for (var i = 0; i < graph.length; i++) {
 19968      var nodeId = graph[i];
 19969      var nodeIx = layoutInfo.idToIndex[nodeId];
 19970      var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
 19971  
 19972      if (0 === children.length) {
 19973        continue;
 19974      }
 19975  
 19976      var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
 19977      var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
 19978  
 19979      if (0 === result.count) {
 19980        // Neither node1 nor node2 are present in this subgraph
 19981        continue;
 19982      } else if (1 === result.count) {
 19983        // One of (node1, node2) is present in this subgraph
 19984        c++;
 19985  
 19986        if (2 === c) {
 19987          // We've already found both nodes, no need to keep searching
 19988          break;
 19989        }
 19990      } else {
 19991        // Both nodes are present in this subgraph
 19992        return result;
 19993      }
 19994    }
 19995  
 19996    return {
 19997      count: c,
 19998      graph: graphIx
 19999    };
 20000  };
 20001  /**
 20002   * @brief: printsLayoutInfo into js console
 20003   *         Only used for debbuging
 20004   */
 20005  
 20006  
 20007  if (false) {
 20008    var printLayoutInfo;
 20009  }
 20010  /**
 20011   * @brief : Randomizes the position of all nodes
 20012   */
 20013  
 20014  
 20015  var randomizePositions = function randomizePositions(layoutInfo, cy) {
 20016    var width = layoutInfo.clientWidth;
 20017    var height = layoutInfo.clientHeight;
 20018  
 20019    for (var i = 0; i < layoutInfo.nodeSize; i++) {
 20020      var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
 20021  
 20022      if (0 === n.children.length && !n.isLocked) {
 20023        n.positionX = Math.random() * width;
 20024        n.positionY = Math.random() * height;
 20025      }
 20026    }
 20027  };
 20028  
 20029  var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
 20030    var bb = layoutInfo.boundingBox;
 20031    var coseBB = {
 20032      x1: Infinity,
 20033      x2: -Infinity,
 20034      y1: Infinity,
 20035      y2: -Infinity
 20036    };
 20037  
 20038    if (options.boundingBox) {
 20039      nodes.forEach(function (node) {
 20040        var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
 20041        coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
 20042        coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
 20043        coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
 20044        coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
 20045      });
 20046      coseBB.w = coseBB.x2 - coseBB.x1;
 20047      coseBB.h = coseBB.y2 - coseBB.y1;
 20048    }
 20049  
 20050    return function (ele, i) {
 20051      var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
 20052  
 20053      if (options.boundingBox) {
 20054        // then add extra bounding box constraint
 20055        var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
 20056        var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
 20057        return {
 20058          x: bb.x1 + pctX * bb.w,
 20059          y: bb.y1 + pctY * bb.h
 20060        };
 20061      } else {
 20062        return {
 20063          x: lnode.positionX,
 20064          y: lnode.positionY
 20065        };
 20066      }
 20067    };
 20068  };
 20069  /**
 20070   * @brief          : Updates the positions of nodes in the network
 20071   * @arg layoutInfo : LayoutInfo object
 20072   * @arg cy         : Cytoscape object
 20073   * @arg options    : Layout options
 20074   */
 20075  
 20076  
 20077  var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
 20078    // var s = 'Refreshing positions';
 20079    // logDebug(s);
 20080    var layout = options.layout;
 20081    var nodes = options.eles.nodes();
 20082    var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
 20083    nodes.positions(getScaledPos); // Trigger layoutReady only on first call
 20084  
 20085    if (true !== layoutInfo.ready) {
 20086      // s = 'Triggering layoutready';
 20087      // logDebug(s);
 20088      layoutInfo.ready = true;
 20089      layout.one('layoutready', options.ready);
 20090      layout.emit({
 20091        type: 'layoutready',
 20092        layout: this
 20093      });
 20094    }
 20095  };
 20096  /**
 20097   * @brief : Logs a debug message in JS console, if DEBUG is ON
 20098   */
 20099  // var logDebug = function(text) {
 20100  //   if (DEBUG) {
 20101  //     console.debug(text);
 20102  //   }
 20103  // };
 20104  
 20105  /**
 20106   * @brief          : Performs one iteration of the physical simulation
 20107   * @arg layoutInfo : LayoutInfo object already initialized
 20108   * @arg cy         : Cytoscape object
 20109   * @arg options    : Layout options
 20110   */
 20111  
 20112  
 20113  var step$1 = function step(layoutInfo, options, _step) {
 20114    // var s = "\n\n###############################";
 20115    // s += "\nSTEP: " + step;
 20116    // s += "\n###############################\n";
 20117    // logDebug(s);
 20118    // Calculate node repulsions
 20119    calculateNodeForces(layoutInfo, options); // Calculate edge forces
 20120  
 20121    calculateEdgeForces(layoutInfo); // Calculate gravity forces
 20122  
 20123    calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
 20124  
 20125    propagateForces(layoutInfo); // Update positions based on calculated forces
 20126  
 20127    updatePositions(layoutInfo);
 20128  };
 20129  /**
 20130   * @brief : Computes the node repulsion forces
 20131   */
 20132  
 20133  
 20134  var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
 20135    // Go through each of the graphs in graphSet
 20136    // Nodes only repel each other if they belong to the same graph
 20137    // var s = 'calculateNodeForces';
 20138    // logDebug(s);
 20139    for (var i = 0; i < layoutInfo.graphSet.length; i++) {
 20140      var graph = layoutInfo.graphSet[i];
 20141      var numNodes = graph.length; // s = "Set: " + graph.toString();
 20142      // logDebug(s);
 20143      // Now get all the pairs of nodes
 20144      // Only get each pair once, (A, B) = (B, A)
 20145  
 20146      for (var j = 0; j < numNodes; j++) {
 20147        var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
 20148  
 20149        for (var k = j + 1; k < numNodes; k++) {
 20150          var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
 20151          nodeRepulsion(node1, node2, layoutInfo, options);
 20152        }
 20153      }
 20154    }
 20155  };
 20156  
 20157  var randomDistance = function randomDistance(max) {
 20158    return -max + 2 * max * Math.random();
 20159  };
 20160  /**
 20161   * @brief : Compute the node repulsion forces between a pair of nodes
 20162   */
 20163  
 20164  
 20165  var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
 20166    // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
 20167    var cmptId1 = node1.cmptId;
 20168    var cmptId2 = node2.cmptId;
 20169  
 20170    if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
 20171      return;
 20172    } // Get direction of line connecting both node centers
 20173  
 20174  
 20175    var directionX = node2.positionX - node1.positionX;
 20176    var directionY = node2.positionY - node1.positionY;
 20177    var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
 20178    // If both centers are the same, apply a random force
 20179  
 20180    if (0 === directionX && 0 === directionY) {
 20181      directionX = randomDistance(maxRandDist);
 20182      directionY = randomDistance(maxRandDist);
 20183    }
 20184  
 20185    var overlap = nodesOverlap(node1, node2, directionX, directionY);
 20186  
 20187    if (overlap > 0) {
 20188      // s += "\nNodes DO overlap.";
 20189      // s += "\nOverlap: " + overlap;
 20190      // If nodes overlap, repulsion force is proportional
 20191      // to the overlap
 20192      var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
 20193  
 20194      var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
 20195  
 20196      var forceX = force * directionX / distance;
 20197      var forceY = force * directionY / distance;
 20198    } else {
 20199      // s += "\nNodes do NOT overlap.";
 20200      // If there's no overlap, force is inversely proportional
 20201      // to squared distance
 20202      // Get clipping points for both nodes
 20203      var point1 = findClippingPoint(node1, directionX, directionY);
 20204      var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
 20205  
 20206      var distanceX = point2.x - point1.x;
 20207      var distanceY = point2.y - point1.y;
 20208      var distanceSqr = distanceX * distanceX + distanceY * distanceY;
 20209      var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
 20210      // Compute the module and components of the force vector
 20211  
 20212      var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
 20213      var forceX = force * distanceX / distance;
 20214      var forceY = force * distanceY / distance;
 20215    } // Apply force
 20216  
 20217  
 20218    if (!node1.isLocked) {
 20219      node1.offsetX -= forceX;
 20220      node1.offsetY -= forceY;
 20221    }
 20222  
 20223    if (!node2.isLocked) {
 20224      node2.offsetX += forceX;
 20225      node2.offsetY += forceY;
 20226    } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
 20227    // logDebug(s);
 20228  
 20229  
 20230    return;
 20231  };
 20232  /**
 20233   * @brief  : Determines whether two nodes overlap or not
 20234   * @return : Amount of overlapping (0 => no overlap)
 20235   */
 20236  
 20237  
 20238  var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
 20239    if (dX > 0) {
 20240      var overlapX = node1.maxX - node2.minX;
 20241    } else {
 20242      var overlapX = node2.maxX - node1.minX;
 20243    }
 20244  
 20245    if (dY > 0) {
 20246      var overlapY = node1.maxY - node2.minY;
 20247    } else {
 20248      var overlapY = node2.maxY - node1.minY;
 20249    }
 20250  
 20251    if (overlapX >= 0 && overlapY >= 0) {
 20252      return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
 20253    } else {
 20254      return 0;
 20255    }
 20256  };
 20257  /**
 20258   * @brief : Finds the point in which an edge (direction dX, dY) intersects
 20259   *          the rectangular bounding box of it's source/target node
 20260   */
 20261  
 20262  
 20263  var findClippingPoint = function findClippingPoint(node, dX, dY) {
 20264    // Shorcuts
 20265    var X = node.positionX;
 20266    var Y = node.positionY;
 20267    var H = node.height || 1;
 20268    var W = node.width || 1;
 20269    var dirSlope = dY / dX;
 20270    var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
 20271    //   " . Height:  " + H + ", Width: " + W +
 20272    //   "\nDirection " + dX + ", " + dY;
 20273    //
 20274    // Compute intersection
 20275  
 20276    var res = {}; // Case: Vertical direction (up)
 20277  
 20278    if (0 === dX && 0 < dY) {
 20279      res.x = X; // s += "\nUp direction";
 20280  
 20281      res.y = Y + H / 2;
 20282      return res;
 20283    } // Case: Vertical direction (down)
 20284  
 20285  
 20286    if (0 === dX && 0 > dY) {
 20287      res.x = X;
 20288      res.y = Y + H / 2; // s += "\nDown direction";
 20289  
 20290      return res;
 20291    } // Case: Intersects the right border
 20292  
 20293  
 20294    if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
 20295      res.x = X + W / 2;
 20296      res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
 20297  
 20298      return res;
 20299    } // Case: Intersects the left border
 20300  
 20301  
 20302    if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
 20303      res.x = X - W / 2;
 20304      res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
 20305  
 20306      return res;
 20307    } // Case: Intersects the top border
 20308  
 20309  
 20310    if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
 20311      res.x = X + H * dX / 2 / dY;
 20312      res.y = Y + H / 2; // s += "\nTop border";
 20313  
 20314      return res;
 20315    } // Case: Intersects the bottom border
 20316  
 20317  
 20318    if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
 20319      res.x = X - H * dX / 2 / dY;
 20320      res.y = Y - H / 2; // s += "\nBottom border";
 20321  
 20322      return res;
 20323    } // s += "\nClipping point found at " + res.x + ", " + res.y;
 20324    // logDebug(s);
 20325  
 20326  
 20327    return res;
 20328  };
 20329  /**
 20330   * @brief : Calculates all edge forces
 20331   */
 20332  
 20333  
 20334  var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
 20335    // Iterate over all edges
 20336    for (var i = 0; i < layoutInfo.edgeSize; i++) {
 20337      // Get edge, source & target nodes
 20338      var edge = layoutInfo.layoutEdges[i];
 20339      var sourceIx = layoutInfo.idToIndex[edge.sourceId];
 20340      var source = layoutInfo.layoutNodes[sourceIx];
 20341      var targetIx = layoutInfo.idToIndex[edge.targetId];
 20342      var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
 20343  
 20344      var directionX = target.positionX - source.positionX;
 20345      var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
 20346      // A random force has already been applied as node repulsion
 20347  
 20348      if (0 === directionX && 0 === directionY) {
 20349        continue;
 20350      } // Get clipping points for both nodes
 20351  
 20352  
 20353      var point1 = findClippingPoint(source, directionX, directionY);
 20354      var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
 20355      var lx = point2.x - point1.x;
 20356      var ly = point2.y - point1.y;
 20357      var l = Math.sqrt(lx * lx + ly * ly);
 20358      var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
 20359  
 20360      if (0 !== l) {
 20361        var forceX = force * lx / l;
 20362        var forceY = force * ly / l;
 20363      } else {
 20364        var forceX = 0;
 20365        var forceY = 0;
 20366      } // Add this force to target and source nodes
 20367  
 20368  
 20369      if (!source.isLocked) {
 20370        source.offsetX += forceX;
 20371        source.offsetY += forceY;
 20372      }
 20373  
 20374      if (!target.isLocked) {
 20375        target.offsetX -= forceX;
 20376        target.offsetY -= forceY;
 20377      } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
 20378      // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
 20379      // logDebug(s);
 20380  
 20381    }
 20382  };
 20383  /**
 20384   * @brief : Computes gravity forces for all nodes
 20385   */
 20386  
 20387  
 20388  var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
 20389    var distThreshold = 1; // var s = 'calculateGravityForces';
 20390    // logDebug(s);
 20391  
 20392    for (var i = 0; i < layoutInfo.graphSet.length; i++) {
 20393      var graph = layoutInfo.graphSet[i];
 20394      var numNodes = graph.length; // s = "Set: " + graph.toString();
 20395      // logDebug(s);
 20396      // Compute graph center
 20397  
 20398      if (0 === i) {
 20399        var centerX = layoutInfo.clientHeight / 2;
 20400        var centerY = layoutInfo.clientWidth / 2;
 20401      } else {
 20402        // Get Parent node for this graph, and use its position as center
 20403        var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
 20404        var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
 20405        var centerX = parent.positionX;
 20406        var centerY = parent.positionY;
 20407      } // s = "Center found at: " + centerX + ", " + centerY;
 20408      // logDebug(s);
 20409      // Apply force to all nodes in graph
 20410  
 20411  
 20412      for (var j = 0; j < numNodes; j++) {
 20413        var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
 20414  
 20415        if (node.isLocked) {
 20416          continue;
 20417        }
 20418  
 20419        var dx = centerX - node.positionX;
 20420        var dy = centerY - node.positionY;
 20421        var d = Math.sqrt(dx * dx + dy * dy);
 20422  
 20423        if (d > distThreshold) {
 20424          var fx = options.gravity * dx / d;
 20425          var fy = options.gravity * dy / d;
 20426          node.offsetX += fx;
 20427          node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
 20428        } // s += ": skypped since it's too close to center";
 20429          // logDebug(s);
 20430  
 20431      }
 20432    }
 20433  };
 20434  /**
 20435   * @brief          : This function propagates the existing offsets from
 20436   *                   parent nodes to its descendents.
 20437   * @arg layoutInfo : layoutInfo Object
 20438   * @arg cy         : cytoscape Object
 20439   * @arg options    : Layout options
 20440   */
 20441  
 20442  
 20443  var propagateForces = function propagateForces(layoutInfo, options) {
 20444    // Inline implementation of a queue, used for traversing the graph in BFS order
 20445    var queue = [];
 20446    var start = 0; // Points to the start the queue
 20447  
 20448    var end = -1; // Points to the end of the queue
 20449    // logDebug('propagateForces');
 20450    // Start by visiting the nodes in the root graph
 20451  
 20452    queue.push.apply(queue, layoutInfo.graphSet[0]);
 20453    end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
 20454  
 20455    while (start <= end) {
 20456      // Get the node to visit and remove it from queue
 20457      var nodeId = queue[start++];
 20458      var nodeIndex = layoutInfo.idToIndex[nodeId];
 20459      var node = layoutInfo.layoutNodes[nodeIndex];
 20460      var children = node.children; // We only need to process the node if it's compound
 20461  
 20462      if (0 < children.length && !node.isLocked) {
 20463        var offX = node.offsetX;
 20464        var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
 20465        //   ". OffsetX: " + offX + ". OffsetY: " + offY;
 20466        // s += "\n Children: " + children.toString();
 20467        // logDebug(s);
 20468  
 20469        for (var i = 0; i < children.length; i++) {
 20470          var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
 20471  
 20472          childNode.offsetX += offX;
 20473          childNode.offsetY += offY; // Add children to queue to be visited
 20474  
 20475          queue[++end] = children[i];
 20476        } // Reset parent offsets
 20477  
 20478  
 20479        node.offsetX = 0;
 20480        node.offsetY = 0;
 20481      }
 20482    }
 20483  };
 20484  /**
 20485   * @brief : Updates the layout model positions, based on
 20486   *          the accumulated forces
 20487   */
 20488  
 20489  
 20490  var updatePositions = function updatePositions(layoutInfo, options) {
 20491    // var s = 'Updating positions';
 20492    // logDebug(s);
 20493    // Reset boundaries for compound nodes
 20494    for (var i = 0; i < layoutInfo.nodeSize; i++) {
 20495      var n = layoutInfo.layoutNodes[i];
 20496  
 20497      if (0 < n.children.length) {
 20498        // logDebug("Resetting boundaries of compound node: " + n.id);
 20499        n.maxX = undefined;
 20500        n.minX = undefined;
 20501        n.maxY = undefined;
 20502        n.minY = undefined;
 20503      }
 20504    }
 20505  
 20506    for (var i = 0; i < layoutInfo.nodeSize; i++) {
 20507      var n = layoutInfo.layoutNodes[i];
 20508  
 20509      if (0 < n.children.length || n.isLocked) {
 20510        // No need to set compound or locked node position
 20511        // logDebug("Skipping position update of node: " + n.id);
 20512        continue;
 20513      } // s = "Node: " + n.id + " Previous position: (" +
 20514      // n.positionX + ", " + n.positionY + ").";
 20515      // Limit displacement in order to improve stability
 20516  
 20517  
 20518      var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
 20519      n.positionX += tempForce.x;
 20520      n.positionY += tempForce.y;
 20521      n.offsetX = 0;
 20522      n.offsetY = 0;
 20523      n.minX = n.positionX - n.width;
 20524      n.maxX = n.positionX + n.width;
 20525      n.minY = n.positionY - n.height;
 20526      n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
 20527      // logDebug(s);
 20528      // Update ancestry boudaries
 20529  
 20530      updateAncestryBoundaries(n, layoutInfo);
 20531    } // Update size, position of compund nodes
 20532  
 20533  
 20534    for (var i = 0; i < layoutInfo.nodeSize; i++) {
 20535      var n = layoutInfo.layoutNodes[i];
 20536  
 20537      if (0 < n.children.length && !n.isLocked) {
 20538        n.positionX = (n.maxX + n.minX) / 2;
 20539        n.positionY = (n.maxY + n.minY) / 2;
 20540        n.width = n.maxX - n.minX;
 20541        n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
 20542        // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
 20543        // s += "\nWidth: " + n.width + ", Height: " + n.height;
 20544        // logDebug(s);
 20545      }
 20546    }
 20547  };
 20548  /**
 20549   * @brief : Limits a force (forceX, forceY) to be not
 20550   *          greater (in modulo) than max.
 20551   8          Preserves force direction.
 20552    */
 20553  
 20554  
 20555  var limitForce = function limitForce(forceX, forceY, max) {
 20556    // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
 20557    var force = Math.sqrt(forceX * forceX + forceY * forceY);
 20558  
 20559    if (force > max) {
 20560      var res = {
 20561        x: max * forceX / force,
 20562        y: max * forceY / force
 20563      };
 20564    } else {
 20565      var res = {
 20566        x: forceX,
 20567        y: forceY
 20568      };
 20569    } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
 20570    // logDebug(s);
 20571  
 20572  
 20573    return res;
 20574  };
 20575  /**
 20576   * @brief : Function used for keeping track of compound node
 20577   *          sizes, since they should bound all their subnodes.
 20578   */
 20579  
 20580  
 20581  var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
 20582    // var s = "Propagating new position/size of node " + node.id;
 20583    var parentId = node.parentId;
 20584  
 20585    if (null == parentId) {
 20586      // If there's no parent, we are done
 20587      // s += ". No parent node.";
 20588      // logDebug(s);
 20589      return;
 20590    } // Get Parent Node
 20591  
 20592  
 20593    var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
 20594    var flag = false; // MaxX
 20595  
 20596    if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
 20597      p.maxX = node.maxX + p.padRight;
 20598      flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
 20599    } // MinX
 20600  
 20601  
 20602    if (null == p.minX || node.minX - p.padLeft < p.minX) {
 20603      p.minX = node.minX - p.padLeft;
 20604      flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
 20605    } // MaxY
 20606  
 20607  
 20608    if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
 20609      p.maxY = node.maxY + p.padBottom;
 20610      flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
 20611    } // MinY
 20612  
 20613  
 20614    if (null == p.minY || node.minY - p.padTop < p.minY) {
 20615      p.minY = node.minY - p.padTop;
 20616      flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
 20617    } // If updated boundaries, propagate changes upward
 20618  
 20619  
 20620    if (flag) {
 20621      // logDebug(s);
 20622      return updateAncestryBoundaries(p, layoutInfo);
 20623    } // s += ". No changes in boundaries/position of parent node " + p.id;
 20624    // logDebug(s);
 20625  
 20626  
 20627    return;
 20628  };
 20629  
 20630  var separateComponents = function separateComponents(layoutInfo, options) {
 20631    var nodes = layoutInfo.layoutNodes;
 20632    var components = [];
 20633  
 20634    for (var i = 0; i < nodes.length; i++) {
 20635      var node = nodes[i];
 20636      var cid = node.cmptId;
 20637      var component = components[cid] = components[cid] || [];
 20638      component.push(node);
 20639    }
 20640  
 20641    var totalA = 0;
 20642  
 20643    for (var i = 0; i < components.length; i++) {
 20644      var c = components[i];
 20645  
 20646      if (!c) {
 20647        continue;
 20648      }
 20649  
 20650      c.x1 = Infinity;
 20651      c.x2 = -Infinity;
 20652      c.y1 = Infinity;
 20653      c.y2 = -Infinity;
 20654  
 20655      for (var j = 0; j < c.length; j++) {
 20656        var n = c[j];
 20657        c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
 20658        c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
 20659        c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
 20660        c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
 20661      }
 20662  
 20663      c.w = c.x2 - c.x1;
 20664      c.h = c.y2 - c.y1;
 20665      totalA += c.w * c.h;
 20666    }
 20667  
 20668    components.sort(function (c1, c2) {
 20669      return c2.w * c2.h - c1.w * c1.h;
 20670    });
 20671    var x = 0;
 20672    var y = 0;
 20673    var usedW = 0;
 20674    var rowH = 0;
 20675    var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
 20676  
 20677    for (var i = 0; i < components.length; i++) {
 20678      var c = components[i];
 20679  
 20680      if (!c) {
 20681        continue;
 20682      }
 20683  
 20684      for (var j = 0; j < c.length; j++) {
 20685        var n = c[j];
 20686  
 20687        if (!n.isLocked) {
 20688          n.positionX += x - c.x1;
 20689          n.positionY += y - c.y1;
 20690        }
 20691      }
 20692  
 20693      x += c.w + options.componentSpacing;
 20694      usedW += c.w + options.componentSpacing;
 20695      rowH = Math.max(rowH, c.h);
 20696  
 20697      if (usedW > maxRowW) {
 20698        y += rowH + options.componentSpacing;
 20699        x = 0;
 20700        usedW = 0;
 20701        rowH = 0;
 20702      }
 20703    }
 20704  };
 20705  
 20706  var defaults$d = {
 20707    fit: true,
 20708    // whether to fit the viewport to the graph
 20709    padding: 30,
 20710    // padding used on fit
 20711    boundingBox: undefined,
 20712    // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 20713    avoidOverlap: true,
 20714    // prevents node overlap, may overflow boundingBox if not enough space
 20715    avoidOverlapPadding: 10,
 20716    // extra spacing around nodes when avoidOverlap: true
 20717    nodeDimensionsIncludeLabels: false,
 20718    // Excludes the label when calculating node bounding boxes for the layout algorithm
 20719    spacingFactor: undefined,
 20720    // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
 20721    condense: false,
 20722    // uses all available space on false, uses minimal space on true
 20723    rows: undefined,
 20724    // force num of rows in the grid
 20725    cols: undefined,
 20726    // force num of columns in the grid
 20727    position: function position(node) {},
 20728    // returns { row, col } for element
 20729    sort: undefined,
 20730    // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
 20731    animate: false,
 20732    // whether to transition the node positions
 20733    animationDuration: 500,
 20734    // duration of animation in ms if enabled
 20735    animationEasing: undefined,
 20736    // easing of animation if enabled
 20737    animateFilter: function animateFilter(node, i) {
 20738      return true;
 20739    },
 20740    // 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
 20741    ready: undefined,
 20742    // callback on layoutready
 20743    stop: undefined,
 20744    // callback on layoutstop
 20745    transform: function transform(node, position) {
 20746      return position;
 20747    } // transform a given node position. Useful for changing flow direction in discrete layouts 
 20748  
 20749  };
 20750  
 20751  function GridLayout(options) {
 20752    this.options = extend({}, defaults$d, options);
 20753  }
 20754  
 20755  GridLayout.prototype.run = function () {
 20756    var params = this.options;
 20757    var options = params;
 20758    var cy = params.cy;
 20759    var eles = options.eles;
 20760    var nodes = eles.nodes().not(':parent');
 20761  
 20762    if (options.sort) {
 20763      nodes = nodes.sort(options.sort);
 20764    }
 20765  
 20766    var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
 20767      x1: 0,
 20768      y1: 0,
 20769      w: cy.width(),
 20770      h: cy.height()
 20771    });
 20772  
 20773    if (bb.h === 0 || bb.w === 0) {
 20774      nodes.layoutPositions(this, options, function (ele) {
 20775        return {
 20776          x: bb.x1,
 20777          y: bb.y1
 20778        };
 20779      });
 20780    } else {
 20781      // width/height * splits^2 = cells where splits is number of times to split width
 20782      var cells = nodes.size();
 20783      var splits = Math.sqrt(cells * bb.h / bb.w);
 20784      var rows = Math.round(splits);
 20785      var cols = Math.round(bb.w / bb.h * splits);
 20786  
 20787      var small = function small(val) {
 20788        if (val == null) {
 20789          return Math.min(rows, cols);
 20790        } else {
 20791          var min = Math.min(rows, cols);
 20792  
 20793          if (min == rows) {
 20794            rows = val;
 20795          } else {
 20796            cols = val;
 20797          }
 20798        }
 20799      };
 20800  
 20801      var large = function large(val) {
 20802        if (val == null) {
 20803          return Math.max(rows, cols);
 20804        } else {
 20805          var max = Math.max(rows, cols);
 20806  
 20807          if (max == rows) {
 20808            rows = val;
 20809          } else {
 20810            cols = val;
 20811          }
 20812        }
 20813      };
 20814  
 20815      var oRows = options.rows;
 20816      var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
 20817  
 20818      if (oRows != null && oCols != null) {
 20819        rows = oRows;
 20820        cols = oCols;
 20821      } else if (oRows != null && oCols == null) {
 20822        rows = oRows;
 20823        cols = Math.ceil(cells / rows);
 20824      } else if (oRows == null && oCols != null) {
 20825        cols = oCols;
 20826        rows = Math.ceil(cells / cols);
 20827      } // otherwise use the automatic values and adjust accordingly
 20828      // if rounding was up, see if we can reduce rows or columns
 20829      else if (cols * rows > cells) {
 20830          var sm = small();
 20831          var lg = large(); // reducing the small side takes away the most cells, so try it first
 20832  
 20833          if ((sm - 1) * lg >= cells) {
 20834            small(sm - 1);
 20835          } else if ((lg - 1) * sm >= cells) {
 20836            large(lg - 1);
 20837          }
 20838        } else {
 20839          // if rounding was too low, add rows or columns
 20840          while (cols * rows < cells) {
 20841            var _sm = small();
 20842  
 20843            var _lg = large(); // try to add to larger side first (adds less in multiplication)
 20844  
 20845  
 20846            if ((_lg + 1) * _sm >= cells) {
 20847              large(_lg + 1);
 20848            } else {
 20849              small(_sm + 1);
 20850            }
 20851          }
 20852        }
 20853  
 20854      var cellWidth = bb.w / cols;
 20855      var cellHeight = bb.h / rows;
 20856  
 20857      if (options.condense) {
 20858        cellWidth = 0;
 20859        cellHeight = 0;
 20860      }
 20861  
 20862      if (options.avoidOverlap) {
 20863        for (var i = 0; i < nodes.length; i++) {
 20864          var node = nodes[i];
 20865          var pos = node._private.position;
 20866  
 20867          if (pos.x == null || pos.y == null) {
 20868            // for bb
 20869            pos.x = 0;
 20870            pos.y = 0;
 20871          }
 20872  
 20873          var nbb = node.layoutDimensions(options);
 20874          var p = options.avoidOverlapPadding;
 20875          var w = nbb.w + p;
 20876          var h = nbb.h + p;
 20877          cellWidth = Math.max(cellWidth, w);
 20878          cellHeight = Math.max(cellHeight, h);
 20879        }
 20880      }
 20881  
 20882      var cellUsed = {}; // e.g. 'c-0-2' => true
 20883  
 20884      var used = function used(row, col) {
 20885        return cellUsed['c-' + row + '-' + col] ? true : false;
 20886      };
 20887  
 20888      var use = function use(row, col) {
 20889        cellUsed['c-' + row + '-' + col] = true;
 20890      }; // to keep track of current cell position
 20891  
 20892  
 20893      var row = 0;
 20894      var col = 0;
 20895  
 20896      var moveToNextCell = function moveToNextCell() {
 20897        col++;
 20898  
 20899        if (col >= cols) {
 20900          col = 0;
 20901          row++;
 20902        }
 20903      }; // get a cache of all the manual positions
 20904  
 20905  
 20906      var id2manPos = {};
 20907  
 20908      for (var _i = 0; _i < nodes.length; _i++) {
 20909        var _node = nodes[_i];
 20910        var rcPos = options.position(_node);
 20911  
 20912        if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
 20913          // must have at least row or col def'd
 20914          var _pos = {
 20915            row: rcPos.row,
 20916            col: rcPos.col
 20917          };
 20918  
 20919          if (_pos.col === undefined) {
 20920            // find unused col
 20921            _pos.col = 0;
 20922  
 20923            while (used(_pos.row, _pos.col)) {
 20924              _pos.col++;
 20925            }
 20926          } else if (_pos.row === undefined) {
 20927            // find unused row
 20928            _pos.row = 0;
 20929  
 20930            while (used(_pos.row, _pos.col)) {
 20931              _pos.row++;
 20932            }
 20933          }
 20934  
 20935          id2manPos[_node.id()] = _pos;
 20936          use(_pos.row, _pos.col);
 20937        }
 20938      }
 20939  
 20940      var getPos = function getPos(element, i) {
 20941        var x, y;
 20942  
 20943        if (element.locked() || element.isParent()) {
 20944          return false;
 20945        } // see if we have a manual position set
 20946  
 20947  
 20948        var rcPos = id2manPos[element.id()];
 20949  
 20950        if (rcPos) {
 20951          x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
 20952          y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
 20953        } else {
 20954          // otherwise set automatically
 20955          while (used(row, col)) {
 20956            moveToNextCell();
 20957          }
 20958  
 20959          x = col * cellWidth + cellWidth / 2 + bb.x1;
 20960          y = row * cellHeight + cellHeight / 2 + bb.y1;
 20961          use(row, col);
 20962          moveToNextCell();
 20963        }
 20964  
 20965        return {
 20966          x: x,
 20967          y: y
 20968        };
 20969      };
 20970  
 20971      nodes.layoutPositions(this, options, getPos);
 20972    }
 20973  
 20974    return this; // chaining
 20975  };
 20976  
 20977  var defaults$e = {
 20978    ready: function ready() {},
 20979    // on layoutready
 20980    stop: function stop() {} // on layoutstop
 20981  
 20982  }; // constructor
 20983  // options : object containing layout options
 20984  
 20985  function NullLayout(options) {
 20986    this.options = extend({}, defaults$e, options);
 20987  } // runs the layout
 20988  
 20989  
 20990  NullLayout.prototype.run = function () {
 20991    var options = this.options;
 20992    var eles = options.eles; // elements to consider in the layout
 20993  
 20994    var layout = this; // cy is automatically populated for us in the constructor
 20995    // (disable eslint for next line as this serves as example layout code to external developers)
 20996    // eslint-disable-next-line no-unused-vars
 20997  
 20998    var cy = options.cy;
 20999    layout.emit('layoutstart'); // puts all nodes at (0, 0)
 21000    // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
 21001  
 21002    eles.nodes().positions(function () {
 21003      return {
 21004        x: 0,
 21005        y: 0
 21006      };
 21007    }); // trigger layoutready when each node has had its position set at least once
 21008  
 21009    layout.one('layoutready', options.ready);
 21010    layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
 21011  
 21012    layout.one('layoutstop', options.stop);
 21013    layout.emit('layoutstop');
 21014    return this; // chaining
 21015  }; // called on continuous layouts to stop them before they finish
 21016  
 21017  
 21018  NullLayout.prototype.stop = function () {
 21019    return this; // chaining
 21020  };
 21021  
 21022  var defaults$f = {
 21023    positions: undefined,
 21024    // map of (node id) => (position obj); or function(node){ return somPos; }
 21025    zoom: undefined,
 21026    // the zoom level to set (prob want fit = false if set)
 21027    pan: undefined,
 21028    // the pan level to set (prob want fit = false if set)
 21029    fit: true,
 21030    // whether to fit to viewport
 21031    padding: 30,
 21032    // padding on fit
 21033    animate: false,
 21034    // whether to transition the node positions
 21035    animationDuration: 500,
 21036    // duration of animation in ms if enabled
 21037    animationEasing: undefined,
 21038    // easing of animation if enabled
 21039    animateFilter: function animateFilter(node, i) {
 21040      return true;
 21041    },
 21042    // 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
 21043    ready: undefined,
 21044    // callback on layoutready
 21045    stop: undefined,
 21046    // callback on layoutstop
 21047    transform: function transform(node, position) {
 21048      return position;
 21049    } // transform a given node position. Useful for changing flow direction in discrete layouts
 21050  
 21051  };
 21052  
 21053  function PresetLayout(options) {
 21054    this.options = extend({}, defaults$f, options);
 21055  }
 21056  
 21057  PresetLayout.prototype.run = function () {
 21058    var options = this.options;
 21059    var eles = options.eles;
 21060    var nodes = eles.nodes();
 21061    var posIsFn = fn(options.positions);
 21062  
 21063    function getPosition(node) {
 21064      if (options.positions == null) {
 21065        return copyPosition(node.position());
 21066      }
 21067  
 21068      if (posIsFn) {
 21069        return options.positions(node);
 21070      }
 21071  
 21072      var pos = options.positions[node._private.data.id];
 21073  
 21074      if (pos == null) {
 21075        return null;
 21076      }
 21077  
 21078      return pos;
 21079    }
 21080  
 21081    nodes.layoutPositions(this, options, function (node, i) {
 21082      var position = getPosition(node);
 21083  
 21084      if (node.locked() || position == null) {
 21085        return false;
 21086      }
 21087  
 21088      return position;
 21089    });
 21090    return this; // chaining
 21091  };
 21092  
 21093  var defaults$g = {
 21094    fit: true,
 21095    // whether to fit to viewport
 21096    padding: 30,
 21097    // fit padding
 21098    boundingBox: undefined,
 21099    // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 21100    animate: false,
 21101    // whether to transition the node positions
 21102    animationDuration: 500,
 21103    // duration of animation in ms if enabled
 21104    animationEasing: undefined,
 21105    // easing of animation if enabled
 21106    animateFilter: function animateFilter(node, i) {
 21107      return true;
 21108    },
 21109    // 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
 21110    ready: undefined,
 21111    // callback on layoutready
 21112    stop: undefined,
 21113    // callback on layoutstop
 21114    transform: function transform(node, position) {
 21115      return position;
 21116    } // transform a given node position. Useful for changing flow direction in discrete layouts 
 21117  
 21118  };
 21119  
 21120  function RandomLayout(options) {
 21121    this.options = extend({}, defaults$g, options);
 21122  }
 21123  
 21124  RandomLayout.prototype.run = function () {
 21125    var options = this.options;
 21126    var cy = options.cy;
 21127    var eles = options.eles;
 21128    var nodes = eles.nodes().not(':parent');
 21129    var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
 21130      x1: 0,
 21131      y1: 0,
 21132      w: cy.width(),
 21133      h: cy.height()
 21134    });
 21135  
 21136    var getPos = function getPos(node, i) {
 21137      return {
 21138        x: bb.x1 + Math.round(Math.random() * bb.w),
 21139        y: bb.y1 + Math.round(Math.random() * bb.h)
 21140      };
 21141    };
 21142  
 21143    nodes.layoutPositions(this, options, getPos);
 21144    return this; // chaining
 21145  };
 21146  
 21147  var layout = [{
 21148    name: 'breadthfirst',
 21149    impl: BreadthFirstLayout
 21150  }, {
 21151    name: 'circle',
 21152    impl: CircleLayout
 21153  }, {
 21154    name: 'concentric',
 21155    impl: ConcentricLayout
 21156  }, {
 21157    name: 'cose',
 21158    impl: CoseLayout
 21159  }, {
 21160    name: 'grid',
 21161    impl: GridLayout
 21162  }, {
 21163    name: 'null',
 21164    impl: NullLayout
 21165  }, {
 21166    name: 'preset',
 21167    impl: PresetLayout
 21168  }, {
 21169    name: 'random',
 21170    impl: RandomLayout
 21171  }];
 21172  
 21173  function NullRenderer(options) {
 21174    this.options = options;
 21175    this.notifications = 0; // for testing
 21176  }
 21177  
 21178  var noop$1 = function noop() {};
 21179  
 21180  var throwImgErr = function throwImgErr() {
 21181    throw new Error('A headless instance can not render images');
 21182  };
 21183  
 21184  NullRenderer.prototype = {
 21185    recalculateRenderedStyle: noop$1,
 21186    notify: function notify() {
 21187      this.notifications++;
 21188    },
 21189    init: noop$1,
 21190    isHeadless: function isHeadless() {
 21191      return true;
 21192    },
 21193    png: throwImgErr,
 21194    jpg: throwImgErr
 21195  };
 21196  
 21197  var BRp = {};
 21198  BRp.arrowShapeWidth = 0.3;
 21199  
 21200  BRp.registerArrowShapes = function () {
 21201    var arrowShapes = this.arrowShapes = {};
 21202    var renderer = this; // Contract for arrow shapes:
 21203    // 0, 0 is arrow tip
 21204    // (0, 1) is direction towards node
 21205    // (1, 0) is right
 21206    //
 21207    // functional api:
 21208    // collide: check x, y in shape
 21209    // roughCollide: called before collide, no false negatives
 21210    // draw: draw
 21211    // spacing: dist(arrowTip, nodeBoundary)
 21212    // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
 21213  
 21214    var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
 21215      var x1 = translation.x - size / 2 - padding;
 21216      var x2 = translation.x + size / 2 + padding;
 21217      var y1 = translation.y - size / 2 - padding;
 21218      var y2 = translation.y + size / 2 + padding;
 21219      var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
 21220      return inside;
 21221    };
 21222  
 21223    var transform = function transform(x, y, size, angle, translation) {
 21224      var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
 21225      var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
 21226      var xScaled = xRotated * size;
 21227      var yScaled = yRotated * size;
 21228      var xTranslated = xScaled + translation.x;
 21229      var yTranslated = yScaled + translation.y;
 21230      return {
 21231        x: xTranslated,
 21232        y: yTranslated
 21233      };
 21234    };
 21235  
 21236    var transformPoints = function transformPoints(pts, size, angle, translation) {
 21237      var retPts = [];
 21238  
 21239      for (var i = 0; i < pts.length; i += 2) {
 21240        var x = pts[i];
 21241        var y = pts[i + 1];
 21242        retPts.push(transform(x, y, size, angle, translation));
 21243      }
 21244  
 21245      return retPts;
 21246    };
 21247  
 21248    var pointsToArr = function pointsToArr(pts) {
 21249      var ret = [];
 21250  
 21251      for (var i = 0; i < pts.length; i++) {
 21252        var p = pts[i];
 21253        ret.push(p.x, p.y);
 21254      }
 21255  
 21256      return ret;
 21257    };
 21258  
 21259    var standardGap = function standardGap(edge) {
 21260      return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
 21261    };
 21262  
 21263    var defineArrowShape = function defineArrowShape(name, defn) {
 21264      if (string(defn)) {
 21265        defn = arrowShapes[defn];
 21266      }
 21267  
 21268      arrowShapes[name] = extend({
 21269        name: name,
 21270        points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
 21271        collide: function collide(x, y, size, angle, translation, padding) {
 21272          var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
 21273          var inside = pointInsidePolygonPoints(x, y, points);
 21274          return inside;
 21275        },
 21276        roughCollide: bbCollide,
 21277        draw: function draw(context, size, angle, translation) {
 21278          var points = transformPoints(this.points, size, angle, translation);
 21279          renderer.arrowShapeImpl('polygon')(context, points);
 21280        },
 21281        spacing: function spacing(edge) {
 21282          return 0;
 21283        },
 21284        gap: standardGap
 21285      }, defn);
 21286    };
 21287  
 21288    defineArrowShape('none', {
 21289      collide: falsify,
 21290      roughCollide: falsify,
 21291      draw: noop,
 21292      spacing: zeroify,
 21293      gap: zeroify
 21294    });
 21295    defineArrowShape('triangle', {
 21296      points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
 21297    });
 21298    defineArrowShape('arrow', 'triangle');
 21299    defineArrowShape('triangle-backcurve', {
 21300      points: arrowShapes['triangle'].points,
 21301      controlPoint: [0, -0.15],
 21302      roughCollide: bbCollide,
 21303      draw: function draw(context, size, angle, translation, edgeWidth) {
 21304        var ptsTrans = transformPoints(this.points, size, angle, translation);
 21305        var ctrlPt = this.controlPoint;
 21306        var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
 21307        renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
 21308      },
 21309      gap: function gap(edge) {
 21310        return standardGap(edge) * 0.8;
 21311      }
 21312    });
 21313    defineArrowShape('triangle-tee', {
 21314      points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
 21315      pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
 21316      collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
 21317        var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
 21318        var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
 21319        var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
 21320        return inside;
 21321      },
 21322      draw: function draw(context, size, angle, translation, edgeWidth) {
 21323        var triPts = transformPoints(this.points, size, angle, translation);
 21324        var teePts = transformPoints(this.pointsTee, size, angle, translation);
 21325        renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
 21326      }
 21327    });
 21328    defineArrowShape('triangle-cross', {
 21329      points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
 21330      baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
 21331      -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
 21332      0.15, -0.4],
 21333      crossLinePts: function crossLinePts(size, edgeWidth) {
 21334        // shift points so that the distance between the cross points matches edge width
 21335        var p = this.baseCrossLinePts.slice();
 21336        var shiftFactor = edgeWidth / size;
 21337        var y0 = 3;
 21338        var y1 = 5;
 21339        p[y0] = p[y0] - shiftFactor;
 21340        p[y1] = p[y1] - shiftFactor;
 21341        return p;
 21342      },
 21343      collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
 21344        var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
 21345        var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
 21346        var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
 21347        return inside;
 21348      },
 21349      draw: function draw(context, size, angle, translation, edgeWidth) {
 21350        var triPts = transformPoints(this.points, size, angle, translation);
 21351        var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
 21352        renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
 21353      }
 21354    });
 21355    defineArrowShape('vee', {
 21356      points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
 21357      gap: function gap(edge) {
 21358        return standardGap(edge) * 0.525;
 21359      }
 21360    });
 21361    defineArrowShape('circle', {
 21362      radius: 0.15,
 21363      collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
 21364        var t = translation;
 21365        var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
 21366        return inside;
 21367      },
 21368      draw: function draw(context, size, angle, translation, edgeWidth) {
 21369        renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
 21370      },
 21371      spacing: function spacing(edge) {
 21372        return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
 21373      }
 21374    });
 21375    defineArrowShape('tee', {
 21376      points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
 21377      spacing: function spacing(edge) {
 21378        return 1;
 21379      },
 21380      gap: function gap(edge) {
 21381        return 1;
 21382      }
 21383    });
 21384    defineArrowShape('square', {
 21385      points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
 21386    });
 21387    defineArrowShape('diamond', {
 21388      points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
 21389      gap: function gap(edge) {
 21390        return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
 21391      }
 21392    });
 21393    defineArrowShape('chevron', {
 21394      points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
 21395      gap: function gap(edge) {
 21396        return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
 21397      }
 21398    });
 21399  };
 21400  
 21401  var BRp$1 = {}; // Project mouse
 21402  
 21403  BRp$1.projectIntoViewport = function (clientX, clientY) {
 21404    var cy = this.cy;
 21405    var offsets = this.findContainerClientCoords();
 21406    var offsetLeft = offsets[0];
 21407    var offsetTop = offsets[1];
 21408    var scale = offsets[4];
 21409    var pan = cy.pan();
 21410    var zoom = cy.zoom();
 21411    var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
 21412    var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
 21413    return [x, y];
 21414  };
 21415  
 21416  BRp$1.findContainerClientCoords = function () {
 21417    if (this.containerBB) {
 21418      return this.containerBB;
 21419    }
 21420  
 21421    var container = this.container;
 21422    var rect = container.getBoundingClientRect();
 21423    var style = window$1.getComputedStyle(container);
 21424  
 21425    var styleValue = function styleValue(name) {
 21426      return parseFloat(style.getPropertyValue(name));
 21427    };
 21428  
 21429    var padding = {
 21430      left: styleValue('padding-left'),
 21431      right: styleValue('padding-right'),
 21432      top: styleValue('padding-top'),
 21433      bottom: styleValue('padding-bottom')
 21434    };
 21435    var border = {
 21436      left: styleValue('border-left-width'),
 21437      right: styleValue('border-right-width'),
 21438      top: styleValue('border-top-width'),
 21439      bottom: styleValue('border-bottom-width')
 21440    };
 21441    var clientWidth = container.clientWidth;
 21442    var clientHeight = container.clientHeight;
 21443    var paddingHor = padding.left + padding.right;
 21444    var paddingVer = padding.top + padding.bottom;
 21445    var borderHor = border.left + border.right;
 21446    var scale = rect.width / (clientWidth + borderHor);
 21447    var unscaledW = clientWidth - paddingHor;
 21448    var unscaledH = clientHeight - paddingVer;
 21449    var left = rect.left + padding.left + border.left;
 21450    var top = rect.top + padding.top + border.top;
 21451    return this.containerBB = [left, top, unscaledW, unscaledH, scale];
 21452  };
 21453  
 21454  BRp$1.invalidateContainerClientCoordsCache = function () {
 21455    this.containerBB = null;
 21456  };
 21457  
 21458  BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
 21459    return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
 21460  };
 21461  
 21462  BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
 21463    var self = this;
 21464    var r = this;
 21465    var eles = r.getCachedZSortedEles();
 21466    var near = []; // 1 node max, 1 edge max
 21467  
 21468    var zoom = r.cy.zoom();
 21469    var hasCompounds = r.cy.hasCompoundNodes();
 21470    var edgeThreshold = (isTouch ? 24 : 8) / zoom;
 21471    var nodeThreshold = (isTouch ? 8 : 2) / zoom;
 21472    var labelThreshold = (isTouch ? 8 : 2) / zoom;
 21473    var minSqDist = Infinity;
 21474    var nearEdge;
 21475    var nearNode;
 21476  
 21477    if (interactiveElementsOnly) {
 21478      eles = eles.interactive;
 21479    }
 21480  
 21481    function addEle(ele, sqDist) {
 21482      if (ele.isNode()) {
 21483        if (nearNode) {
 21484          return; // can't replace node
 21485        } else {
 21486          nearNode = ele;
 21487          near.push(ele);
 21488        }
 21489      }
 21490  
 21491      if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
 21492        if (nearEdge) {
 21493          // then replace existing edge
 21494          // can replace only if same z-index
 21495          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) {
 21496            for (var i = 0; i < near.length; i++) {
 21497              if (near[i].isEdge()) {
 21498                near[i] = ele;
 21499                nearEdge = ele;
 21500                minSqDist = sqDist != null ? sqDist : minSqDist;
 21501                break;
 21502              }
 21503            }
 21504          }
 21505        } else {
 21506          near.push(ele);
 21507          nearEdge = ele;
 21508          minSqDist = sqDist != null ? sqDist : minSqDist;
 21509        }
 21510      }
 21511    }
 21512  
 21513    function checkNode(node) {
 21514      var width = node.outerWidth() + 2 * nodeThreshold;
 21515      var height = node.outerHeight() + 2 * nodeThreshold;
 21516      var hw = width / 2;
 21517      var hh = height / 2;
 21518      var pos = node.position();
 21519  
 21520      if (pos.x - hw <= x && x <= pos.x + hw // bb check x
 21521      && pos.y - hh <= y && y <= pos.y + hh // bb check y
 21522      ) {
 21523          var shape = r.nodeShapes[self.getNodeShape(node)];
 21524  
 21525          if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
 21526            addEle(node, 0);
 21527            return true;
 21528          }
 21529        }
 21530    }
 21531  
 21532    function checkEdge(edge) {
 21533      var _p = edge._private;
 21534      var rs = _p.rscratch;
 21535      var styleWidth = edge.pstyle('width').pfValue;
 21536      var scale = edge.pstyle('arrow-scale').value;
 21537      var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
 21538  
 21539      var widthSq = width * width;
 21540      var width2 = width * 2;
 21541      var src = _p.source;
 21542      var tgt = _p.target;
 21543      var sqDist;
 21544  
 21545      if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
 21546        var pts = rs.allpts;
 21547  
 21548        for (var i = 0; i + 3 < pts.length; i += 2) {
 21549          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]))) {
 21550            addEle(edge, sqDist);
 21551            return true;
 21552          }
 21553        }
 21554      } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
 21555        var pts = rs.allpts;
 21556  
 21557        for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
 21558          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]))) {
 21559            addEle(edge, sqDist);
 21560            return true;
 21561          }
 21562        }
 21563      } // if we're close to the edge but didn't hit it, maybe we hit its arrows
 21564  
 21565  
 21566      var src = src || _p.source;
 21567      var tgt = tgt || _p.target;
 21568      var arSize = self.getArrowWidth(styleWidth, scale);
 21569      var arrows = [{
 21570        name: 'source',
 21571        x: rs.arrowStartX,
 21572        y: rs.arrowStartY,
 21573        angle: rs.srcArrowAngle
 21574      }, {
 21575        name: 'target',
 21576        x: rs.arrowEndX,
 21577        y: rs.arrowEndY,
 21578        angle: rs.tgtArrowAngle
 21579      }, {
 21580        name: 'mid-source',
 21581        x: rs.midX,
 21582        y: rs.midY,
 21583        angle: rs.midsrcArrowAngle
 21584      }, {
 21585        name: 'mid-target',
 21586        x: rs.midX,
 21587        y: rs.midY,
 21588        angle: rs.midtgtArrowAngle
 21589      }];
 21590  
 21591      for (var i = 0; i < arrows.length; i++) {
 21592        var ar = arrows[i];
 21593        var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
 21594        var edgeWidth = edge.pstyle('width').pfValue;
 21595  
 21596        if (shape.roughCollide(x, y, arSize, ar.angle, {
 21597          x: ar.x,
 21598          y: ar.y
 21599        }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
 21600          x: ar.x,
 21601          y: ar.y
 21602        }, edgeWidth, edgeThreshold)) {
 21603          addEle(edge);
 21604          return true;
 21605        }
 21606      } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
 21607  
 21608  
 21609      if (hasCompounds && near.length > 0) {
 21610        checkNode(src);
 21611        checkNode(tgt);
 21612      }
 21613    }
 21614  
 21615    function preprop(obj, name, pre) {
 21616      return getPrefixedProperty(obj, name, pre);
 21617    }
 21618  
 21619    function checkLabel(ele, prefix) {
 21620      var _p = ele._private;
 21621      var th = labelThreshold;
 21622      var prefixDash;
 21623  
 21624      if (prefix) {
 21625        prefixDash = prefix + '-';
 21626      } else {
 21627        prefixDash = '';
 21628      }
 21629  
 21630      ele.boundingBox();
 21631      var bb = _p.labelBounds[prefix || 'main'];
 21632      var text = ele.pstyle(prefixDash + 'label').value;
 21633      var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
 21634  
 21635      if (!eventsEnabled || !text) {
 21636        return;
 21637      }
 21638  
 21639      var rstyle = _p.rstyle;
 21640      var lx = preprop(rstyle, 'labelX', prefix);
 21641      var ly = preprop(rstyle, 'labelY', prefix);
 21642      var theta = preprop(_p.rscratch, 'labelAngle', prefix);
 21643      var lx1 = bb.x1 - th;
 21644      var lx2 = bb.x2 + th;
 21645      var ly1 = bb.y1 - th;
 21646      var ly2 = bb.y2 + th;
 21647  
 21648      if (theta) {
 21649        var cos = Math.cos(theta);
 21650        var sin = Math.sin(theta);
 21651  
 21652        var rotate = function rotate(x, y) {
 21653          x = x - lx;
 21654          y = y - ly;
 21655          return {
 21656            x: x * cos - y * sin + lx,
 21657            y: x * sin + y * cos + ly
 21658          };
 21659        };
 21660  
 21661        var px1y1 = rotate(lx1, ly1);
 21662        var px1y2 = rotate(lx1, ly2);
 21663        var px2y1 = rotate(lx2, ly1);
 21664        var px2y2 = rotate(lx2, ly2);
 21665        var points = [px1y1.x, px1y1.y, px2y1.x, px2y1.y, px2y2.x, px2y2.y, px1y2.x, px1y2.y];
 21666  
 21667        if (pointInsidePolygonPoints(x, y, points)) {
 21668          addEle(ele);
 21669          return true;
 21670        }
 21671      } else {
 21672        // do a cheaper bb check
 21673        if (inBoundingBox(bb, x, y)) {
 21674          addEle(ele);
 21675          return true;
 21676        }
 21677      }
 21678    }
 21679  
 21680    for (var i = eles.length - 1; i >= 0; i--) {
 21681      // reverse order for precedence
 21682      var ele = eles[i];
 21683  
 21684      if (ele.isNode()) {
 21685        checkNode(ele) || checkLabel(ele);
 21686      } else {
 21687        // then edge
 21688        checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
 21689      }
 21690    }
 21691  
 21692    return near;
 21693  }; // 'Give me everything from this box'
 21694  
 21695  
 21696  BRp$1.getAllInBox = function (x1, y1, x2, y2) {
 21697    var eles = this.getCachedZSortedEles().interactive;
 21698    var box = [];
 21699    var x1c = Math.min(x1, x2);
 21700    var x2c = Math.max(x1, x2);
 21701    var y1c = Math.min(y1, y2);
 21702    var y2c = Math.max(y1, y2);
 21703    x1 = x1c;
 21704    x2 = x2c;
 21705    y1 = y1c;
 21706    y2 = y2c;
 21707    var boxBb = makeBoundingBox({
 21708      x1: x1,
 21709      y1: y1,
 21710      x2: x2,
 21711      y2: y2
 21712    });
 21713  
 21714    for (var e = 0; e < eles.length; e++) {
 21715      var ele = eles[e];
 21716  
 21717      if (ele.isNode()) {
 21718        var node = ele;
 21719        var nodeBb = node.boundingBox({
 21720          includeNodes: true,
 21721          includeEdges: false,
 21722          includeLabels: false
 21723        });
 21724  
 21725        if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
 21726          box.push(node);
 21727        }
 21728      } else {
 21729        var edge = ele;
 21730        var _p = edge._private;
 21731        var rs = _p.rscratch;
 21732  
 21733        if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
 21734          continue;
 21735        }
 21736  
 21737        if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
 21738          continue;
 21739        }
 21740  
 21741        if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
 21742          var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
 21743          var allInside = true;
 21744  
 21745          for (var i = 0; i < pts.length; i++) {
 21746            if (!pointInBoundingBox(boxBb, pts[i])) {
 21747              allInside = false;
 21748              break;
 21749            }
 21750          }
 21751  
 21752          if (allInside) {
 21753            box.push(edge);
 21754          }
 21755        } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
 21756          box.push(edge);
 21757        }
 21758      }
 21759    }
 21760  
 21761    return box;
 21762  };
 21763  
 21764  var BRp$2 = {};
 21765  
 21766  BRp$2.calculateArrowAngles = function (edge) {
 21767    var rs = edge._private.rscratch;
 21768    var isHaystack = rs.edgeType === 'haystack';
 21769    var isBezier = rs.edgeType === 'bezier';
 21770    var isMultibezier = rs.edgeType === 'multibezier';
 21771    var isSegments = rs.edgeType === 'segments';
 21772    var isCompound = rs.edgeType === 'compound';
 21773    var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
 21774  
 21775    var dispX, dispY;
 21776    var startX, startY, endX, endY, midX, midY;
 21777  
 21778    if (isHaystack) {
 21779      startX = rs.haystackPts[0];
 21780      startY = rs.haystackPts[1];
 21781      endX = rs.haystackPts[2];
 21782      endY = rs.haystackPts[3];
 21783    } else {
 21784      startX = rs.arrowStartX;
 21785      startY = rs.arrowStartY;
 21786      endX = rs.arrowEndX;
 21787      endY = rs.arrowEndY;
 21788    }
 21789  
 21790    midX = rs.midX;
 21791    midY = rs.midY; // source
 21792    //
 21793  
 21794    if (isSegments) {
 21795      dispX = startX - rs.segpts[0];
 21796      dispY = startY - rs.segpts[1];
 21797    } else if (isMultibezier || isCompound || isSelf || isBezier) {
 21798      var pts = rs.allpts;
 21799      var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
 21800      var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
 21801      dispX = startX - bX;
 21802      dispY = startY - bY;
 21803    } else {
 21804      dispX = startX - midX;
 21805      dispY = startY - midY;
 21806    }
 21807  
 21808    rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
 21809    //
 21810  
 21811    var midX = rs.midX;
 21812    var midY = rs.midY;
 21813  
 21814    if (isHaystack) {
 21815      midX = (startX + endX) / 2;
 21816      midY = (startY + endY) / 2;
 21817    }
 21818  
 21819    dispX = endX - startX;
 21820    dispY = endY - startY;
 21821  
 21822    if (isSegments) {
 21823      var pts = rs.allpts;
 21824  
 21825      if (pts.length / 2 % 2 === 0) {
 21826        var i2 = pts.length / 2;
 21827        var i1 = i2 - 2;
 21828        dispX = pts[i2] - pts[i1];
 21829        dispY = pts[i2 + 1] - pts[i1 + 1];
 21830      } else {
 21831        var i2 = pts.length / 2 - 1;
 21832        var i1 = i2 - 2;
 21833        var i3 = i2 + 2;
 21834        dispX = pts[i2] - pts[i1];
 21835        dispY = pts[i2 + 1] - pts[i1 + 1];
 21836      }
 21837    } else if (isMultibezier || isCompound || isSelf) {
 21838      var pts = rs.allpts;
 21839      var cpts = rs.ctrlpts;
 21840      var bp0x, bp0y;
 21841      var bp1x, bp1y;
 21842  
 21843      if (cpts.length / 2 % 2 === 0) {
 21844        var p0 = pts.length / 2 - 1; // startpt
 21845  
 21846        var ic = p0 + 2;
 21847        var p1 = ic + 2;
 21848        bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
 21849        bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
 21850        bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
 21851        bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
 21852      } else {
 21853        var ic = pts.length / 2 - 1; // ctrpt
 21854  
 21855        var p0 = ic - 2; // startpt
 21856  
 21857        var p1 = ic + 2; // endpt
 21858  
 21859        bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
 21860        bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
 21861        bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
 21862        bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
 21863      }
 21864  
 21865      dispX = bp1x - bp0x;
 21866      dispY = bp1y - bp0y;
 21867    }
 21868  
 21869    rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
 21870    rs.midDispX = dispX;
 21871    rs.midDispY = dispY; // mid source
 21872    //
 21873  
 21874    dispX *= -1;
 21875    dispY *= -1;
 21876  
 21877    if (isSegments) {
 21878      var pts = rs.allpts;
 21879  
 21880      if (pts.length / 2 % 2 === 0) ; else {
 21881        var i2 = pts.length / 2 - 1;
 21882        var i3 = i2 + 2;
 21883        dispX = -(pts[i3] - pts[i2]);
 21884        dispY = -(pts[i3 + 1] - pts[i2 + 1]);
 21885      }
 21886    }
 21887  
 21888    rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
 21889    //
 21890  
 21891    if (isSegments) {
 21892      dispX = endX - rs.segpts[rs.segpts.length - 2];
 21893      dispY = endY - rs.segpts[rs.segpts.length - 1];
 21894    } else if (isMultibezier || isCompound || isSelf || isBezier) {
 21895      var pts = rs.allpts;
 21896      var l = pts.length;
 21897      var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
 21898      var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
 21899      dispX = endX - bX;
 21900      dispY = endY - bY;
 21901    } else {
 21902      dispX = endX - midX;
 21903      dispY = endY - midY;
 21904    }
 21905  
 21906    rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
 21907  };
 21908  
 21909  BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
 21910    var cache = this.arrowWidthCache = this.arrowWidthCache || {};
 21911    var cachedVal = cache[edgeWidth + ', ' + scale];
 21912  
 21913    if (cachedVal) {
 21914      return cachedVal;
 21915    }
 21916  
 21917    cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
 21918    cache[edgeWidth + ', ' + scale] = cachedVal;
 21919    return cachedVal;
 21920  };
 21921  
 21922  var BRp$3 = {};
 21923  
 21924  BRp$3.findHaystackPoints = function (edges) {
 21925    for (var i = 0; i < edges.length; i++) {
 21926      var edge = edges[i];
 21927      var _p = edge._private;
 21928      var rs = _p.rscratch;
 21929  
 21930      if (!rs.haystack) {
 21931        var angle = Math.random() * 2 * Math.PI;
 21932        rs.source = {
 21933          x: Math.cos(angle),
 21934          y: Math.sin(angle)
 21935        };
 21936        angle = Math.random() * 2 * Math.PI;
 21937        rs.target = {
 21938          x: Math.cos(angle),
 21939          y: Math.sin(angle)
 21940        };
 21941      }
 21942  
 21943      var src = _p.source;
 21944      var tgt = _p.target;
 21945      var srcPos = src.position();
 21946      var tgtPos = tgt.position();
 21947      var srcW = src.width();
 21948      var tgtW = tgt.width();
 21949      var srcH = src.height();
 21950      var tgtH = tgt.height();
 21951      var radius = edge.pstyle('haystack-radius').value;
 21952      var halfRadius = radius / 2; // b/c have to half width/height
 21953  
 21954      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];
 21955      rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
 21956      rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
 21957  
 21958      rs.edgeType = 'haystack';
 21959      rs.haystack = true;
 21960      this.storeEdgeProjections(edge);
 21961      this.calculateArrowAngles(edge);
 21962      this.recalculateEdgeLabelProjections(edge);
 21963      this.calculateLabelAngles(edge);
 21964    }
 21965  };
 21966  
 21967  BRp$3.findSegmentsPoints = function (edge, pairInfo) {
 21968    // Segments (multiple straight lines)
 21969    var rs = edge._private.rscratch;
 21970    var posPts = pairInfo.posPts,
 21971        intersectionPts = pairInfo.intersectionPts,
 21972        vectorNormInverse = pairInfo.vectorNormInverse;
 21973    var edgeDistances = edge.pstyle('edge-distances').value;
 21974    var segmentWs = edge.pstyle('segment-weights');
 21975    var segmentDs = edge.pstyle('segment-distances');
 21976    var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
 21977    rs.edgeType = 'segments';
 21978    rs.segpts = [];
 21979  
 21980    for (var s = 0; s < segmentsN; s++) {
 21981      var w = segmentWs.pfValue[s];
 21982      var d = segmentDs.pfValue[s];
 21983      var w1 = 1 - w;
 21984      var w2 = w;
 21985      var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
 21986      var adjustedMidpt = {
 21987        x: midptPts.x1 * w1 + midptPts.x2 * w2,
 21988        y: midptPts.y1 * w1 + midptPts.y2 * w2
 21989      };
 21990      rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
 21991    }
 21992  };
 21993  
 21994  BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
 21995    // Self-edge
 21996    var rs = edge._private.rscratch;
 21997    var dirCounts = pairInfo.dirCounts,
 21998        srcPos = pairInfo.srcPos;
 21999    var ctrlptDists = edge.pstyle('control-point-distances');
 22000    var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
 22001    var loopDir = edge.pstyle('loop-direction').pfValue;
 22002    var loopSwp = edge.pstyle('loop-sweep').pfValue;
 22003    var stepSize = edge.pstyle('control-point-step-size').pfValue;
 22004    rs.edgeType = 'self';
 22005    var j = i;
 22006    var loopDist = stepSize;
 22007  
 22008    if (edgeIsUnbundled) {
 22009      j = 0;
 22010      loopDist = ctrlptDist;
 22011    }
 22012  
 22013    var loopAngle = loopDir - Math.PI / 2;
 22014    var outAngle = loopAngle - loopSwp / 2;
 22015    var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
 22016  
 22017    var dc = String(loopDir + '_' + loopSwp);
 22018    j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
 22019    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)];
 22020  };
 22021  
 22022  BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
 22023    // Compound edge
 22024    var rs = edge._private.rscratch;
 22025    rs.edgeType = 'compound';
 22026    var srcPos = pairInfo.srcPos,
 22027        tgtPos = pairInfo.tgtPos,
 22028        srcW = pairInfo.srcW,
 22029        srcH = pairInfo.srcH,
 22030        tgtW = pairInfo.tgtW,
 22031        tgtH = pairInfo.tgtH;
 22032    var stepSize = edge.pstyle('control-point-step-size').pfValue;
 22033    var ctrlptDists = edge.pstyle('control-point-distances');
 22034    var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
 22035    var j = i;
 22036    var loopDist = stepSize;
 22037  
 22038    if (edgeIsUnbundled) {
 22039      j = 0;
 22040      loopDist = ctrlptDist;
 22041    }
 22042  
 22043    var loopW = 50;
 22044    var loopaPos = {
 22045      x: srcPos.x - srcW / 2,
 22046      y: srcPos.y - srcH / 2
 22047    };
 22048    var loopbPos = {
 22049      x: tgtPos.x - tgtW / 2,
 22050      y: tgtPos.y - tgtH / 2
 22051    };
 22052    var loopPos = {
 22053      x: Math.min(loopaPos.x, loopbPos.x),
 22054      y: Math.min(loopaPos.y, loopbPos.y)
 22055    }; // avoids cases with impossible beziers
 22056  
 22057    var minCompoundStretch = 0.5;
 22058    var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
 22059    var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
 22060    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];
 22061  };
 22062  
 22063  BRp$3.findStraightEdgePoints = function (edge) {
 22064    // Straight edge within bundle
 22065    edge._private.rscratch.edgeType = 'straight';
 22066  };
 22067  
 22068  BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
 22069    var rs = edge._private.rscratch;
 22070    var vectorNormInverse = pairInfo.vectorNormInverse,
 22071        posPts = pairInfo.posPts,
 22072        intersectionPts = pairInfo.intersectionPts;
 22073    var edgeDistances = edge.pstyle('edge-distances').value;
 22074    var stepSize = edge.pstyle('control-point-step-size').pfValue;
 22075    var ctrlptDists = edge.pstyle('control-point-distances');
 22076    var ctrlptWs = edge.pstyle('control-point-weights');
 22077    var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
 22078    var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
 22079    var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
 22080  
 22081    var multi = edgeIsUnbundled;
 22082    rs.edgeType = multi ? 'multibezier' : 'bezier';
 22083    rs.ctrlpts = [];
 22084  
 22085    for (var b = 0; b < bezierN; b++) {
 22086      var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
 22087      var manctrlptDist = void 0;
 22088      var sign = signum(normctrlptDist);
 22089  
 22090      if (multi) {
 22091        ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
 22092  
 22093        ctrlptWeight = ctrlptWs.value[b];
 22094      }
 22095  
 22096      if (edgeIsUnbundled) {
 22097        // multi or single unbundled
 22098        manctrlptDist = ctrlptDist;
 22099      } else {
 22100        manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
 22101      }
 22102  
 22103      var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
 22104      var w1 = 1 - ctrlptWeight;
 22105      var w2 = ctrlptWeight;
 22106      var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
 22107      var adjustedMidpt = {
 22108        x: midptPts.x1 * w1 + midptPts.x2 * w2,
 22109        y: midptPts.y1 * w1 + midptPts.y2 * w2
 22110      };
 22111      rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
 22112    }
 22113  };
 22114  
 22115  BRp$3.findTaxiPoints = function (edge, pairInfo) {
 22116    // Taxicab geometry with two turns maximum
 22117    var rs = edge._private.rscratch;
 22118    rs.edgeType = 'segments';
 22119    var VERTICAL = 'vertical';
 22120    var HORIZONTAL = 'horizontal';
 22121    var LEFTWARD = 'leftward';
 22122    var RIGHTWARD = 'rightward';
 22123    var DOWNWARD = 'downward';
 22124    var UPWARD = 'upward';
 22125    var AUTO = 'auto';
 22126    var posPts = pairInfo.posPts,
 22127        srcW = pairInfo.srcW,
 22128        srcH = pairInfo.srcH,
 22129        tgtW = pairInfo.tgtW,
 22130        tgtH = pairInfo.tgtH;
 22131    var edgeDistances = edge.pstyle('edge-distances').value;
 22132    var dIncludesNodeBody = edgeDistances !== 'node-position';
 22133    var taxiDir = edge.pstyle('taxi-direction').value;
 22134    var rawTaxiDir = taxiDir; // unprocessed value
 22135  
 22136    var taxiTurn = edge.pstyle('taxi-turn');
 22137    var taxiTurnPfVal = taxiTurn.pfValue;
 22138    var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
 22139    var turnIsPercent = taxiTurn.units === '%';
 22140    var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
 22141    var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
 22142    var pdx = posPts.x2 - posPts.x1;
 22143    var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
 22144  
 22145    var subDWH = function subDWH(dxy, dwh) {
 22146      if (dxy > 0) {
 22147        return Math.max(dxy - dwh, 0);
 22148      } else {
 22149        return Math.min(dxy + dwh, 0);
 22150      }
 22151    };
 22152  
 22153    var dx = subDWH(pdx, dw);
 22154    var dy = subDWH(pdy, dh);
 22155    var isExplicitDir = false;
 22156  
 22157    if (taxiDir === AUTO) {
 22158      taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
 22159    } else if (taxiDir === UPWARD || taxiDir === DOWNWARD) {
 22160      taxiDir = VERTICAL;
 22161      isExplicitDir = true;
 22162    } else if (taxiDir === LEFTWARD || taxiDir === RIGHTWARD) {
 22163      taxiDir = HORIZONTAL;
 22164      isExplicitDir = true;
 22165    }
 22166  
 22167    var isVert = taxiDir === VERTICAL;
 22168    var l = isVert ? dy : dx;
 22169    var pl = isVert ? pdy : pdx;
 22170    var sgnL = signum(pl);
 22171    var forcedDir = false;
 22172  
 22173    if (!(isExplicitDir && turnIsPercent) // forcing in this case would cause weird growing in the opposite direction
 22174    && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
 22175      sgnL *= -1;
 22176      l = sgnL * Math.abs(l);
 22177      forcedDir = true;
 22178    }
 22179  
 22180    var d = turnIsPercent ? taxiTurnPfVal * l : taxiTurnPfVal * sgnL;
 22181  
 22182    var getIsTooClose = function getIsTooClose(d) {
 22183      return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
 22184    };
 22185  
 22186    var isTooCloseSrc = getIsTooClose(d);
 22187    var isTooCloseTgt = getIsTooClose(l - d);
 22188    var isTooClose = isTooCloseSrc || isTooCloseTgt;
 22189  
 22190    if (isTooClose && !forcedDir) {
 22191      // non-ideal routing
 22192      if (isVert) {
 22193        // vertical fallbacks
 22194        var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
 22195        var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
 22196  
 22197        if (lShapeInsideSrc) {
 22198          // horizontal Z-shape (direction not respected)
 22199          var x = (posPts.x1 + posPts.x2) / 2;
 22200          var y1 = posPts.y1,
 22201              y2 = posPts.y2;
 22202          rs.segpts = [x, y1, x, y2];
 22203        } else if (lShapeInsideTgt) {
 22204          // vertical Z-shape (distance not respected)
 22205          var y = (posPts.y1 + posPts.y2) / 2;
 22206          var x1 = posPts.x1,
 22207              x2 = posPts.x2;
 22208          rs.segpts = [x1, y, x2, y];
 22209        } else {
 22210          // L-shape fallback (turn distance not respected, but works well with tree siblings)
 22211          rs.segpts = [posPts.x1, posPts.y2];
 22212        }
 22213      } else {
 22214        // horizontal fallbacks
 22215        var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
 22216  
 22217        var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
 22218  
 22219        if (_lShapeInsideSrc) {
 22220          // vertical Z-shape (direction not respected)
 22221          var _y = (posPts.y1 + posPts.y2) / 2;
 22222  
 22223          var _x = posPts.x1,
 22224              _x2 = posPts.x2;
 22225          rs.segpts = [_x, _y, _x2, _y];
 22226        } else if (_lShapeInsideTgt) {
 22227          // horizontal Z-shape (turn distance not respected)
 22228          var _x3 = (posPts.x1 + posPts.x2) / 2;
 22229  
 22230          var _y2 = posPts.y1,
 22231              _y3 = posPts.y2;
 22232          rs.segpts = [_x3, _y2, _x3, _y3];
 22233        } else {
 22234          // L-shape (turn distance not respected, but works well for tree siblings)
 22235          rs.segpts = [posPts.x2, posPts.y1];
 22236        }
 22237      }
 22238    } else {
 22239      // ideal routing
 22240      if (isVert) {
 22241        var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
 22242  
 22243        var _x4 = posPts.x1,
 22244            _x5 = posPts.x2;
 22245        rs.segpts = [_x4, _y4, _x5, _y4];
 22246      } else {
 22247        // horizontal
 22248        var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
 22249  
 22250        var _y5 = posPts.y1,
 22251            _y6 = posPts.y2;
 22252        rs.segpts = [_x6, _y5, _x6, _y6];
 22253      }
 22254    }
 22255  };
 22256  
 22257  BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
 22258    var rs = edge._private.rscratch; // can only correct beziers for now...
 22259  
 22260    if (rs.edgeType === 'bezier') {
 22261      var srcPos = pairInfo.srcPos,
 22262          tgtPos = pairInfo.tgtPos,
 22263          srcW = pairInfo.srcW,
 22264          srcH = pairInfo.srcH,
 22265          tgtW = pairInfo.tgtW,
 22266          tgtH = pairInfo.tgtH,
 22267          srcShape = pairInfo.srcShape,
 22268          tgtShape = pairInfo.tgtShape;
 22269      var badStart = !number(rs.startX) || !number(rs.startY);
 22270      var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
 22271      var badEnd = !number(rs.endX) || !number(rs.endY);
 22272      var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
 22273      var minCpADistFactor = 3;
 22274      var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
 22275      var minCpADist = minCpADistFactor * arrowW;
 22276      var startACpDist = dist({
 22277        x: rs.ctrlpts[0],
 22278        y: rs.ctrlpts[1]
 22279      }, {
 22280        x: rs.startX,
 22281        y: rs.startY
 22282      });
 22283      var closeStartACp = startACpDist < minCpADist;
 22284      var endACpDist = dist({
 22285        x: rs.ctrlpts[0],
 22286        y: rs.ctrlpts[1]
 22287      }, {
 22288        x: rs.endX,
 22289        y: rs.endY
 22290      });
 22291      var closeEndACp = endACpDist < minCpADist;
 22292      var overlapping = false;
 22293  
 22294      if (badStart || badAStart || closeStartACp) {
 22295        overlapping = true; // project control point along line from src centre to outside the src shape
 22296        // (otherwise intersection will yield nothing)
 22297  
 22298        var cpD = {
 22299          // delta
 22300          x: rs.ctrlpts[0] - srcPos.x,
 22301          y: rs.ctrlpts[1] - srcPos.y
 22302        };
 22303        var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
 22304  
 22305        var cpM = {
 22306          // normalised delta
 22307          x: cpD.x / cpL,
 22308          y: cpD.y / cpL
 22309        };
 22310        var radius = Math.max(srcW, srcH);
 22311        var cpProj = {
 22312          // *2 radius guarantees outside shape
 22313          x: rs.ctrlpts[0] + cpM.x * 2 * radius,
 22314          y: rs.ctrlpts[1] + cpM.y * 2 * radius
 22315        };
 22316        var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
 22317  
 22318        if (closeStartACp) {
 22319          rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
 22320          rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
 22321        } else {
 22322          rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
 22323          rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
 22324        }
 22325      }
 22326  
 22327      if (badEnd || badAEnd || closeEndACp) {
 22328        overlapping = true; // project control point along line from tgt centre to outside the tgt shape
 22329        // (otherwise intersection will yield nothing)
 22330  
 22331        var _cpD = {
 22332          // delta
 22333          x: rs.ctrlpts[0] - tgtPos.x,
 22334          y: rs.ctrlpts[1] - tgtPos.y
 22335        };
 22336  
 22337        var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
 22338  
 22339  
 22340        var _cpM = {
 22341          // normalised delta
 22342          x: _cpD.x / _cpL,
 22343          y: _cpD.y / _cpL
 22344        };
 22345  
 22346        var _radius = Math.max(srcW, srcH);
 22347  
 22348        var _cpProj = {
 22349          // *2 radius guarantees outside shape
 22350          x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
 22351          y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
 22352        };
 22353        var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
 22354  
 22355        if (closeEndACp) {
 22356          rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
 22357          rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
 22358        } else {
 22359          rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
 22360          rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
 22361        }
 22362      }
 22363  
 22364      if (overlapping) {
 22365        // recalc endpts
 22366        this.findEndpoints(edge);
 22367      }
 22368    }
 22369  };
 22370  
 22371  BRp$3.storeAllpts = function (edge) {
 22372    var rs = edge._private.rscratch;
 22373  
 22374    if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
 22375      rs.allpts = [];
 22376      rs.allpts.push(rs.startX, rs.startY);
 22377  
 22378      for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
 22379        // ctrl pt itself
 22380        rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
 22381  
 22382        if (b + 3 < rs.ctrlpts.length) {
 22383          rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
 22384        }
 22385      }
 22386  
 22387      rs.allpts.push(rs.endX, rs.endY);
 22388      var m, mt;
 22389  
 22390      if (rs.ctrlpts.length / 2 % 2 === 0) {
 22391        m = rs.allpts.length / 2 - 1;
 22392        rs.midX = rs.allpts[m];
 22393        rs.midY = rs.allpts[m + 1];
 22394      } else {
 22395        m = rs.allpts.length / 2 - 3;
 22396        mt = 0.5;
 22397        rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
 22398        rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
 22399      }
 22400    } else if (rs.edgeType === 'straight') {
 22401      // need to calc these after endpts
 22402      rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
 22403  
 22404      rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
 22405      rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
 22406    } else if (rs.edgeType === 'segments') {
 22407      rs.allpts = [];
 22408      rs.allpts.push(rs.startX, rs.startY);
 22409      rs.allpts.push.apply(rs.allpts, rs.segpts);
 22410      rs.allpts.push(rs.endX, rs.endY);
 22411  
 22412      if (rs.segpts.length % 4 === 0) {
 22413        var i2 = rs.segpts.length / 2;
 22414        var i1 = i2 - 2;
 22415        rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
 22416        rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
 22417      } else {
 22418        var _i = rs.segpts.length / 2 - 1;
 22419  
 22420        rs.midX = rs.segpts[_i];
 22421        rs.midY = rs.segpts[_i + 1];
 22422      }
 22423    }
 22424  };
 22425  
 22426  BRp$3.checkForInvalidEdgeWarning = function (edge) {
 22427    var rs = edge[0]._private.rscratch;
 22428  
 22429    if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
 22430      rs.loggedErr = false;
 22431    } else {
 22432      if (!rs.loggedErr) {
 22433        rs.loggedErr = true;
 22434        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.');
 22435      }
 22436    }
 22437  };
 22438  
 22439  BRp$3.findEdgeControlPoints = function (edges) {
 22440    var _this = this;
 22441  
 22442    if (!edges || edges.length === 0) {
 22443      return;
 22444    }
 22445  
 22446    var r = this;
 22447    var cy = r.cy;
 22448    var hasCompounds = cy.hasCompoundNodes();
 22449    var hashTable = {
 22450      map: new Map$1(),
 22451      get: function get(pairId) {
 22452        var map2 = this.map.get(pairId[0]);
 22453  
 22454        if (map2 != null) {
 22455          return map2.get(pairId[1]);
 22456        } else {
 22457          return null;
 22458        }
 22459      },
 22460      set: function set(pairId, val) {
 22461        var map2 = this.map.get(pairId[0]);
 22462  
 22463        if (map2 == null) {
 22464          map2 = new Map$1();
 22465          this.map.set(pairId[0], map2);
 22466        }
 22467  
 22468        map2.set(pairId[1], val);
 22469      }
 22470    };
 22471    var pairIds = [];
 22472    var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
 22473  
 22474    for (var i = 0; i < edges.length; i++) {
 22475      var edge = edges[i];
 22476      var _p = edge._private;
 22477      var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
 22478      // they shouldn't take up space
 22479  
 22480      if (edge.removed() || !edge.takesUpSpace()) {
 22481        continue;
 22482      }
 22483  
 22484      if (curveStyle === 'haystack') {
 22485        haystackEdges.push(edge);
 22486        continue;
 22487      }
 22488  
 22489      var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'taxi';
 22490      var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
 22491      var src = _p.source;
 22492      var tgt = _p.target;
 22493      var srcIndex = src.poolIndex();
 22494      var tgtIndex = tgt.poolIndex();
 22495      var pairId = [srcIndex, tgtIndex].sort();
 22496      var tableEntry = hashTable.get(pairId);
 22497  
 22498      if (tableEntry == null) {
 22499        tableEntry = {
 22500          eles: []
 22501        };
 22502        hashTable.set(pairId, tableEntry);
 22503        pairIds.push(pairId);
 22504      }
 22505  
 22506      tableEntry.eles.push(edge);
 22507  
 22508      if (edgeIsUnbundled) {
 22509        tableEntry.hasUnbundled = true;
 22510      }
 22511  
 22512      if (edgeIsBezier) {
 22513        tableEntry.hasBezier = true;
 22514      }
 22515    } // for each pair (src, tgt), create the ctrl pts
 22516    // Nested for loop is OK; total number of iterations for both loops = edgeCount
 22517  
 22518  
 22519    var _loop = function _loop(p) {
 22520      var pairId = pairIds[p];
 22521      var pairInfo = hashTable.get(pairId);
 22522      var swappedpairInfo = void 0;
 22523  
 22524      if (!pairInfo.hasUnbundled) {
 22525        var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
 22526          return e.isBundledBezier();
 22527        });
 22528        clearArray(pairInfo.eles);
 22529        pllEdges.forEach(function (edge) {
 22530          return pairInfo.eles.push(edge);
 22531        }); // for each pair id, the edges should be sorted by index
 22532  
 22533        pairInfo.eles.sort(function (edge1, edge2) {
 22534          return edge1.poolIndex() - edge2.poolIndex();
 22535        });
 22536      }
 22537  
 22538      var firstEdge = pairInfo.eles[0];
 22539      var src = firstEdge.source();
 22540      var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
 22541  
 22542      if (src.poolIndex() > tgt.poolIndex()) {
 22543        var temp = src;
 22544        src = tgt;
 22545        tgt = temp;
 22546      }
 22547  
 22548      var srcPos = pairInfo.srcPos = src.position();
 22549      var tgtPos = pairInfo.tgtPos = tgt.position();
 22550      var srcW = pairInfo.srcW = src.outerWidth();
 22551      var srcH = pairInfo.srcH = src.outerHeight();
 22552      var tgtW = pairInfo.tgtW = tgt.outerWidth();
 22553      var tgtH = pairInfo.tgtH = tgt.outerHeight();
 22554  
 22555      var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
 22556  
 22557      var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
 22558  
 22559      pairInfo.dirCounts = {
 22560        'north': 0,
 22561        'west': 0,
 22562        'south': 0,
 22563        'east': 0,
 22564        'northwest': 0,
 22565        'southwest': 0,
 22566        'northeast': 0,
 22567        'southeast': 0
 22568      };
 22569  
 22570      for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
 22571        var _edge = pairInfo.eles[_i2];
 22572        var rs = _edge[0]._private.rscratch;
 22573  
 22574        var _curveStyle = _edge.pstyle('curve-style').value;
 22575  
 22576        var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
 22577  
 22578  
 22579        var edgeIsSwapped = !src.same(_edge.source());
 22580  
 22581        if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
 22582          pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
 22583  
 22584          var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
 22585          var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
 22586  
 22587          var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
 22588          var tgtIntn = pairInfo.tgtIntn = tgtOutside;
 22589          var intersectionPts = pairInfo.intersectionPts = {
 22590            x1: srcOutside[0],
 22591            x2: tgtOutside[0],
 22592            y1: srcOutside[1],
 22593            y2: tgtOutside[1]
 22594          };
 22595          var posPts = pairInfo.posPts = {
 22596            x1: srcPos.x,
 22597            x2: tgtPos.x,
 22598            y1: srcPos.y,
 22599            y2: tgtPos.y
 22600          };
 22601          var dy = tgtOutside[1] - srcOutside[1];
 22602          var dx = tgtOutside[0] - srcOutside[0];
 22603          var l = Math.sqrt(dx * dx + dy * dy);
 22604          var vector = pairInfo.vector = {
 22605            x: dx,
 22606            y: dy
 22607          };
 22608          var vectorNorm = pairInfo.vectorNorm = {
 22609            x: vector.x / l,
 22610            y: vector.y / l
 22611          };
 22612          var vectorNormInverse = {
 22613            x: -vectorNorm.y,
 22614            y: vectorNorm.x
 22615          }; // if node shapes overlap, then no ctrl pts to draw
 22616  
 22617          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);
 22618          pairInfo.vectorNormInverse = vectorNormInverse;
 22619          swappedpairInfo = {
 22620            nodesOverlap: pairInfo.nodesOverlap,
 22621            dirCounts: pairInfo.dirCounts,
 22622            calculatedIntersection: true,
 22623            hasBezier: pairInfo.hasBezier,
 22624            hasUnbundled: pairInfo.hasUnbundled,
 22625            eles: pairInfo.eles,
 22626            srcPos: tgtPos,
 22627            tgtPos: srcPos,
 22628            srcW: tgtW,
 22629            srcH: tgtH,
 22630            tgtW: srcW,
 22631            tgtH: srcH,
 22632            srcIntn: tgtIntn,
 22633            tgtIntn: srcIntn,
 22634            srcShape: tgtShape,
 22635            tgtShape: srcShape,
 22636            posPts: {
 22637              x1: posPts.x2,
 22638              y1: posPts.y2,
 22639              x2: posPts.x1,
 22640              y2: posPts.y1
 22641            },
 22642            intersectionPts: {
 22643              x1: intersectionPts.x2,
 22644              y1: intersectionPts.y2,
 22645              x2: intersectionPts.x1,
 22646              y2: intersectionPts.y1
 22647            },
 22648            vector: {
 22649              x: -vector.x,
 22650              y: -vector.y
 22651            },
 22652            vectorNorm: {
 22653              x: -vectorNorm.x,
 22654              y: -vectorNorm.y
 22655            },
 22656            vectorNormInverse: {
 22657              x: -vectorNormInverse.x,
 22658              y: -vectorNormInverse.y
 22659            }
 22660          };
 22661        }
 22662  
 22663        var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
 22664        rs.nodesOverlap = passedPairInfo.nodesOverlap;
 22665        rs.srcIntn = passedPairInfo.srcIntn;
 22666        rs.tgtIntn = passedPairInfo.tgtIntn;
 22667  
 22668        if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
 22669          _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
 22670        } else if (src === tgt) {
 22671          _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
 22672        } else if (_curveStyle === 'segments') {
 22673          _this.findSegmentsPoints(_edge, passedPairInfo);
 22674        } else if (_curveStyle === 'taxi') {
 22675          _this.findTaxiPoints(_edge, passedPairInfo);
 22676        } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
 22677          _this.findStraightEdgePoints(_edge);
 22678        } else {
 22679          _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
 22680        }
 22681  
 22682        _this.findEndpoints(_edge);
 22683  
 22684        _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
 22685  
 22686        _this.checkForInvalidEdgeWarning(_edge);
 22687  
 22688        _this.storeAllpts(_edge);
 22689  
 22690        _this.storeEdgeProjections(_edge);
 22691  
 22692        _this.calculateArrowAngles(_edge);
 22693  
 22694        _this.recalculateEdgeLabelProjections(_edge);
 22695  
 22696        _this.calculateLabelAngles(_edge);
 22697      } // for pair edges
 22698  
 22699    };
 22700  
 22701    for (var p = 0; p < pairIds.length; p++) {
 22702      _loop(p);
 22703    } // for pair ids
 22704    // haystacks avoid the expense of pairInfo stuff (intersections etc.)
 22705  
 22706  
 22707    this.findHaystackPoints(haystackEdges);
 22708  };
 22709  
 22710  function getPts(pts) {
 22711    var retPts = [];
 22712  
 22713    if (pts == null) {
 22714      return;
 22715    }
 22716  
 22717    for (var i = 0; i < pts.length; i += 2) {
 22718      var x = pts[i];
 22719      var y = pts[i + 1];
 22720      retPts.push({
 22721        x: x,
 22722        y: y
 22723      });
 22724    }
 22725  
 22726    return retPts;
 22727  }
 22728  
 22729  BRp$3.getSegmentPoints = function (edge) {
 22730    var rs = edge[0]._private.rscratch;
 22731    var type = rs.edgeType;
 22732  
 22733    if (type === 'segments') {
 22734      this.recalculateRenderedStyle(edge);
 22735      return getPts(rs.segpts);
 22736    }
 22737  };
 22738  
 22739  BRp$3.getControlPoints = function (edge) {
 22740    var rs = edge[0]._private.rscratch;
 22741    var type = rs.edgeType;
 22742  
 22743    if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
 22744      this.recalculateRenderedStyle(edge);
 22745      return getPts(rs.ctrlpts);
 22746    }
 22747  };
 22748  
 22749  BRp$3.getEdgeMidpoint = function (edge) {
 22750    var rs = edge[0]._private.rscratch;
 22751    this.recalculateRenderedStyle(edge);
 22752    return {
 22753      x: rs.midX,
 22754      y: rs.midY
 22755    };
 22756  };
 22757  
 22758  var BRp$4 = {};
 22759  
 22760  BRp$4.manualEndptToPx = function (node, prop) {
 22761    var r = this;
 22762    var npos = node.position();
 22763    var w = node.outerWidth();
 22764    var h = node.outerHeight();
 22765  
 22766    if (prop.value.length === 2) {
 22767      var p = [prop.pfValue[0], prop.pfValue[1]];
 22768  
 22769      if (prop.units[0] === '%') {
 22770        p[0] = p[0] * w;
 22771      }
 22772  
 22773      if (prop.units[1] === '%') {
 22774        p[1] = p[1] * h;
 22775      }
 22776  
 22777      p[0] += npos.x;
 22778      p[1] += npos.y;
 22779      return p;
 22780    } else {
 22781      var angle = prop.pfValue[0];
 22782      angle = -Math.PI / 2 + angle; // start at 12 o'clock
 22783  
 22784      var l = 2 * Math.max(w, h);
 22785      var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
 22786      return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
 22787    }
 22788  };
 22789  
 22790  BRp$4.findEndpoints = function (edge) {
 22791    var r = this;
 22792    var intersect;
 22793    var source = edge.source()[0];
 22794    var target = edge.target()[0];
 22795    var srcPos = source.position();
 22796    var tgtPos = target.position();
 22797    var tgtArShape = edge.pstyle('target-arrow-shape').value;
 22798    var srcArShape = edge.pstyle('source-arrow-shape').value;
 22799    var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
 22800    var srcDist = edge.pstyle('source-distance-from-node').pfValue;
 22801    var curveStyle = edge.pstyle('curve-style').value;
 22802    var rs = edge._private.rscratch;
 22803    var et = rs.edgeType;
 22804    var taxi = curveStyle === 'taxi';
 22805    var self = et === 'self' || et === 'compound';
 22806    var bezier = et === 'bezier' || et === 'multibezier' || self;
 22807    var multi = et !== 'bezier';
 22808    var lines = et === 'straight' || et === 'segments';
 22809    var segments = et === 'segments';
 22810    var hasEndpts = bezier || multi || lines;
 22811    var overrideEndpts = self || taxi;
 22812    var srcManEndpt = edge.pstyle('source-endpoint');
 22813    var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
 22814    var tgtManEndpt = edge.pstyle('target-endpoint');
 22815    var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
 22816    rs.srcManEndpt = srcManEndpt;
 22817    rs.tgtManEndpt = tgtManEndpt;
 22818    var p1; // last known point of edge on target side
 22819  
 22820    var p2; // last known point of edge on source side
 22821  
 22822    var p1_i; // point to intersect with target shape
 22823  
 22824    var p2_i; // point to intersect with source shape
 22825  
 22826    if (bezier) {
 22827      var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
 22828      var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
 22829      p1 = cpEnd;
 22830      p2 = cpStart;
 22831    } else if (lines) {
 22832      var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
 22833      var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
 22834      p1 = tgtArrowFromPt;
 22835      p2 = srcArrowFromPt;
 22836    }
 22837  
 22838    if (tgtManEndptVal === 'inside-to-node') {
 22839      intersect = [tgtPos.x, tgtPos.y];
 22840    } else if (tgtManEndpt.units) {
 22841      intersect = this.manualEndptToPx(target, tgtManEndpt);
 22842    } else if (tgtManEndptVal === 'outside-to-line') {
 22843      intersect = rs.tgtIntn; // use cached value from ctrlpt calc
 22844    } else {
 22845      if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
 22846        p1_i = p1;
 22847      } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
 22848        p1_i = [srcPos.x, srcPos.y];
 22849      }
 22850  
 22851      intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
 22852  
 22853      if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
 22854        var trs = target._private.rscratch;
 22855        var lw = trs.labelWidth;
 22856        var lh = trs.labelHeight;
 22857        var lx = trs.labelX;
 22858        var ly = trs.labelY;
 22859        var lw2 = lw / 2;
 22860        var lh2 = lh / 2;
 22861        var va = target.pstyle('text-valign').value;
 22862  
 22863        if (va === 'top') {
 22864          ly -= lh2;
 22865        } else if (va === 'bottom') {
 22866          ly += lh2;
 22867        }
 22868  
 22869        var ha = target.pstyle('text-halign').value;
 22870  
 22871        if (ha === 'left') {
 22872          lx -= lw2;
 22873        } else if (ha === 'right') {
 22874          lx += lw2;
 22875        }
 22876  
 22877        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);
 22878  
 22879        if (labelIntersect.length > 0) {
 22880          var refPt = srcPos;
 22881          var intSqdist = sqdist(refPt, array2point(intersect));
 22882          var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
 22883          var minSqDist = intSqdist;
 22884  
 22885          if (labIntSqdist < intSqdist) {
 22886            intersect = labelIntersect;
 22887            minSqDist = labIntSqdist;
 22888          }
 22889  
 22890          if (labelIntersect.length > 2) {
 22891            var labInt2SqDist = sqdist(refPt, {
 22892              x: labelIntersect[2],
 22893              y: labelIntersect[3]
 22894            });
 22895  
 22896            if (labInt2SqDist < minSqDist) {
 22897              intersect = [labelIntersect[2], labelIntersect[3]];
 22898            }
 22899          }
 22900        }
 22901      }
 22902    }
 22903  
 22904    var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
 22905    var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
 22906    rs.endX = edgeEnd[0];
 22907    rs.endY = edgeEnd[1];
 22908    rs.arrowEndX = arrowEnd[0];
 22909    rs.arrowEndY = arrowEnd[1];
 22910  
 22911    if (srcManEndptVal === 'inside-to-node') {
 22912      intersect = [srcPos.x, srcPos.y];
 22913    } else if (srcManEndpt.units) {
 22914      intersect = this.manualEndptToPx(source, srcManEndpt);
 22915    } else if (srcManEndptVal === 'outside-to-line') {
 22916      intersect = rs.srcIntn; // use cached value from ctrlpt calc
 22917    } else {
 22918      if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
 22919        p2_i = p2;
 22920      } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
 22921        p2_i = [tgtPos.x, tgtPos.y];
 22922      }
 22923  
 22924      intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
 22925  
 22926      if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
 22927        var srs = source._private.rscratch;
 22928        var _lw = srs.labelWidth;
 22929        var _lh = srs.labelHeight;
 22930        var _lx = srs.labelX;
 22931        var _ly = srs.labelY;
 22932  
 22933        var _lw2 = _lw / 2;
 22934  
 22935        var _lh2 = _lh / 2;
 22936  
 22937        var _va = source.pstyle('text-valign').value;
 22938  
 22939        if (_va === 'top') {
 22940          _ly -= _lh2;
 22941        } else if (_va === 'bottom') {
 22942          _ly += _lh2;
 22943        }
 22944  
 22945        var _ha = source.pstyle('text-halign').value;
 22946  
 22947        if (_ha === 'left') {
 22948          _lx -= _lw2;
 22949        } else if (_ha === 'right') {
 22950          _lx += _lw2;
 22951        }
 22952  
 22953        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);
 22954  
 22955        if (_labelIntersect.length > 0) {
 22956          var _refPt = tgtPos;
 22957  
 22958          var _intSqdist = sqdist(_refPt, array2point(intersect));
 22959  
 22960          var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
 22961  
 22962          var _minSqDist = _intSqdist;
 22963  
 22964          if (_labIntSqdist < _intSqdist) {
 22965            intersect = [_labelIntersect[0], _labelIntersect[1]];
 22966            _minSqDist = _labIntSqdist;
 22967          }
 22968  
 22969          if (_labelIntersect.length > 2) {
 22970            var _labInt2SqDist = sqdist(_refPt, {
 22971              x: _labelIntersect[2],
 22972              y: _labelIntersect[3]
 22973            });
 22974  
 22975            if (_labInt2SqDist < _minSqDist) {
 22976              intersect = [_labelIntersect[2], _labelIntersect[3]];
 22977            }
 22978          }
 22979        }
 22980      }
 22981    }
 22982  
 22983    var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
 22984    var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
 22985    rs.startX = edgeStart[0];
 22986    rs.startY = edgeStart[1];
 22987    rs.arrowStartX = arrowStart[0];
 22988    rs.arrowStartY = arrowStart[1];
 22989  
 22990    if (hasEndpts) {
 22991      if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
 22992        rs.badLine = true;
 22993      } else {
 22994        rs.badLine = false;
 22995      }
 22996    }
 22997  };
 22998  
 22999  BRp$4.getSourceEndpoint = function (edge) {
 23000    var rs = edge[0]._private.rscratch;
 23001    this.recalculateRenderedStyle(edge);
 23002  
 23003    switch (rs.edgeType) {
 23004      case 'haystack':
 23005        return {
 23006          x: rs.haystackPts[0],
 23007          y: rs.haystackPts[1]
 23008        };
 23009  
 23010      default:
 23011        return {
 23012          x: rs.arrowStartX,
 23013          y: rs.arrowStartY
 23014        };
 23015    }
 23016  };
 23017  
 23018  BRp$4.getTargetEndpoint = function (edge) {
 23019    var rs = edge[0]._private.rscratch;
 23020    this.recalculateRenderedStyle(edge);
 23021  
 23022    switch (rs.edgeType) {
 23023      case 'haystack':
 23024        return {
 23025          x: rs.haystackPts[2],
 23026          y: rs.haystackPts[3]
 23027        };
 23028  
 23029      default:
 23030        return {
 23031          x: rs.arrowEndX,
 23032          y: rs.arrowEndY
 23033        };
 23034    }
 23035  };
 23036  
 23037  var BRp$5 = {};
 23038  
 23039  function pushBezierPts(r, edge, pts) {
 23040    var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
 23041      return qbezierAt(p1, p2, p3, t);
 23042    };
 23043  
 23044    var _p = edge._private;
 23045    var bpts = _p.rstyle.bezierPts;
 23046  
 23047    for (var i = 0; i < r.bezierProjPcts.length; i++) {
 23048      var p = r.bezierProjPcts[i];
 23049      bpts.push({
 23050        x: qbezierAt$1(pts[0], pts[2], pts[4], p),
 23051        y: qbezierAt$1(pts[1], pts[3], pts[5], p)
 23052      });
 23053    }
 23054  }
 23055  
 23056  BRp$5.storeEdgeProjections = function (edge) {
 23057    var _p = edge._private;
 23058    var rs = _p.rscratch;
 23059    var et = rs.edgeType; // clear the cached points state
 23060  
 23061    _p.rstyle.bezierPts = null;
 23062    _p.rstyle.linePts = null;
 23063    _p.rstyle.haystackPts = null;
 23064  
 23065    if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
 23066      _p.rstyle.bezierPts = [];
 23067  
 23068      for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
 23069        pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
 23070      }
 23071    } else if (et === 'segments') {
 23072      var lpts = _p.rstyle.linePts = [];
 23073  
 23074      for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
 23075        lpts.push({
 23076          x: rs.allpts[i],
 23077          y: rs.allpts[i + 1]
 23078        });
 23079      }
 23080    } else if (et === 'haystack') {
 23081      var hpts = rs.haystackPts;
 23082      _p.rstyle.haystackPts = [{
 23083        x: hpts[0],
 23084        y: hpts[1]
 23085      }, {
 23086        x: hpts[2],
 23087        y: hpts[3]
 23088      }];
 23089    }
 23090  
 23091    _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
 23092  };
 23093  
 23094  BRp$5.recalculateEdgeProjections = function (edges) {
 23095    this.findEdgeControlPoints(edges);
 23096  };
 23097  
 23098  var BRp$6 = {};
 23099  
 23100  BRp$6.recalculateNodeLabelProjection = function (node) {
 23101    var content = node.pstyle('label').strValue;
 23102  
 23103    if (emptyString(content)) {
 23104      return;
 23105    }
 23106  
 23107    var textX, textY;
 23108    var _p = node._private;
 23109    var nodeWidth = node.width();
 23110    var nodeHeight = node.height();
 23111    var padding = node.padding();
 23112    var nodePos = node.position();
 23113    var textHalign = node.pstyle('text-halign').strValue;
 23114    var textValign = node.pstyle('text-valign').strValue;
 23115    var rs = _p.rscratch;
 23116    var rstyle = _p.rstyle;
 23117  
 23118    switch (textHalign) {
 23119      case 'left':
 23120        textX = nodePos.x - nodeWidth / 2 - padding;
 23121        break;
 23122  
 23123      case 'right':
 23124        textX = nodePos.x + nodeWidth / 2 + padding;
 23125        break;
 23126  
 23127      default:
 23128        // e.g. center
 23129        textX = nodePos.x;
 23130    }
 23131  
 23132    switch (textValign) {
 23133      case 'top':
 23134        textY = nodePos.y - nodeHeight / 2 - padding;
 23135        break;
 23136  
 23137      case 'bottom':
 23138        textY = nodePos.y + nodeHeight / 2 + padding;
 23139        break;
 23140  
 23141      default:
 23142        // e.g. middle
 23143        textY = nodePos.y;
 23144    }
 23145  
 23146    rs.labelX = textX;
 23147    rs.labelY = textY;
 23148    rstyle.labelX = textX;
 23149    rstyle.labelY = textY;
 23150    this.applyLabelDimensions(node);
 23151  };
 23152  
 23153  var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
 23154    var angle = Math.atan(dy / dx);
 23155  
 23156    if (dx === 0 && angle < 0) {
 23157      angle = angle * -1;
 23158    }
 23159  
 23160    return angle;
 23161  };
 23162  
 23163  var lineAngle = function lineAngle(p0, p1) {
 23164    var dx = p1.x - p0.x;
 23165    var dy = p1.y - p0.y;
 23166    return lineAngleFromDelta(dx, dy);
 23167  };
 23168  
 23169  var bezierAngle = function bezierAngle(p0, p1, p2, t) {
 23170    var t0 = bound(0, t - 0.001, 1);
 23171    var t1 = bound(0, t + 0.001, 1);
 23172    var lp0 = qbezierPtAt(p0, p1, p2, t0);
 23173    var lp1 = qbezierPtAt(p0, p1, p2, t1);
 23174    return lineAngle(lp0, lp1);
 23175  };
 23176  
 23177  BRp$6.recalculateEdgeLabelProjections = function (edge) {
 23178    var p;
 23179    var _p = edge._private;
 23180    var rs = _p.rscratch;
 23181    var r = this;
 23182    var content = {
 23183      mid: edge.pstyle('label').strValue,
 23184      source: edge.pstyle('source-label').strValue,
 23185      target: edge.pstyle('target-label').strValue
 23186    };
 23187  
 23188    if (content.mid || content.source || content.target) ; else {
 23189        return; // no labels => no calcs
 23190      } // add center point to style so bounding box calculations can use it
 23191    //
 23192  
 23193  
 23194    p = {
 23195      x: rs.midX,
 23196      y: rs.midY
 23197    };
 23198  
 23199    var setRs = function setRs(propName, prefix, value) {
 23200      setPrefixedProperty(_p.rscratch, propName, prefix, value);
 23201      setPrefixedProperty(_p.rstyle, propName, prefix, value);
 23202    };
 23203  
 23204    setRs('labelX', null, p.x);
 23205    setRs('labelY', null, p.y);
 23206    var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
 23207    setRs('labelAutoAngle', null, midAngle);
 23208  
 23209    var createControlPointInfo = function createControlPointInfo() {
 23210      if (createControlPointInfo.cache) {
 23211        return createControlPointInfo.cache;
 23212      } // use cache so only 1x per edge
 23213  
 23214  
 23215      var ctrlpts = []; // store each ctrlpt info init
 23216  
 23217      for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
 23218        var p0 = {
 23219          x: rs.allpts[i],
 23220          y: rs.allpts[i + 1]
 23221        };
 23222        var p1 = {
 23223          x: rs.allpts[i + 2],
 23224          y: rs.allpts[i + 3]
 23225        }; // ctrlpt
 23226  
 23227        var p2 = {
 23228          x: rs.allpts[i + 4],
 23229          y: rs.allpts[i + 5]
 23230        };
 23231        ctrlpts.push({
 23232          p0: p0,
 23233          p1: p1,
 23234          p2: p2,
 23235          startDist: 0,
 23236          length: 0,
 23237          segments: []
 23238        });
 23239      }
 23240  
 23241      var bpts = _p.rstyle.bezierPts;
 23242      var nProjs = r.bezierProjPcts.length;
 23243  
 23244      function addSegment(cp, p0, p1, t0, t1) {
 23245        var length = dist(p0, p1);
 23246        var prevSegment = cp.segments[cp.segments.length - 1];
 23247        var segment = {
 23248          p0: p0,
 23249          p1: p1,
 23250          t0: t0,
 23251          t1: t1,
 23252          startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
 23253          length: length
 23254        };
 23255        cp.segments.push(segment);
 23256        cp.length += length;
 23257      } // update each ctrlpt with segment info
 23258  
 23259  
 23260      for (var _i = 0; _i < ctrlpts.length; _i++) {
 23261        var cp = ctrlpts[_i];
 23262        var prevCp = ctrlpts[_i - 1];
 23263  
 23264        if (prevCp) {
 23265          cp.startDist = prevCp.startDist + prevCp.length;
 23266        }
 23267  
 23268        addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
 23269  
 23270        for (var j = 0; j < nProjs - 1; j++) {
 23271          addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
 23272        }
 23273  
 23274        addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
 23275      }
 23276  
 23277      return createControlPointInfo.cache = ctrlpts;
 23278    };
 23279  
 23280    var calculateEndProjection = function calculateEndProjection(prefix) {
 23281      var angle;
 23282      var isSrc = prefix === 'source';
 23283  
 23284      if (!content[prefix]) {
 23285        return;
 23286      }
 23287  
 23288      var offset = edge.pstyle(prefix + '-text-offset').pfValue;
 23289  
 23290      switch (rs.edgeType) {
 23291        case 'self':
 23292        case 'compound':
 23293        case 'bezier':
 23294        case 'multibezier':
 23295          {
 23296            var cps = createControlPointInfo();
 23297            var selected;
 23298            var startDist = 0;
 23299            var totalDist = 0; // find the segment we're on
 23300  
 23301            for (var i = 0; i < cps.length; i++) {
 23302              var _cp = cps[isSrc ? i : cps.length - 1 - i];
 23303  
 23304              for (var j = 0; j < _cp.segments.length; j++) {
 23305                var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
 23306                var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
 23307                startDist = totalDist;
 23308                totalDist += _seg.length;
 23309  
 23310                if (totalDist >= offset || lastSeg) {
 23311                  selected = {
 23312                    cp: _cp,
 23313                    segment: _seg
 23314                  };
 23315                  break;
 23316                }
 23317              }
 23318  
 23319              if (selected) {
 23320                break;
 23321              }
 23322            }
 23323  
 23324            var cp = selected.cp;
 23325            var seg = selected.segment;
 23326            var tSegment = (offset - startDist) / seg.length;
 23327            var segDt = seg.t1 - seg.t0;
 23328            var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
 23329            t = bound(0, t, 1);
 23330            p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
 23331            angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
 23332            break;
 23333          }
 23334  
 23335        case 'straight':
 23336        case 'segments':
 23337        case 'haystack':
 23338          {
 23339            var d = 0,
 23340                di,
 23341                d0;
 23342            var p0, p1;
 23343            var l = rs.allpts.length;
 23344  
 23345            for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
 23346              if (isSrc) {
 23347                p0 = {
 23348                  x: rs.allpts[_i2],
 23349                  y: rs.allpts[_i2 + 1]
 23350                };
 23351                p1 = {
 23352                  x: rs.allpts[_i2 + 2],
 23353                  y: rs.allpts[_i2 + 3]
 23354                };
 23355              } else {
 23356                p0 = {
 23357                  x: rs.allpts[l - 2 - _i2],
 23358                  y: rs.allpts[l - 1 - _i2]
 23359                };
 23360                p1 = {
 23361                  x: rs.allpts[l - 4 - _i2],
 23362                  y: rs.allpts[l - 3 - _i2]
 23363                };
 23364              }
 23365  
 23366              di = dist(p0, p1);
 23367              d0 = d;
 23368              d += di;
 23369  
 23370              if (d >= offset) {
 23371                break;
 23372              }
 23373            }
 23374  
 23375            var pD = offset - d0;
 23376  
 23377            var _t = pD / di;
 23378  
 23379            _t = bound(0, _t, 1);
 23380            p = lineAt(p0, p1, _t);
 23381            angle = lineAngle(p0, p1);
 23382            break;
 23383          }
 23384      }
 23385  
 23386      setRs('labelX', prefix, p.x);
 23387      setRs('labelY', prefix, p.y);
 23388      setRs('labelAutoAngle', prefix, angle);
 23389    };
 23390  
 23391    calculateEndProjection('source');
 23392    calculateEndProjection('target');
 23393    this.applyLabelDimensions(edge);
 23394  };
 23395  
 23396  BRp$6.applyLabelDimensions = function (ele) {
 23397    this.applyPrefixedLabelDimensions(ele);
 23398  
 23399    if (ele.isEdge()) {
 23400      this.applyPrefixedLabelDimensions(ele, 'source');
 23401      this.applyPrefixedLabelDimensions(ele, 'target');
 23402    }
 23403  };
 23404  
 23405  BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
 23406    var _p = ele._private;
 23407    var text = this.getLabelText(ele, prefix);
 23408    var labelDims = this.calculateLabelDimensions(ele, text);
 23409    var lineHeight = ele.pstyle('line-height').pfValue;
 23410    var textWrap = ele.pstyle('text-wrap').strValue;
 23411    var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
 23412    var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
 23413    var normPerLineHeight = labelDims.height / numLines;
 23414    var labelLineHeight = normPerLineHeight * lineHeight;
 23415    var width = labelDims.width;
 23416    var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
 23417    setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
 23418    setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
 23419    setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
 23420    setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
 23421    setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
 23422  };
 23423  
 23424  BRp$6.getLabelText = function (ele, prefix) {
 23425    var _p = ele._private;
 23426    var pfd = prefix ? prefix + '-' : '';
 23427    var text = ele.pstyle(pfd + 'label').strValue;
 23428    var textTransform = ele.pstyle('text-transform').value;
 23429  
 23430    var rscratch = function rscratch(propName, value) {
 23431      if (value) {
 23432        setPrefixedProperty(_p.rscratch, propName, prefix, value);
 23433        return value;
 23434      } else {
 23435        return getPrefixedProperty(_p.rscratch, propName, prefix);
 23436      }
 23437    }; // for empty text, skip all processing
 23438  
 23439  
 23440    if (!text) {
 23441      return '';
 23442    }
 23443  
 23444    if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
 23445      text = text.toUpperCase();
 23446    } else if (textTransform == 'lowercase') {
 23447      text = text.toLowerCase();
 23448    }
 23449  
 23450    var wrapStyle = ele.pstyle('text-wrap').value;
 23451  
 23452    if (wrapStyle === 'wrap') {
 23453      var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
 23454  
 23455      if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
 23456        return rscratch('labelWrapCachedText');
 23457      }
 23458  
 23459      var zwsp = "\u200B";
 23460      var lines = text.split('\n');
 23461      var maxW = ele.pstyle('text-max-width').pfValue;
 23462      var overflow = ele.pstyle('text-overflow-wrap').value;
 23463      var overflowAny = overflow === 'anywhere';
 23464      var wrappedLines = [];
 23465      var wordsRegex = /[\s\u200b]+/;
 23466      var wordSeparator = overflowAny ? '' : ' ';
 23467  
 23468      for (var l = 0; l < lines.length; l++) {
 23469        var line = lines[l];
 23470        var lineDims = this.calculateLabelDimensions(ele, line);
 23471        var lineW = lineDims.width;
 23472  
 23473        if (overflowAny) {
 23474          var processedLine = line.split('').join(zwsp);
 23475          line = processedLine;
 23476        }
 23477  
 23478        if (lineW > maxW) {
 23479          // line is too long
 23480          var words = line.split(wordsRegex);
 23481          var subline = '';
 23482  
 23483          for (var w = 0; w < words.length; w++) {
 23484            var word = words[w];
 23485            var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
 23486            var testDims = this.calculateLabelDimensions(ele, testLine);
 23487            var testW = testDims.width;
 23488  
 23489            if (testW <= maxW) {
 23490              // word fits on current line
 23491              subline += word + wordSeparator;
 23492            } else {
 23493              // word starts new line
 23494              if (subline) {
 23495                wrappedLines.push(subline);
 23496              }
 23497  
 23498              subline = word + wordSeparator;
 23499            }
 23500          } // if there's remaining text, put it in a wrapped line
 23501  
 23502  
 23503          if (!subline.match(/^[\s\u200b]+$/)) {
 23504            wrappedLines.push(subline);
 23505          }
 23506        } else {
 23507          // line is already short enough
 23508          wrappedLines.push(line);
 23509        }
 23510      } // for
 23511  
 23512  
 23513      rscratch('labelWrapCachedLines', wrappedLines);
 23514      text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
 23515      rscratch('labelWrapKey', labelKey);
 23516    } else if (wrapStyle === 'ellipsis') {
 23517      var _maxW = ele.pstyle('text-max-width').pfValue;
 23518      var ellipsized = '';
 23519      var ellipsis = "\u2026";
 23520      var incLastCh = false;
 23521  
 23522      for (var i = 0; i < text.length; i++) {
 23523        var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
 23524  
 23525        if (widthWithNextCh > _maxW) {
 23526          break;
 23527        }
 23528  
 23529        ellipsized += text[i];
 23530  
 23531        if (i === text.length - 1) {
 23532          incLastCh = true;
 23533        }
 23534      }
 23535  
 23536      if (!incLastCh) {
 23537        ellipsized += ellipsis;
 23538      }
 23539  
 23540      return ellipsized;
 23541    } // if ellipsize
 23542  
 23543  
 23544    return text;
 23545  };
 23546  
 23547  BRp$6.getLabelJustification = function (ele) {
 23548    var justification = ele.pstyle('text-justification').strValue;
 23549    var textHalign = ele.pstyle('text-halign').strValue;
 23550  
 23551    if (justification === 'auto') {
 23552      if (ele.isNode()) {
 23553        switch (textHalign) {
 23554          case 'left':
 23555            return 'right';
 23556  
 23557          case 'right':
 23558            return 'left';
 23559  
 23560          default:
 23561            return 'center';
 23562        }
 23563      } else {
 23564        return 'center';
 23565      }
 23566    } else {
 23567      return justification;
 23568    }
 23569  };
 23570  
 23571  BRp$6.calculateLabelDimensions = function (ele, text) {
 23572    var r = this;
 23573    var cacheKey = hashString(text, ele._private.labelDimsKey);
 23574    var cache = r.labelDimCache || (r.labelDimCache = []);
 23575    var existingVal = cache[cacheKey];
 23576  
 23577    if (existingVal != null) {
 23578      return existingVal;
 23579    }
 23580  
 23581    var sizeMult = 1; // increase the scale to increase accuracy w.r.t. zoomed text
 23582  
 23583    var fStyle = ele.pstyle('font-style').strValue;
 23584    var size = sizeMult * ele.pstyle('font-size').pfValue + 'px';
 23585    var family = ele.pstyle('font-family').strValue;
 23586    var weight = ele.pstyle('font-weight').strValue;
 23587    var div = this.labelCalcDiv;
 23588  
 23589    if (!div) {
 23590      div = this.labelCalcDiv = document.createElement('div'); // eslint-disable-line no-undef
 23591  
 23592      document.body.appendChild(div); // eslint-disable-line no-undef
 23593    }
 23594  
 23595    var ds = div.style; // from ele style
 23596  
 23597    ds.fontFamily = family;
 23598    ds.fontStyle = fStyle;
 23599    ds.fontSize = size;
 23600    ds.fontWeight = weight; // forced style
 23601  
 23602    ds.position = 'absolute';
 23603    ds.left = '-9999px';
 23604    ds.top = '-9999px';
 23605    ds.zIndex = '-1';
 23606    ds.visibility = 'hidden';
 23607    ds.pointerEvents = 'none';
 23608    ds.padding = '0';
 23609    ds.lineHeight = '1'; // - newlines must be taken into account for text-wrap:wrap
 23610    // - since spaces are not collapsed, each space must be taken into account
 23611  
 23612    ds.whiteSpace = 'pre'; // put label content in div
 23613  
 23614    div.textContent = text;
 23615    return cache[cacheKey] = {
 23616      width: Math.ceil(div.clientWidth / sizeMult),
 23617      height: Math.ceil(div.clientHeight / sizeMult)
 23618    };
 23619  };
 23620  
 23621  BRp$6.calculateLabelAngle = function (ele, prefix) {
 23622    var _p = ele._private;
 23623    var rs = _p.rscratch;
 23624    var isEdge = ele.isEdge();
 23625    var prefixDash = prefix ? prefix + '-' : '';
 23626    var rot = ele.pstyle(prefixDash + 'text-rotation');
 23627    var rotStr = rot.strValue;
 23628  
 23629    if (rotStr === 'none') {
 23630      return 0;
 23631    } else if (isEdge && rotStr === 'autorotate') {
 23632      return rs.labelAutoAngle;
 23633    } else if (rotStr === 'autorotate') {
 23634      return 0;
 23635    } else {
 23636      return rot.pfValue;
 23637    }
 23638  };
 23639  
 23640  BRp$6.calculateLabelAngles = function (ele) {
 23641    var r = this;
 23642    var isEdge = ele.isEdge();
 23643    var _p = ele._private;
 23644    var rs = _p.rscratch;
 23645    rs.labelAngle = r.calculateLabelAngle(ele);
 23646  
 23647    if (isEdge) {
 23648      rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
 23649      rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
 23650    }
 23651  };
 23652  
 23653  var BRp$7 = {};
 23654  var TOO_SMALL_CUT_RECT = 28;
 23655  var warnedCutRect = false;
 23656  
 23657  BRp$7.getNodeShape = function (node) {
 23658    var r = this;
 23659    var shape = node.pstyle('shape').value;
 23660  
 23661    if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
 23662      if (!warnedCutRect) {
 23663        warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
 23664        warnedCutRect = true;
 23665      }
 23666  
 23667      return 'rectangle';
 23668    }
 23669  
 23670    if (node.isParent()) {
 23671      if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'cutrectangle' || shape === 'barrel') {
 23672        return shape;
 23673      } else {
 23674        return 'rectangle';
 23675      }
 23676    }
 23677  
 23678    if (shape === 'polygon') {
 23679      var points = node.pstyle('shape-polygon-points').value;
 23680      return r.nodeShapes.makePolygon(points).name;
 23681    }
 23682  
 23683    return shape;
 23684  };
 23685  
 23686  var BRp$8 = {};
 23687  
 23688  BRp$8.registerCalculationListeners = function () {
 23689    var cy = this.cy;
 23690    var elesToUpdate = cy.collection();
 23691    var r = this;
 23692  
 23693    var enqueue = function enqueue(eles) {
 23694      var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
 23695      elesToUpdate.merge(eles);
 23696  
 23697      if (dirtyStyleCaches) {
 23698        for (var i = 0; i < eles.length; i++) {
 23699          var ele = eles[i];
 23700          var _p = ele._private;
 23701          var rstyle = _p.rstyle;
 23702          rstyle.clean = false;
 23703          rstyle.cleanConnected = false;
 23704        }
 23705      }
 23706    };
 23707  
 23708    r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
 23709      var ele = e.target;
 23710      enqueue(ele);
 23711    }).on('style.* background.*', function onDirtyStyle(e) {
 23712      var ele = e.target;
 23713      enqueue(ele, false);
 23714    });
 23715  
 23716    var updateEleCalcs = function updateEleCalcs(willDraw) {
 23717      if (willDraw) {
 23718        var fns = r.onUpdateEleCalcsFns;
 23719  
 23720        for (var i = 0; i < elesToUpdate.length; i++) {
 23721          var ele = elesToUpdate[i];
 23722          var rstyle = ele._private.rstyle;
 23723  
 23724          if (ele.isNode() && !rstyle.cleanConnected) {
 23725            enqueue(ele.connectedEdges());
 23726            rstyle.cleanConnected = true;
 23727          }
 23728        }
 23729  
 23730        if (fns) {
 23731          for (var i = 0; i < fns.length; i++) {
 23732            var fn = fns[i];
 23733            fn(willDraw, elesToUpdate);
 23734          }
 23735        }
 23736  
 23737        r.recalculateRenderedStyle(elesToUpdate);
 23738        elesToUpdate = cy.collection();
 23739      }
 23740    };
 23741  
 23742    r.flushRenderedStyleQueue = function () {
 23743      updateEleCalcs(true);
 23744    };
 23745  
 23746    r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
 23747  };
 23748  
 23749  BRp$8.onUpdateEleCalcs = function (fn) {
 23750    var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
 23751    fns.push(fn);
 23752  };
 23753  
 23754  BRp$8.recalculateRenderedStyle = function (eles, useCache) {
 23755    var isCleanConnected = function isCleanConnected(ele) {
 23756      return ele._private.rstyle.cleanConnected;
 23757    };
 23758  
 23759    var edges = [];
 23760    var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
 23761  
 23762    if (this.destroyed) {
 23763      return;
 23764    } // use cache by default for perf
 23765  
 23766  
 23767    if (useCache === undefined) {
 23768      useCache = true;
 23769    }
 23770  
 23771    for (var i = 0; i < eles.length; i++) {
 23772      var ele = eles[i];
 23773      var _p = ele._private;
 23774      var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
 23775      // (and a request for recalc may come in between frames)
 23776  
 23777      if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
 23778        rstyle.clean = false;
 23779      } // only update if dirty and in graph
 23780  
 23781  
 23782      if (useCache && rstyle.clean || ele.removed()) {
 23783        continue;
 23784      } // only update if not display: none
 23785  
 23786  
 23787      if (ele.pstyle('display').value === 'none') {
 23788        continue;
 23789      }
 23790  
 23791      if (_p.group === 'nodes') {
 23792        nodes.push(ele);
 23793      } else {
 23794        // edges
 23795        edges.push(ele);
 23796      }
 23797  
 23798      rstyle.clean = true;
 23799    } // update node data from projections
 23800  
 23801  
 23802    for (var i = 0; i < nodes.length; i++) {
 23803      var ele = nodes[i];
 23804      var _p = ele._private;
 23805      var rstyle = _p.rstyle;
 23806      var pos = ele.position();
 23807      this.recalculateNodeLabelProjection(ele);
 23808      rstyle.nodeX = pos.x;
 23809      rstyle.nodeY = pos.y;
 23810      rstyle.nodeW = ele.pstyle('width').pfValue;
 23811      rstyle.nodeH = ele.pstyle('height').pfValue;
 23812    }
 23813  
 23814    this.recalculateEdgeProjections(edges); // update edge data from projections
 23815  
 23816    for (var i = 0; i < edges.length; i++) {
 23817      var ele = edges[i];
 23818      var _p = ele._private;
 23819      var rstyle = _p.rstyle;
 23820      var rs = _p.rscratch; // update rstyle positions
 23821  
 23822      rstyle.srcX = rs.arrowStartX;
 23823      rstyle.srcY = rs.arrowStartY;
 23824      rstyle.tgtX = rs.arrowEndX;
 23825      rstyle.tgtY = rs.arrowEndY;
 23826      rstyle.midX = rs.midX;
 23827      rstyle.midY = rs.midY;
 23828      rstyle.labelAngle = rs.labelAngle;
 23829      rstyle.sourceLabelAngle = rs.sourceLabelAngle;
 23830      rstyle.targetLabelAngle = rs.targetLabelAngle;
 23831    }
 23832  };
 23833  
 23834  var BRp$9 = {};
 23835  
 23836  BRp$9.updateCachedGrabbedEles = function () {
 23837    var eles = this.cachedZSortedEles;
 23838  
 23839    if (!eles) {
 23840      // just let this be recalculated on the next z sort tick
 23841      return;
 23842    }
 23843  
 23844    eles.drag = [];
 23845    eles.nondrag = [];
 23846    var grabTargets = [];
 23847  
 23848    for (var i = 0; i < eles.length; i++) {
 23849      var ele = eles[i];
 23850      var rs = ele._private.rscratch;
 23851  
 23852      if (ele.grabbed() && !ele.isParent()) {
 23853        grabTargets.push(ele);
 23854      } else if (rs.inDragLayer) {
 23855        eles.drag.push(ele);
 23856      } else {
 23857        eles.nondrag.push(ele);
 23858      }
 23859    } // put the grab target nodes last so it's on top of its neighbourhood
 23860  
 23861  
 23862    for (var i = 0; i < grabTargets.length; i++) {
 23863      var ele = grabTargets[i];
 23864      eles.drag.push(ele);
 23865    }
 23866  };
 23867  
 23868  BRp$9.invalidateCachedZSortedEles = function () {
 23869    this.cachedZSortedEles = null;
 23870  };
 23871  
 23872  BRp$9.getCachedZSortedEles = function (forceRecalc) {
 23873    if (forceRecalc || !this.cachedZSortedEles) {
 23874      var eles = this.cy.mutableElements().toArray();
 23875      eles.sort(zIndexSort);
 23876      eles.interactive = eles.filter(function (ele) {
 23877        return ele.interactive();
 23878      });
 23879      this.cachedZSortedEles = eles;
 23880      this.updateCachedGrabbedEles();
 23881    } else {
 23882      eles = this.cachedZSortedEles;
 23883    }
 23884  
 23885    return eles;
 23886  };
 23887  
 23888  var BRp$a = {};
 23889  [BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
 23890    extend(BRp$a, props);
 23891  });
 23892  
 23893  var BRp$b = {};
 23894  
 23895  BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
 23896    var r = this;
 23897    var imageCache = r.imageCache = r.imageCache || {};
 23898    var cache = imageCache[url];
 23899  
 23900    if (cache) {
 23901      if (!cache.image.complete) {
 23902        cache.image.addEventListener('load', onLoad);
 23903      }
 23904  
 23905      return cache.image;
 23906    } else {
 23907      cache = imageCache[url] = imageCache[url] || {};
 23908      var image = cache.image = new Image(); // eslint-disable-line no-undef
 23909  
 23910      image.addEventListener('load', onLoad);
 23911      image.addEventListener('error', function () {
 23912        image.error = true;
 23913      }); // #1582 safari doesn't load data uris with crossOrigin properly
 23914      // https://bugs.webkit.org/show_bug.cgi?id=123978
 23915  
 23916      var dataUriPrefix = 'data:';
 23917      var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
 23918  
 23919      if (!isDataUri) {
 23920        image.crossOrigin = crossOrigin; // prevent tainted canvas
 23921      }
 23922  
 23923      image.src = url;
 23924      return image;
 23925    }
 23926  };
 23927  
 23928  var BRp$c = {};
 23929  /* global document, window, ResizeObserver, MutationObserver */
 23930  
 23931  BRp$c.registerBinding = function (target, event, handler, useCapture) {
 23932    // eslint-disable-line no-unused-vars
 23933    var args = Array.prototype.slice.apply(arguments, [1]); // copy
 23934  
 23935    var b = this.binder(target);
 23936    return b.on.apply(b, args);
 23937  };
 23938  
 23939  BRp$c.binder = function (tgt) {
 23940    var r = this;
 23941    var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
 23942  
 23943    if (r.supportsPassiveEvents == null) {
 23944      // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
 23945      var supportsPassive = false;
 23946  
 23947      try {
 23948        var opts = Object.defineProperty({}, 'passive', {
 23949          get: function get() {
 23950            supportsPassive = true;
 23951            return true;
 23952          }
 23953        });
 23954        window.addEventListener('test', null, opts);
 23955      } catch (err) {// not supported
 23956      }
 23957  
 23958      r.supportsPassiveEvents = supportsPassive;
 23959    }
 23960  
 23961    var on = function on(event, handler, useCapture) {
 23962      var args = Array.prototype.slice.call(arguments);
 23963  
 23964      if (tgtIsDom && r.supportsPassiveEvents) {
 23965        // replace useCapture w/ opts obj
 23966        args[2] = {
 23967          capture: useCapture != null ? useCapture : false,
 23968          passive: false,
 23969          once: false
 23970        };
 23971      }
 23972  
 23973      r.bindings.push({
 23974        target: tgt,
 23975        args: args
 23976      });
 23977      (tgt.addEventListener || tgt.on).apply(tgt, args);
 23978      return this;
 23979    };
 23980  
 23981    return {
 23982      on: on,
 23983      addEventListener: on,
 23984      addListener: on,
 23985      bind: on
 23986    };
 23987  };
 23988  
 23989  BRp$c.nodeIsDraggable = function (node) {
 23990    return node && node.isNode() && !node.locked() && node.grabbable();
 23991  };
 23992  
 23993  BRp$c.nodeIsGrabbable = function (node) {
 23994    return this.nodeIsDraggable(node) && node.interactive();
 23995  };
 23996  
 23997  BRp$c.load = function () {
 23998    var r = this;
 23999  
 24000    var isSelected = function isSelected(ele) {
 24001      return ele.selected();
 24002    };
 24003  
 24004    var triggerEvents = function triggerEvents(target, names, e, position) {
 24005      if (target == null) {
 24006        target = r.cy;
 24007      }
 24008  
 24009      for (var i = 0; i < names.length; i++) {
 24010        var name = names[i];
 24011        target.emit({
 24012          originalEvent: e,
 24013          type: name,
 24014          position: position
 24015        });
 24016      }
 24017    };
 24018  
 24019    var isMultSelKeyDown = function isMultSelKeyDown(e) {
 24020      return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
 24021    };
 24022  
 24023    var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
 24024      var allowPassthrough = true;
 24025  
 24026      if (r.cy.hasCompoundNodes() && down && down.pannable()) {
 24027        // a grabbable compound node below the ele => no passthrough panning
 24028        for (var i = 0; downs && i < downs.length; i++) {
 24029          var down = downs[i];
 24030  
 24031          if (down.isNode() && down.isParent()) {
 24032            allowPassthrough = false;
 24033            break;
 24034          }
 24035        }
 24036      } else {
 24037        allowPassthrough = true;
 24038      }
 24039  
 24040      return allowPassthrough;
 24041    };
 24042  
 24043    var setGrabbed = function setGrabbed(ele) {
 24044      ele[0]._private.grabbed = true;
 24045    };
 24046  
 24047    var setFreed = function setFreed(ele) {
 24048      ele[0]._private.grabbed = false;
 24049    };
 24050  
 24051    var setInDragLayer = function setInDragLayer(ele) {
 24052      ele[0]._private.rscratch.inDragLayer = true;
 24053    };
 24054  
 24055    var setOutDragLayer = function setOutDragLayer(ele) {
 24056      ele[0]._private.rscratch.inDragLayer = false;
 24057    };
 24058  
 24059    var setGrabTarget = function setGrabTarget(ele) {
 24060      ele[0]._private.rscratch.isGrabTarget = true;
 24061    };
 24062  
 24063    var removeGrabTarget = function removeGrabTarget(ele) {
 24064      ele[0]._private.rscratch.isGrabTarget = false;
 24065    };
 24066  
 24067    var addToDragList = function addToDragList(ele, opts) {
 24068      var list = opts.addToList;
 24069      var listHasEle = list.has(ele);
 24070  
 24071      if (!listHasEle) {
 24072        list.merge(ele);
 24073        setGrabbed(ele);
 24074      }
 24075    }; // helper function to determine which child nodes and inner edges
 24076    // of a compound node to be dragged as well as the grabbed and selected nodes
 24077  
 24078  
 24079    var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
 24080      if (!node.cy().hasCompoundNodes()) {
 24081        return;
 24082      }
 24083  
 24084      if (opts.inDragLayer == null && opts.addToList == null) {
 24085        return;
 24086      } // nothing to do
 24087  
 24088  
 24089      var innerNodes = node.descendants();
 24090  
 24091      if (opts.inDragLayer) {
 24092        innerNodes.forEach(setInDragLayer);
 24093        innerNodes.connectedEdges().forEach(setInDragLayer);
 24094      }
 24095  
 24096      if (opts.addToList) {
 24097        opts.addToList.unmerge(innerNodes);
 24098      }
 24099    }; // adds the given nodes and its neighbourhood to the drag layer
 24100  
 24101  
 24102    var addNodesToDrag = function addNodesToDrag(nodes, opts) {
 24103      opts = opts || {};
 24104      var hasCompoundNodes = nodes.cy().hasCompoundNodes();
 24105  
 24106      if (opts.inDragLayer) {
 24107        nodes.forEach(setInDragLayer);
 24108        nodes.neighborhood().stdFilter(function (ele) {
 24109          return !hasCompoundNodes || ele.isEdge();
 24110        }).forEach(setInDragLayer);
 24111      }
 24112  
 24113      if (opts.addToList) {
 24114        nodes.forEach(function (ele) {
 24115          addToDragList(ele, opts);
 24116        });
 24117      }
 24118  
 24119      addDescendantsToDrag(nodes, opts); // always add to drag
 24120      // also add nodes and edges related to the topmost ancestor
 24121  
 24122      updateAncestorsInDragLayer(nodes, {
 24123        inDragLayer: opts.inDragLayer
 24124      });
 24125      r.updateCachedGrabbedEles();
 24126    };
 24127  
 24128    var addNodeToDrag = addNodesToDrag;
 24129  
 24130    var freeDraggedElements = function freeDraggedElements(grabbedEles) {
 24131      if (!grabbedEles) {
 24132        return;
 24133      } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
 24134  
 24135  
 24136      r.getCachedZSortedEles().forEach(function (ele) {
 24137        setFreed(ele);
 24138        setOutDragLayer(ele);
 24139        removeGrabTarget(ele);
 24140      });
 24141      r.updateCachedGrabbedEles();
 24142    }; // helper function to determine which ancestor nodes and edges should go
 24143    // to the drag layer (or should be removed from drag layer).
 24144  
 24145  
 24146    var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
 24147      if (opts.inDragLayer == null && opts.addToList == null) {
 24148        return;
 24149      } // nothing to do
 24150  
 24151  
 24152      if (!node.cy().hasCompoundNodes()) {
 24153        return;
 24154      } // find top-level parent
 24155  
 24156  
 24157      var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
 24158  
 24159      if (parent.same(node)) {
 24160        return;
 24161      }
 24162  
 24163      var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
 24164      var edges = nodes.connectedEdges();
 24165  
 24166      if (opts.inDragLayer) {
 24167        edges.forEach(setInDragLayer);
 24168        nodes.forEach(setInDragLayer);
 24169      }
 24170  
 24171      if (opts.addToList) {
 24172        nodes.forEach(function (ele) {
 24173          addToDragList(ele, opts);
 24174        });
 24175      }
 24176    };
 24177  
 24178    var blurActiveDomElement = function blurActiveDomElement() {
 24179      if (document.activeElement != null && document.activeElement.blur != null) {
 24180        document.activeElement.blur();
 24181      }
 24182    };
 24183  
 24184    var haveMutationsApi = typeof MutationObserver !== 'undefined';
 24185    var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
 24186  
 24187    if (haveMutationsApi) {
 24188      r.removeObserver = new MutationObserver(function (mutns) {
 24189        // eslint-disable-line no-undef
 24190        for (var i = 0; i < mutns.length; i++) {
 24191          var mutn = mutns[i];
 24192          var rNodes = mutn.removedNodes;
 24193  
 24194          if (rNodes) {
 24195            for (var j = 0; j < rNodes.length; j++) {
 24196              var rNode = rNodes[j];
 24197  
 24198              if (rNode === r.container) {
 24199                r.destroy();
 24200                break;
 24201              }
 24202            }
 24203          }
 24204        }
 24205      });
 24206  
 24207      if (r.container.parentNode) {
 24208        r.removeObserver.observe(r.container.parentNode, {
 24209          childList: true
 24210        });
 24211      }
 24212    } else {
 24213      r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
 24214        // eslint-disable-line no-unused-vars
 24215        r.destroy();
 24216      });
 24217    }
 24218  
 24219    var onResize = util(function () {
 24220      r.cy.resize();
 24221    }, 100);
 24222  
 24223    if (haveMutationsApi) {
 24224      r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
 24225  
 24226      r.styleObserver.observe(r.container, {
 24227        attributes: true
 24228      });
 24229    } // auto resize
 24230  
 24231  
 24232    r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
 24233  
 24234    if (haveResizeObserverApi) {
 24235      r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
 24236  
 24237      r.resizeObserver.observe(r.container);
 24238    }
 24239  
 24240    var forEachUp = function forEachUp(domEle, fn) {
 24241      while (domEle != null) {
 24242        fn(domEle);
 24243        domEle = domEle.parentNode;
 24244      }
 24245    };
 24246  
 24247    var invalidateCoords = function invalidateCoords() {
 24248      r.invalidateContainerClientCoordsCache();
 24249    };
 24250  
 24251    forEachUp(r.container, function (domEle) {
 24252      r.registerBinding(domEle, 'transitionend', invalidateCoords);
 24253      r.registerBinding(domEle, 'animationend', invalidateCoords);
 24254      r.registerBinding(domEle, 'scroll', invalidateCoords);
 24255    }); // stop right click menu from appearing on cy
 24256  
 24257    r.registerBinding(r.container, 'contextmenu', function (e) {
 24258      e.preventDefault();
 24259    });
 24260  
 24261    var inBoxSelection = function inBoxSelection() {
 24262      return r.selection[4] !== 0;
 24263    };
 24264  
 24265    var eventInContainer = function eventInContainer(e) {
 24266      // save cycles if mouse events aren't to be captured
 24267      var containerPageCoords = r.findContainerClientCoords();
 24268      var x = containerPageCoords[0];
 24269      var y = containerPageCoords[1];
 24270      var width = containerPageCoords[2];
 24271      var height = containerPageCoords[3];
 24272      var positions = e.touches ? e.touches : [e];
 24273      var atLeastOnePosInside = false;
 24274  
 24275      for (var i = 0; i < positions.length; i++) {
 24276        var p = positions[i];
 24277  
 24278        if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
 24279          atLeastOnePosInside = true;
 24280          break;
 24281        }
 24282      }
 24283  
 24284      if (!atLeastOnePosInside) {
 24285        return false;
 24286      }
 24287  
 24288      var container = r.container;
 24289      var target = e.target;
 24290      var tParent = target.parentNode;
 24291      var containerIsTarget = false;
 24292  
 24293      while (tParent) {
 24294        if (tParent === container) {
 24295          containerIsTarget = true;
 24296          break;
 24297        }
 24298  
 24299        tParent = tParent.parentNode;
 24300      }
 24301  
 24302      if (!containerIsTarget) {
 24303        return false;
 24304      } // if target is outisde cy container, then this event is not for us
 24305  
 24306  
 24307      return true;
 24308    }; // Primary key
 24309  
 24310  
 24311    r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
 24312      if (!eventInContainer(e)) {
 24313        return;
 24314      }
 24315  
 24316      e.preventDefault();
 24317      blurActiveDomElement();
 24318      r.hoverData.capture = true;
 24319      r.hoverData.which = e.which;
 24320      var cy = r.cy;
 24321      var gpos = [e.clientX, e.clientY];
 24322      var pos = r.projectIntoViewport(gpos[0], gpos[1]);
 24323      var select = r.selection;
 24324      var nears = r.findNearestElements(pos[0], pos[1], true, false);
 24325      var near = nears[0];
 24326      var draggedElements = r.dragData.possibleDragElements;
 24327      r.hoverData.mdownPos = pos;
 24328      r.hoverData.mdownGPos = gpos;
 24329  
 24330      var checkForTaphold = function checkForTaphold() {
 24331        r.hoverData.tapholdCancelled = false;
 24332        clearTimeout(r.hoverData.tapholdTimeout);
 24333        r.hoverData.tapholdTimeout = setTimeout(function () {
 24334          if (r.hoverData.tapholdCancelled) {
 24335            return;
 24336          } else {
 24337            var ele = r.hoverData.down;
 24338  
 24339            if (ele) {
 24340              ele.emit({
 24341                originalEvent: e,
 24342                type: 'taphold',
 24343                position: {
 24344                  x: pos[0],
 24345                  y: pos[1]
 24346                }
 24347              });
 24348            } else {
 24349              cy.emit({
 24350                originalEvent: e,
 24351                type: 'taphold',
 24352                position: {
 24353                  x: pos[0],
 24354                  y: pos[1]
 24355                }
 24356              });
 24357            }
 24358          }
 24359        }, r.tapholdDuration);
 24360      }; // Right click button
 24361  
 24362  
 24363      if (e.which == 3) {
 24364        r.hoverData.cxtStarted = true;
 24365        var cxtEvt = {
 24366          originalEvent: e,
 24367          type: 'cxttapstart',
 24368          position: {
 24369            x: pos[0],
 24370            y: pos[1]
 24371          }
 24372        };
 24373  
 24374        if (near) {
 24375          near.activate();
 24376          near.emit(cxtEvt);
 24377          r.hoverData.down = near;
 24378        } else {
 24379          cy.emit(cxtEvt);
 24380        }
 24381  
 24382        r.hoverData.downTime = new Date().getTime();
 24383        r.hoverData.cxtDragged = false; // Primary button
 24384      } else if (e.which == 1) {
 24385        if (near) {
 24386          near.activate();
 24387        } // Element dragging
 24388  
 24389  
 24390        {
 24391          // If something is under the cursor and it is draggable, prepare to grab it
 24392          if (near != null) {
 24393            if (r.nodeIsGrabbable(near)) {
 24394              var makeEvent = function makeEvent(type) {
 24395                return {
 24396                  originalEvent: e,
 24397                  type: type,
 24398                  position: {
 24399                    x: pos[0],
 24400                    y: pos[1]
 24401                  }
 24402                };
 24403              };
 24404  
 24405              var triggerGrab = function triggerGrab(ele) {
 24406                ele.emit(makeEvent('grab'));
 24407              };
 24408  
 24409              setGrabTarget(near);
 24410  
 24411              if (!near.selected()) {
 24412                draggedElements = r.dragData.possibleDragElements = cy.collection();
 24413                addNodeToDrag(near, {
 24414                  addToList: draggedElements
 24415                });
 24416                near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
 24417              } else {
 24418                draggedElements = r.dragData.possibleDragElements = cy.collection();
 24419                var selectedNodes = cy.$(function (ele) {
 24420                  return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
 24421                });
 24422                addNodesToDrag(selectedNodes, {
 24423                  addToList: draggedElements
 24424                });
 24425                near.emit(makeEvent('grabon'));
 24426                selectedNodes.forEach(triggerGrab);
 24427              }
 24428  
 24429              r.redrawHint('eles', true);
 24430              r.redrawHint('drag', true);
 24431            }
 24432          }
 24433  
 24434          r.hoverData.down = near;
 24435          r.hoverData.downs = nears;
 24436          r.hoverData.downTime = new Date().getTime();
 24437        }
 24438        triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
 24439          x: pos[0],
 24440          y: pos[1]
 24441        });
 24442  
 24443        if (near == null) {
 24444          select[4] = 1;
 24445          r.data.bgActivePosistion = {
 24446            x: pos[0],
 24447            y: pos[1]
 24448          };
 24449          r.redrawHint('select', true);
 24450          r.redraw();
 24451        } else if (near.pannable()) {
 24452          select[4] = 1; // for future pan
 24453        }
 24454  
 24455        checkForTaphold();
 24456      } // Initialize selection box coordinates
 24457  
 24458  
 24459      select[0] = select[2] = pos[0];
 24460      select[1] = select[3] = pos[1];
 24461    }, false);
 24462    r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
 24463      // eslint-disable-line no-undef
 24464      var capture = r.hoverData.capture;
 24465  
 24466      if (!capture && !eventInContainer(e)) {
 24467        return;
 24468      }
 24469  
 24470      var preventDefault = false;
 24471      var cy = r.cy;
 24472      var zoom = cy.zoom();
 24473      var gpos = [e.clientX, e.clientY];
 24474      var pos = r.projectIntoViewport(gpos[0], gpos[1]);
 24475      var mdownPos = r.hoverData.mdownPos;
 24476      var mdownGPos = r.hoverData.mdownGPos;
 24477      var select = r.selection;
 24478      var near = null;
 24479  
 24480      if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
 24481        near = r.findNearestElement(pos[0], pos[1], true, false);
 24482      }
 24483  
 24484      var last = r.hoverData.last;
 24485      var down = r.hoverData.down;
 24486      var disp = [pos[0] - select[2], pos[1] - select[3]];
 24487      var draggedElements = r.dragData.possibleDragElements;
 24488      var isOverThresholdDrag;
 24489  
 24490      if (mdownGPos) {
 24491        var dx = gpos[0] - mdownGPos[0];
 24492        var dx2 = dx * dx;
 24493        var dy = gpos[1] - mdownGPos[1];
 24494        var dy2 = dy * dy;
 24495        var dist2 = dx2 + dy2;
 24496        r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
 24497      }
 24498  
 24499      var multSelKeyDown = isMultSelKeyDown(e);
 24500  
 24501      if (isOverThresholdDrag) {
 24502        r.hoverData.tapholdCancelled = true;
 24503      }
 24504  
 24505      var updateDragDelta = function updateDragDelta() {
 24506        var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
 24507  
 24508        if (dragDelta.length === 0) {
 24509          dragDelta.push(disp[0]);
 24510          dragDelta.push(disp[1]);
 24511        } else {
 24512          dragDelta[0] += disp[0];
 24513          dragDelta[1] += disp[1];
 24514        }
 24515      };
 24516  
 24517      preventDefault = true;
 24518      triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
 24519        x: pos[0],
 24520        y: pos[1]
 24521      });
 24522  
 24523      var goIntoBoxMode = function goIntoBoxMode() {
 24524        r.data.bgActivePosistion = undefined;
 24525  
 24526        if (!r.hoverData.selecting) {
 24527          cy.emit({
 24528            originalEvent: e,
 24529            type: 'boxstart',
 24530            position: {
 24531              x: pos[0],
 24532              y: pos[1]
 24533            }
 24534          });
 24535        }
 24536  
 24537        select[4] = 1;
 24538        r.hoverData.selecting = true;
 24539        r.redrawHint('select', true);
 24540        r.redraw();
 24541      }; // trigger context drag if rmouse down
 24542  
 24543  
 24544      if (r.hoverData.which === 3) {
 24545        // but only if over threshold
 24546        if (isOverThresholdDrag) {
 24547          var cxtEvt = {
 24548            originalEvent: e,
 24549            type: 'cxtdrag',
 24550            position: {
 24551              x: pos[0],
 24552              y: pos[1]
 24553            }
 24554          };
 24555  
 24556          if (down) {
 24557            down.emit(cxtEvt);
 24558          } else {
 24559            cy.emit(cxtEvt);
 24560          }
 24561  
 24562          r.hoverData.cxtDragged = true;
 24563  
 24564          if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
 24565            if (r.hoverData.cxtOver) {
 24566              r.hoverData.cxtOver.emit({
 24567                originalEvent: e,
 24568                type: 'cxtdragout',
 24569                position: {
 24570                  x: pos[0],
 24571                  y: pos[1]
 24572                }
 24573              });
 24574            }
 24575  
 24576            r.hoverData.cxtOver = near;
 24577  
 24578            if (near) {
 24579              near.emit({
 24580                originalEvent: e,
 24581                type: 'cxtdragover',
 24582                position: {
 24583                  x: pos[0],
 24584                  y: pos[1]
 24585                }
 24586              });
 24587            }
 24588          }
 24589        } // Check if we are drag panning the entire graph
 24590  
 24591      } else if (r.hoverData.dragging) {
 24592        preventDefault = true;
 24593  
 24594        if (cy.panningEnabled() && cy.userPanningEnabled()) {
 24595          var deltaP;
 24596  
 24597          if (r.hoverData.justStartedPan) {
 24598            var mdPos = r.hoverData.mdownPos;
 24599            deltaP = {
 24600              x: (pos[0] - mdPos[0]) * zoom,
 24601              y: (pos[1] - mdPos[1]) * zoom
 24602            };
 24603            r.hoverData.justStartedPan = false;
 24604          } else {
 24605            deltaP = {
 24606              x: disp[0] * zoom,
 24607              y: disp[1] * zoom
 24608            };
 24609          }
 24610  
 24611          cy.panBy(deltaP);
 24612          r.hoverData.dragged = true;
 24613        } // Needs reproject due to pan changing viewport
 24614  
 24615  
 24616        pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
 24617      } else if (select[4] == 1 && (down == null || down.pannable())) {
 24618        if (isOverThresholdDrag) {
 24619          if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
 24620            goIntoBoxMode();
 24621          } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
 24622            var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
 24623  
 24624            if (allowPassthrough) {
 24625              r.hoverData.dragging = true;
 24626              r.hoverData.justStartedPan = true;
 24627              select[4] = 0;
 24628              r.data.bgActivePosistion = array2point(mdownPos);
 24629              r.redrawHint('select', true);
 24630              r.redraw();
 24631            }
 24632          }
 24633  
 24634          if (down && down.pannable() && down.active()) {
 24635            down.unactivate();
 24636          }
 24637        }
 24638      } else {
 24639        if (down && down.pannable() && down.active()) {
 24640          down.unactivate();
 24641        }
 24642  
 24643        if ((!down || !down.grabbed()) && near != last) {
 24644          if (last) {
 24645            triggerEvents(last, ['mouseout', 'tapdragout'], e, {
 24646              x: pos[0],
 24647              y: pos[1]
 24648            });
 24649          }
 24650  
 24651          if (near) {
 24652            triggerEvents(near, ['mouseover', 'tapdragover'], e, {
 24653              x: pos[0],
 24654              y: pos[1]
 24655            });
 24656          }
 24657  
 24658          r.hoverData.last = near;
 24659        }
 24660  
 24661        if (down) {
 24662          if (isOverThresholdDrag) {
 24663            // then we can take action
 24664            if (cy.boxSelectionEnabled() && multSelKeyDown) {
 24665              // then selection overrides
 24666              if (down && down.grabbed()) {
 24667                freeDraggedElements(draggedElements);
 24668                down.emit('freeon');
 24669                draggedElements.emit('free');
 24670  
 24671                if (r.dragData.didDrag) {
 24672                  down.emit('dragfreeon');
 24673                  draggedElements.emit('dragfree');
 24674                }
 24675              }
 24676  
 24677              goIntoBoxMode();
 24678            } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
 24679              // drag node
 24680              var justStartedDrag = !r.dragData.didDrag;
 24681  
 24682              if (justStartedDrag) {
 24683                r.redrawHint('eles', true);
 24684              }
 24685  
 24686              r.dragData.didDrag = true; // indicate that we actually did drag the node
 24687  
 24688              var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
 24689  
 24690              if (!r.hoverData.draggingEles) {
 24691                addNodesToDrag(draggedElements, {
 24692                  inDragLayer: true
 24693                });
 24694              }
 24695  
 24696              var totalShift = {
 24697                x: 0,
 24698                y: 0
 24699              };
 24700  
 24701              if (number(disp[0]) && number(disp[1])) {
 24702                totalShift.x += disp[0];
 24703                totalShift.y += disp[1];
 24704  
 24705                if (justStartedDrag) {
 24706                  var dragDelta = r.hoverData.dragDelta;
 24707  
 24708                  if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
 24709                    totalShift.x += dragDelta[0];
 24710                    totalShift.y += dragDelta[1];
 24711                  }
 24712                }
 24713              }
 24714  
 24715              for (var i = 0; i < draggedElements.length; i++) {
 24716                var dEle = draggedElements[i];
 24717  
 24718                if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
 24719                  toTrigger.merge(dEle);
 24720                }
 24721              }
 24722  
 24723              r.hoverData.draggingEles = true;
 24724              toTrigger.silentShift(totalShift).emit('position drag');
 24725              r.redrawHint('drag', true);
 24726              r.redraw();
 24727            }
 24728          } else {
 24729            // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
 24730            updateDragDelta();
 24731          }
 24732        } // prevent the dragging from triggering text selection on the page
 24733  
 24734  
 24735        preventDefault = true;
 24736      }
 24737  
 24738      select[2] = pos[0];
 24739      select[3] = pos[1];
 24740  
 24741      if (preventDefault) {
 24742        if (e.stopPropagation) e.stopPropagation();
 24743        if (e.preventDefault) e.preventDefault();
 24744        return false;
 24745      }
 24746    }, false);
 24747    r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
 24748      // eslint-disable-line no-undef
 24749      var capture = r.hoverData.capture;
 24750  
 24751      if (!capture) {
 24752        return;
 24753      }
 24754  
 24755      r.hoverData.capture = false;
 24756      var cy = r.cy;
 24757      var pos = r.projectIntoViewport(e.clientX, e.clientY);
 24758      var select = r.selection;
 24759      var near = r.findNearestElement(pos[0], pos[1], true, false);
 24760      var draggedElements = r.dragData.possibleDragElements;
 24761      var down = r.hoverData.down;
 24762      var multSelKeyDown = isMultSelKeyDown(e);
 24763  
 24764      if (r.data.bgActivePosistion) {
 24765        r.redrawHint('select', true);
 24766        r.redraw();
 24767      }
 24768  
 24769      r.hoverData.tapholdCancelled = true;
 24770      r.data.bgActivePosistion = undefined; // not active bg now
 24771  
 24772      if (down) {
 24773        down.unactivate();
 24774      }
 24775  
 24776      if (r.hoverData.which === 3) {
 24777        var cxtEvt = {
 24778          originalEvent: e,
 24779          type: 'cxttapend',
 24780          position: {
 24781            x: pos[0],
 24782            y: pos[1]
 24783          }
 24784        };
 24785  
 24786        if (down) {
 24787          down.emit(cxtEvt);
 24788        } else {
 24789          cy.emit(cxtEvt);
 24790        }
 24791  
 24792        if (!r.hoverData.cxtDragged) {
 24793          var cxtTap = {
 24794            originalEvent: e,
 24795            type: 'cxttap',
 24796            position: {
 24797              x: pos[0],
 24798              y: pos[1]
 24799            }
 24800          };
 24801  
 24802          if (down) {
 24803            down.emit(cxtTap);
 24804          } else {
 24805            cy.emit(cxtTap);
 24806          }
 24807        }
 24808  
 24809        r.hoverData.cxtDragged = false;
 24810        r.hoverData.which = null;
 24811      } else if (r.hoverData.which === 1) {
 24812        triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
 24813          x: pos[0],
 24814          y: pos[1]
 24815        });
 24816  
 24817        if (!r.dragData.didDrag // didn't move a node around
 24818        && !r.hoverData.dragged // didn't pan
 24819        && !r.hoverData.selecting // not box selection
 24820        && !r.hoverData.isOverThresholdDrag // didn't move too much
 24821        ) {
 24822            triggerEvents(down, ['click', 'tap', 'vclick'], e, {
 24823              x: pos[0],
 24824              y: pos[1]
 24825            });
 24826          } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
 24827  
 24828  
 24829        if (down == null && // not mousedown on node
 24830        !r.dragData.didDrag // didn't move the node around
 24831        && !r.hoverData.selecting // not box selection
 24832        && !r.hoverData.dragged // didn't pan
 24833        && !isMultSelKeyDown(e)) {
 24834          cy.$(isSelected).unselect(['tapunselect']);
 24835  
 24836          if (draggedElements.length > 0) {
 24837            r.redrawHint('eles', true);
 24838          }
 24839  
 24840          r.dragData.possibleDragElements = draggedElements = cy.collection();
 24841        } // Single selection
 24842  
 24843  
 24844        if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
 24845          if (near != null && near._private.selectable) {
 24846            if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
 24847              if (near.selected()) {
 24848                near.unselect(['tapunselect']);
 24849              } else {
 24850                near.select(['tapselect']);
 24851              }
 24852            } else {
 24853              if (!multSelKeyDown) {
 24854                cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
 24855                near.select(['tapselect']);
 24856              }
 24857            }
 24858  
 24859            r.redrawHint('eles', true);
 24860          }
 24861        }
 24862  
 24863        if (r.hoverData.selecting) {
 24864          var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
 24865          r.redrawHint('select', true);
 24866  
 24867          if (box.length > 0) {
 24868            r.redrawHint('eles', true);
 24869          }
 24870  
 24871          cy.emit({
 24872            type: 'boxend',
 24873            originalEvent: e,
 24874            position: {
 24875              x: pos[0],
 24876              y: pos[1]
 24877            }
 24878          });
 24879  
 24880          var eleWouldBeSelected = function eleWouldBeSelected(ele) {
 24881            return ele.selectable() && !ele.selected();
 24882          };
 24883  
 24884          if (cy.selectionType() === 'additive') {
 24885            box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
 24886          } else {
 24887            if (!multSelKeyDown) {
 24888              cy.$(isSelected).unmerge(box).unselect();
 24889            }
 24890  
 24891            box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
 24892          } // always need redraw in case eles unselectable
 24893  
 24894  
 24895          r.redraw();
 24896        } // Cancel drag pan
 24897  
 24898  
 24899        if (r.hoverData.dragging) {
 24900          r.hoverData.dragging = false;
 24901          r.redrawHint('select', true);
 24902          r.redrawHint('eles', true);
 24903          r.redraw();
 24904        }
 24905  
 24906        if (!select[4]) {
 24907          r.redrawHint('drag', true);
 24908          r.redrawHint('eles', true);
 24909          var downWasGrabbed = down && down.grabbed();
 24910          freeDraggedElements(draggedElements);
 24911  
 24912          if (downWasGrabbed) {
 24913            down.emit('freeon');
 24914            draggedElements.emit('free');
 24915  
 24916            if (r.dragData.didDrag) {
 24917              down.emit('dragfreeon');
 24918              draggedElements.emit('dragfree');
 24919            }
 24920          }
 24921        }
 24922      } // else not right mouse
 24923  
 24924  
 24925      select[4] = 0;
 24926      r.hoverData.down = null;
 24927      r.hoverData.cxtStarted = false;
 24928      r.hoverData.draggingEles = false;
 24929      r.hoverData.selecting = false;
 24930      r.hoverData.isOverThresholdDrag = false;
 24931      r.dragData.didDrag = false;
 24932      r.hoverData.dragged = false;
 24933      r.hoverData.dragDelta = [];
 24934      r.hoverData.mdownPos = null;
 24935      r.hoverData.mdownGPos = null;
 24936    }, false);
 24937  
 24938    var wheelHandler = function wheelHandler(e) {
 24939      if (r.scrollingPage) {
 24940        return;
 24941      } // while scrolling, ignore wheel-to-zoom
 24942  
 24943  
 24944      var cy = r.cy;
 24945      var pos = r.projectIntoViewport(e.clientX, e.clientY);
 24946      var rpos = [pos[0] * cy.zoom() + cy.pan().x, pos[1] * cy.zoom() + cy.pan().y];
 24947  
 24948      if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
 24949        // if pan dragging or cxt dragging, wheel movements make no zoom
 24950        e.preventDefault();
 24951        return;
 24952      }
 24953  
 24954      if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
 24955        e.preventDefault();
 24956        r.data.wheelZooming = true;
 24957        clearTimeout(r.data.wheelTimeout);
 24958        r.data.wheelTimeout = setTimeout(function () {
 24959          r.data.wheelZooming = false;
 24960          r.redrawHint('eles', true);
 24961          r.redraw();
 24962        }, 150);
 24963        var diff;
 24964  
 24965        if (e.deltaY != null) {
 24966          diff = e.deltaY / -250;
 24967        } else if (e.wheelDeltaY != null) {
 24968          diff = e.wheelDeltaY / 1000;
 24969        } else {
 24970          diff = e.wheelDelta / 1000;
 24971        }
 24972  
 24973        diff = diff * r.wheelSensitivity;
 24974        var needsWheelFix = e.deltaMode === 1;
 24975  
 24976        if (needsWheelFix) {
 24977          // fixes slow wheel events on ff/linux and ff/windows
 24978          diff *= 33;
 24979        }
 24980  
 24981        cy.zoom({
 24982          level: cy.zoom() * Math.pow(10, diff),
 24983          renderedPosition: {
 24984            x: rpos[0],
 24985            y: rpos[1]
 24986          }
 24987        });
 24988      }
 24989    }; // Functions to help with whether mouse wheel should trigger zooming
 24990    // --
 24991  
 24992  
 24993    r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
 24994    // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
 24995    // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
 24996    // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
 24997  
 24998    r.registerBinding(window, 'scroll', function scrollHandler(e) {
 24999      // eslint-disable-line no-unused-vars
 25000      r.scrollingPage = true;
 25001      clearTimeout(r.scrollingPageTimeout);
 25002      r.scrollingPageTimeout = setTimeout(function () {
 25003        r.scrollingPage = false;
 25004      }, 250);
 25005    }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
 25006    // Handle mouseout on Cytoscape container
 25007  
 25008    r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
 25009      var pos = r.projectIntoViewport(e.clientX, e.clientY);
 25010      r.cy.emit({
 25011        originalEvent: e,
 25012        type: 'mouseout',
 25013        position: {
 25014          x: pos[0],
 25015          y: pos[1]
 25016        }
 25017      });
 25018    }, false);
 25019    r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
 25020      var pos = r.projectIntoViewport(e.clientX, e.clientY);
 25021      r.cy.emit({
 25022        originalEvent: e,
 25023        type: 'mouseover',
 25024        position: {
 25025          x: pos[0],
 25026          y: pos[1]
 25027        }
 25028      });
 25029    }, false);
 25030    var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
 25031  
 25032    var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
 25033  
 25034    var center1, modelCenter1; // center point on start pinch to zoom
 25035  
 25036    var offsetLeft, offsetTop;
 25037    var containerWidth, containerHeight;
 25038    var twoFingersStartInside;
 25039  
 25040    var distance = function distance(x1, y1, x2, y2) {
 25041      return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
 25042    };
 25043  
 25044    var distanceSq = function distanceSq(x1, y1, x2, y2) {
 25045      return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
 25046    };
 25047  
 25048    var touchstartHandler;
 25049    r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
 25050      if (!eventInContainer(e)) {
 25051        return;
 25052      }
 25053  
 25054      blurActiveDomElement();
 25055      r.touchData.capture = true;
 25056      r.data.bgActivePosistion = undefined;
 25057      var cy = r.cy;
 25058      var now = r.touchData.now;
 25059      var earlier = r.touchData.earlier;
 25060  
 25061      if (e.touches[0]) {
 25062        var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
 25063        now[0] = pos[0];
 25064        now[1] = pos[1];
 25065      }
 25066  
 25067      if (e.touches[1]) {
 25068        var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
 25069        now[2] = pos[0];
 25070        now[3] = pos[1];
 25071      }
 25072  
 25073      if (e.touches[2]) {
 25074        var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
 25075        now[4] = pos[0];
 25076        now[5] = pos[1];
 25077      } // record starting points for pinch-to-zoom
 25078  
 25079  
 25080      if (e.touches[1]) {
 25081        r.touchData.singleTouchMoved = true;
 25082        freeDraggedElements(r.dragData.touchDragEles);
 25083        var offsets = r.findContainerClientCoords();
 25084        offsetLeft = offsets[0];
 25085        offsetTop = offsets[1];
 25086        containerWidth = offsets[2];
 25087        containerHeight = offsets[3];
 25088        f1x1 = e.touches[0].clientX - offsetLeft;
 25089        f1y1 = e.touches[0].clientY - offsetTop;
 25090        f2x1 = e.touches[1].clientX - offsetLeft;
 25091        f2y1 = e.touches[1].clientY - offsetTop;
 25092        twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
 25093        var pan = cy.pan();
 25094        var zoom = cy.zoom();
 25095        distance1 = distance(f1x1, f1y1, f2x1, f2y1);
 25096        distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
 25097        center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
 25098        modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
 25099  
 25100        var cxtDistThreshold = 200;
 25101        var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
 25102  
 25103        if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
 25104          var near1 = r.findNearestElement(now[0], now[1], true, true);
 25105          var near2 = r.findNearestElement(now[2], now[3], true, true);
 25106  
 25107          if (near1 && near1.isNode()) {
 25108            near1.activate().emit({
 25109              originalEvent: e,
 25110              type: 'cxttapstart',
 25111              position: {
 25112                x: now[0],
 25113                y: now[1]
 25114              }
 25115            });
 25116            r.touchData.start = near1;
 25117          } else if (near2 && near2.isNode()) {
 25118            near2.activate().emit({
 25119              originalEvent: e,
 25120              type: 'cxttapstart',
 25121              position: {
 25122                x: now[0],
 25123                y: now[1]
 25124              }
 25125            });
 25126            r.touchData.start = near2;
 25127          } else {
 25128            cy.emit({
 25129              originalEvent: e,
 25130              type: 'cxttapstart',
 25131              position: {
 25132                x: now[0],
 25133                y: now[1]
 25134              }
 25135            });
 25136          }
 25137  
 25138          if (r.touchData.start) {
 25139            r.touchData.start._private.grabbed = false;
 25140          }
 25141  
 25142          r.touchData.cxt = true;
 25143          r.touchData.cxtDragged = false;
 25144          r.data.bgActivePosistion = undefined;
 25145          r.redraw();
 25146          return;
 25147        }
 25148      }
 25149  
 25150      if (e.touches[2]) {
 25151        // ignore
 25152        // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
 25153        if (cy.boxSelectionEnabled()) {
 25154          e.preventDefault();
 25155        }
 25156      } else if (e.touches[1]) ; else if (e.touches[0]) {
 25157        var nears = r.findNearestElements(now[0], now[1], true, true);
 25158        var near = nears[0];
 25159  
 25160        if (near != null) {
 25161          near.activate();
 25162          r.touchData.start = near;
 25163          r.touchData.starts = nears;
 25164  
 25165          if (r.nodeIsGrabbable(near)) {
 25166            var draggedEles = r.dragData.touchDragEles = cy.collection();
 25167            var selectedNodes = null;
 25168            r.redrawHint('eles', true);
 25169            r.redrawHint('drag', true);
 25170  
 25171            if (near.selected()) {
 25172              // reset drag elements, since near will be added again
 25173              selectedNodes = cy.$(function (ele) {
 25174                return ele.selected() && r.nodeIsGrabbable(ele);
 25175              });
 25176              addNodesToDrag(selectedNodes, {
 25177                addToList: draggedEles
 25178              });
 25179            } else {
 25180              addNodeToDrag(near, {
 25181                addToList: draggedEles
 25182              });
 25183            }
 25184  
 25185            setGrabTarget(near);
 25186  
 25187            var makeEvent = function makeEvent(type) {
 25188              return {
 25189                originalEvent: e,
 25190                type: type,
 25191                position: {
 25192                  x: now[0],
 25193                  y: now[1]
 25194                }
 25195              };
 25196            };
 25197  
 25198            near.emit(makeEvent('grabon'));
 25199  
 25200            if (selectedNodes) {
 25201              selectedNodes.forEach(function (n) {
 25202                n.emit(makeEvent('grab'));
 25203              });
 25204            } else {
 25205              near.emit(makeEvent('grab'));
 25206            }
 25207          }
 25208        }
 25209  
 25210        triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
 25211          x: now[0],
 25212          y: now[1]
 25213        });
 25214  
 25215        if (near == null) {
 25216          r.data.bgActivePosistion = {
 25217            x: pos[0],
 25218            y: pos[1]
 25219          };
 25220          r.redrawHint('select', true);
 25221          r.redraw();
 25222        } // Tap, taphold
 25223        // -----
 25224  
 25225  
 25226        r.touchData.singleTouchMoved = false;
 25227        r.touchData.singleTouchStartTime = +new Date();
 25228        clearTimeout(r.touchData.tapholdTimeout);
 25229        r.touchData.tapholdTimeout = setTimeout(function () {
 25230          if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
 25231          && !r.touchData.selecting // box selection shouldn't allow taphold through
 25232          ) {
 25233              triggerEvents(r.touchData.start, ['taphold'], e, {
 25234                x: now[0],
 25235                y: now[1]
 25236              });
 25237            }
 25238        }, r.tapholdDuration);
 25239      }
 25240  
 25241      if (e.touches.length >= 1) {
 25242        var sPos = r.touchData.startPosition = [];
 25243  
 25244        for (var i = 0; i < now.length; i++) {
 25245          sPos[i] = earlier[i] = now[i];
 25246        }
 25247  
 25248        var touch0 = e.touches[0];
 25249        r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
 25250      }
 25251    }, false);
 25252    var touchmoveHandler;
 25253    r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
 25254      // eslint-disable-line no-undef
 25255      var capture = r.touchData.capture;
 25256  
 25257      if (!capture && !eventInContainer(e)) {
 25258        return;
 25259      }
 25260  
 25261      var select = r.selection;
 25262      var cy = r.cy;
 25263      var now = r.touchData.now;
 25264      var earlier = r.touchData.earlier;
 25265      var zoom = cy.zoom();
 25266  
 25267      if (e.touches[0]) {
 25268        var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
 25269        now[0] = pos[0];
 25270        now[1] = pos[1];
 25271      }
 25272  
 25273      if (e.touches[1]) {
 25274        var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
 25275        now[2] = pos[0];
 25276        now[3] = pos[1];
 25277      }
 25278  
 25279      if (e.touches[2]) {
 25280        var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
 25281        now[4] = pos[0];
 25282        now[5] = pos[1];
 25283      }
 25284  
 25285      var startGPos = r.touchData.startGPosition;
 25286      var isOverThresholdDrag;
 25287  
 25288      if (capture && e.touches[0] && startGPos) {
 25289        var disp = [];
 25290  
 25291        for (var j = 0; j < now.length; j++) {
 25292          disp[j] = now[j] - earlier[j];
 25293        }
 25294  
 25295        var dx = e.touches[0].clientX - startGPos[0];
 25296        var dx2 = dx * dx;
 25297        var dy = e.touches[0].clientY - startGPos[1];
 25298        var dy2 = dy * dy;
 25299        var dist2 = dx2 + dy2;
 25300        isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
 25301      } // context swipe cancelling
 25302  
 25303  
 25304      if (capture && r.touchData.cxt) {
 25305        e.preventDefault();
 25306        var f1x2 = e.touches[0].clientX - offsetLeft,
 25307            f1y2 = e.touches[0].clientY - offsetTop;
 25308        var f2x2 = e.touches[1].clientX - offsetLeft,
 25309            f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
 25310  
 25311        var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
 25312        var factorSq = distance2Sq / distance1Sq;
 25313        var distThreshold = 150;
 25314        var distThresholdSq = distThreshold * distThreshold;
 25315        var factorThreshold = 1.5;
 25316        var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
 25317  
 25318        if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
 25319          r.touchData.cxt = false;
 25320          r.data.bgActivePosistion = undefined;
 25321          r.redrawHint('select', true);
 25322          var cxtEvt = {
 25323            originalEvent: e,
 25324            type: 'cxttapend',
 25325            position: {
 25326              x: now[0],
 25327              y: now[1]
 25328            }
 25329          };
 25330  
 25331          if (r.touchData.start) {
 25332            r.touchData.start.unactivate().emit(cxtEvt);
 25333            r.touchData.start = null;
 25334          } else {
 25335            cy.emit(cxtEvt);
 25336          }
 25337        }
 25338      } // context swipe
 25339  
 25340  
 25341      if (capture && r.touchData.cxt) {
 25342        var cxtEvt = {
 25343          originalEvent: e,
 25344          type: 'cxtdrag',
 25345          position: {
 25346            x: now[0],
 25347            y: now[1]
 25348          }
 25349        };
 25350        r.data.bgActivePosistion = undefined;
 25351        r.redrawHint('select', true);
 25352  
 25353        if (r.touchData.start) {
 25354          r.touchData.start.emit(cxtEvt);
 25355        } else {
 25356          cy.emit(cxtEvt);
 25357        }
 25358  
 25359        if (r.touchData.start) {
 25360          r.touchData.start._private.grabbed = false;
 25361        }
 25362  
 25363        r.touchData.cxtDragged = true;
 25364        var near = r.findNearestElement(now[0], now[1], true, true);
 25365  
 25366        if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
 25367          if (r.touchData.cxtOver) {
 25368            r.touchData.cxtOver.emit({
 25369              originalEvent: e,
 25370              type: 'cxtdragout',
 25371              position: {
 25372                x: now[0],
 25373                y: now[1]
 25374              }
 25375            });
 25376          }
 25377  
 25378          r.touchData.cxtOver = near;
 25379  
 25380          if (near) {
 25381            near.emit({
 25382              originalEvent: e,
 25383              type: 'cxtdragover',
 25384              position: {
 25385                x: now[0],
 25386                y: now[1]
 25387              }
 25388            });
 25389          }
 25390        } // box selection
 25391  
 25392      } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
 25393        e.preventDefault();
 25394        r.data.bgActivePosistion = undefined;
 25395        this.lastThreeTouch = +new Date();
 25396  
 25397        if (!r.touchData.selecting) {
 25398          cy.emit({
 25399            originalEvent: e,
 25400            type: 'boxstart',
 25401            position: {
 25402              x: now[0],
 25403              y: now[1]
 25404            }
 25405          });
 25406        }
 25407  
 25408        r.touchData.selecting = true;
 25409        r.touchData.didSelect = true;
 25410        select[4] = 1;
 25411  
 25412        if (!select || select.length === 0 || select[0] === undefined) {
 25413          select[0] = (now[0] + now[2] + now[4]) / 3;
 25414          select[1] = (now[1] + now[3] + now[5]) / 3;
 25415          select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
 25416          select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
 25417        } else {
 25418          select[2] = (now[0] + now[2] + now[4]) / 3;
 25419          select[3] = (now[1] + now[3] + now[5]) / 3;
 25420        }
 25421  
 25422        r.redrawHint('select', true);
 25423        r.redraw(); // pinch to zoom
 25424      } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
 25425      && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
 25426        // two fingers => pinch to zoom
 25427        e.preventDefault();
 25428        r.data.bgActivePosistion = undefined;
 25429        r.redrawHint('select', true);
 25430        var draggedEles = r.dragData.touchDragEles;
 25431  
 25432        if (draggedEles) {
 25433          r.redrawHint('drag', true);
 25434  
 25435          for (var i = 0; i < draggedEles.length; i++) {
 25436            var de_p = draggedEles[i]._private;
 25437            de_p.grabbed = false;
 25438            de_p.rscratch.inDragLayer = false;
 25439          }
 25440        }
 25441  
 25442        var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
 25443  
 25444        var f1x2 = e.touches[0].clientX - offsetLeft,
 25445            f1y2 = e.touches[0].clientY - offsetTop;
 25446        var f2x2 = e.touches[1].clientX - offsetLeft,
 25447            f2y2 = e.touches[1].clientY - offsetTop;
 25448        var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
 25449        // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
 25450  
 25451        var factor = distance2 / distance1;
 25452  
 25453        if (twoFingersStartInside) {
 25454          // delta finger1
 25455          var df1x = f1x2 - f1x1;
 25456          var df1y = f1y2 - f1y1; // delta finger 2
 25457  
 25458          var df2x = f2x2 - f2x1;
 25459          var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
 25460          // i.e. so pinching cancels out and moving together pans
 25461  
 25462          var tx = (df1x + df2x) / 2;
 25463          var ty = (df1y + df2y) / 2; // now calculate the zoom
 25464  
 25465          var zoom1 = cy.zoom();
 25466          var zoom2 = zoom1 * factor;
 25467          var pan1 = cy.pan(); // the model center point converted to the current rendered pos
 25468  
 25469          var ctrx = modelCenter1[0] * zoom1 + pan1.x;
 25470          var ctry = modelCenter1[1] * zoom1 + pan1.y;
 25471          var pan2 = {
 25472            x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
 25473            y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
 25474          }; // remove dragged eles
 25475  
 25476          if (_start && _start.active()) {
 25477            var draggedEles = r.dragData.touchDragEles;
 25478            freeDraggedElements(draggedEles);
 25479            r.redrawHint('drag', true);
 25480            r.redrawHint('eles', true);
 25481  
 25482            _start.unactivate().emit('freeon');
 25483  
 25484            draggedEles.emit('free');
 25485  
 25486            if (r.dragData.didDrag) {
 25487              _start.emit('dragfreeon');
 25488  
 25489              draggedEles.emit('dragfree');
 25490            }
 25491          }
 25492  
 25493          cy.viewport({
 25494            zoom: zoom2,
 25495            pan: pan2,
 25496            cancelOnFailedZoom: true
 25497          });
 25498          distance1 = distance2;
 25499          f1x1 = f1x2;
 25500          f1y1 = f1y2;
 25501          f2x1 = f2x2;
 25502          f2y1 = f2y2;
 25503          r.pinching = true;
 25504        } // Re-project
 25505  
 25506  
 25507        if (e.touches[0]) {
 25508          var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
 25509          now[0] = pos[0];
 25510          now[1] = pos[1];
 25511        }
 25512  
 25513        if (e.touches[1]) {
 25514          var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
 25515          now[2] = pos[0];
 25516          now[3] = pos[1];
 25517        }
 25518  
 25519        if (e.touches[2]) {
 25520          var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
 25521          now[4] = pos[0];
 25522          now[5] = pos[1];
 25523        }
 25524      } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
 25525      ) {
 25526          var start = r.touchData.start;
 25527          var last = r.touchData.last;
 25528          var near;
 25529  
 25530          if (!r.hoverData.draggingEles && !r.swipePanning) {
 25531            near = r.findNearestElement(now[0], now[1], true, true);
 25532          }
 25533  
 25534          if (capture && start != null) {
 25535            e.preventDefault();
 25536          } // dragging nodes
 25537  
 25538  
 25539          if (capture && start != null && r.nodeIsDraggable(start)) {
 25540            if (isOverThresholdDrag) {
 25541              // then dragging can happen
 25542              var draggedEles = r.dragData.touchDragEles;
 25543              var justStartedDrag = !r.dragData.didDrag;
 25544  
 25545              if (justStartedDrag) {
 25546                addNodesToDrag(draggedEles, {
 25547                  inDragLayer: true
 25548                });
 25549              }
 25550  
 25551              r.dragData.didDrag = true;
 25552              var totalShift = {
 25553                x: 0,
 25554                y: 0
 25555              };
 25556  
 25557              if (number(disp[0]) && number(disp[1])) {
 25558                totalShift.x += disp[0];
 25559                totalShift.y += disp[1];
 25560  
 25561                if (justStartedDrag) {
 25562                  r.redrawHint('eles', true);
 25563                  var dragDelta = r.touchData.dragDelta;
 25564  
 25565                  if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
 25566                    totalShift.x += dragDelta[0];
 25567                    totalShift.y += dragDelta[1];
 25568                  }
 25569                }
 25570              }
 25571  
 25572              r.hoverData.draggingEles = true;
 25573              draggedEles.silentShift(totalShift).emit('position drag');
 25574              r.redrawHint('drag', true);
 25575  
 25576              if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
 25577                r.redrawHint('eles', true);
 25578              }
 25579  
 25580              r.redraw();
 25581            } else {
 25582              // otherise keep track of drag delta for later
 25583              var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
 25584  
 25585              if (dragDelta.length === 0) {
 25586                dragDelta.push(disp[0]);
 25587                dragDelta.push(disp[1]);
 25588              } else {
 25589                dragDelta[0] += disp[0];
 25590                dragDelta[1] += disp[1];
 25591              }
 25592            }
 25593          } // touchmove
 25594  
 25595  
 25596          {
 25597            triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
 25598              x: now[0],
 25599              y: now[1]
 25600            });
 25601  
 25602            if ((!start || !start.grabbed()) && near != last) {
 25603              if (last) {
 25604                last.emit({
 25605                  originalEvent: e,
 25606                  type: 'tapdragout',
 25607                  position: {
 25608                    x: now[0],
 25609                    y: now[1]
 25610                  }
 25611                });
 25612              }
 25613  
 25614              if (near) {
 25615                near.emit({
 25616                  originalEvent: e,
 25617                  type: 'tapdragover',
 25618                  position: {
 25619                    x: now[0],
 25620                    y: now[1]
 25621                  }
 25622                });
 25623              }
 25624            }
 25625  
 25626            r.touchData.last = near;
 25627          } // check to cancel taphold
 25628  
 25629          if (capture) {
 25630            for (var i = 0; i < now.length; i++) {
 25631              if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
 25632                r.touchData.singleTouchMoved = true;
 25633              }
 25634            }
 25635          } // panning
 25636  
 25637  
 25638          if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
 25639            var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
 25640  
 25641            if (allowPassthrough) {
 25642              e.preventDefault();
 25643  
 25644              if (!r.data.bgActivePosistion) {
 25645                r.data.bgActivePosistion = array2point(r.touchData.startPosition);
 25646              }
 25647  
 25648              if (r.swipePanning) {
 25649                cy.panBy({
 25650                  x: disp[0] * zoom,
 25651                  y: disp[1] * zoom
 25652                });
 25653              } else if (isOverThresholdDrag) {
 25654                r.swipePanning = true;
 25655                cy.panBy({
 25656                  x: dx * zoom,
 25657                  y: dy * zoom
 25658                });
 25659  
 25660                if (start) {
 25661                  start.unactivate();
 25662                  r.redrawHint('select', true);
 25663                  r.touchData.start = null;
 25664                }
 25665              }
 25666            } // Re-project
 25667  
 25668  
 25669            var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
 25670            now[0] = pos[0];
 25671            now[1] = pos[1];
 25672          }
 25673        }
 25674  
 25675      for (var j = 0; j < now.length; j++) {
 25676        earlier[j] = now[j];
 25677      } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
 25678  
 25679  
 25680      if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
 25681        r.data.bgActivePosistion = undefined;
 25682        r.redrawHint('select', true);
 25683        r.redraw();
 25684      }
 25685    }, false);
 25686    var touchcancelHandler;
 25687    r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
 25688      // eslint-disable-line no-unused-vars
 25689      var start = r.touchData.start;
 25690      r.touchData.capture = false;
 25691  
 25692      if (start) {
 25693        start.unactivate();
 25694      }
 25695    });
 25696    var touchendHandler;
 25697    r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
 25698      // eslint-disable-line no-unused-vars
 25699      var start = r.touchData.start;
 25700      var capture = r.touchData.capture;
 25701  
 25702      if (capture) {
 25703        if (e.touches.length === 0) {
 25704          r.touchData.capture = false;
 25705        }
 25706  
 25707        e.preventDefault();
 25708      } else {
 25709        return;
 25710      }
 25711  
 25712      var select = r.selection;
 25713      r.swipePanning = false;
 25714      r.hoverData.draggingEles = false;
 25715      var cy = r.cy;
 25716      var zoom = cy.zoom();
 25717      var now = r.touchData.now;
 25718      var earlier = r.touchData.earlier;
 25719  
 25720      if (e.touches[0]) {
 25721        var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
 25722        now[0] = pos[0];
 25723        now[1] = pos[1];
 25724      }
 25725  
 25726      if (e.touches[1]) {
 25727        var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
 25728        now[2] = pos[0];
 25729        now[3] = pos[1];
 25730      }
 25731  
 25732      if (e.touches[2]) {
 25733        var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
 25734        now[4] = pos[0];
 25735        now[5] = pos[1];
 25736      }
 25737  
 25738      if (start) {
 25739        start.unactivate();
 25740      }
 25741  
 25742      var ctxTapend;
 25743  
 25744      if (r.touchData.cxt) {
 25745        ctxTapend = {
 25746          originalEvent: e,
 25747          type: 'cxttapend',
 25748          position: {
 25749            x: now[0],
 25750            y: now[1]
 25751          }
 25752        };
 25753  
 25754        if (start) {
 25755          start.emit(ctxTapend);
 25756        } else {
 25757          cy.emit(ctxTapend);
 25758        }
 25759  
 25760        if (!r.touchData.cxtDragged) {
 25761          var ctxTap = {
 25762            originalEvent: e,
 25763            type: 'cxttap',
 25764            position: {
 25765              x: now[0],
 25766              y: now[1]
 25767            }
 25768          };
 25769  
 25770          if (start) {
 25771            start.emit(ctxTap);
 25772          } else {
 25773            cy.emit(ctxTap);
 25774          }
 25775        }
 25776  
 25777        if (r.touchData.start) {
 25778          r.touchData.start._private.grabbed = false;
 25779        }
 25780  
 25781        r.touchData.cxt = false;
 25782        r.touchData.start = null;
 25783        r.redraw();
 25784        return;
 25785      } // no more box selection if we don't have three fingers
 25786  
 25787  
 25788      if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
 25789        r.touchData.selecting = false;
 25790        var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
 25791        select[0] = undefined;
 25792        select[1] = undefined;
 25793        select[2] = undefined;
 25794        select[3] = undefined;
 25795        select[4] = 0;
 25796        r.redrawHint('select', true);
 25797        cy.emit({
 25798          type: 'boxend',
 25799          originalEvent: e,
 25800          position: {
 25801            x: now[0],
 25802            y: now[1]
 25803          }
 25804        });
 25805  
 25806        var eleWouldBeSelected = function eleWouldBeSelected(ele) {
 25807          return ele.selectable() && !ele.selected();
 25808        };
 25809  
 25810        box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
 25811  
 25812        if (box.nonempty()) {
 25813          r.redrawHint('eles', true);
 25814        }
 25815  
 25816        r.redraw();
 25817      }
 25818  
 25819      if (start != null) {
 25820        start.unactivate();
 25821      }
 25822  
 25823      if (e.touches[2]) {
 25824        r.data.bgActivePosistion = undefined;
 25825        r.redrawHint('select', true);
 25826      } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
 25827        r.data.bgActivePosistion = undefined;
 25828        r.redrawHint('select', true);
 25829        var draggedEles = r.dragData.touchDragEles;
 25830  
 25831        if (start != null) {
 25832          var startWasGrabbed = start._private.grabbed;
 25833          freeDraggedElements(draggedEles);
 25834          r.redrawHint('drag', true);
 25835          r.redrawHint('eles', true);
 25836  
 25837          if (startWasGrabbed) {
 25838            start.emit('freeon');
 25839            draggedEles.emit('free');
 25840  
 25841            if (r.dragData.didDrag) {
 25842              start.emit('dragfreeon');
 25843              draggedEles.emit('dragfree');
 25844            }
 25845          }
 25846  
 25847          triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
 25848            x: now[0],
 25849            y: now[1]
 25850          });
 25851          start.unactivate();
 25852          r.touchData.start = null;
 25853        } else {
 25854          var near = r.findNearestElement(now[0], now[1], true, true);
 25855          triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
 25856            x: now[0],
 25857            y: now[1]
 25858          });
 25859        }
 25860  
 25861        var dx = r.touchData.startPosition[0] - now[0];
 25862        var dx2 = dx * dx;
 25863        var dy = r.touchData.startPosition[1] - now[1];
 25864        var dy2 = dy * dy;
 25865        var dist2 = dx2 + dy2;
 25866        var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
 25867  
 25868        if (!r.touchData.singleTouchMoved) {
 25869          if (!start) {
 25870            cy.$(':selected').unselect(['tapunselect']);
 25871          }
 25872  
 25873          triggerEvents(start, ['tap', 'vclick'], e, {
 25874            x: now[0],
 25875            y: now[1]
 25876          });
 25877        } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
 25878  
 25879  
 25880        if (start != null && !r.dragData.didDrag // didn't drag nodes around
 25881        && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
 25882        ) {
 25883            if (cy.selectionType() === 'single') {
 25884              cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
 25885              start.select(['tapselect']);
 25886            } else {
 25887              if (start.selected()) {
 25888                start.unselect(['tapunselect']);
 25889              } else {
 25890                start.select(['tapselect']);
 25891              }
 25892            }
 25893  
 25894            r.redrawHint('eles', true);
 25895          }
 25896  
 25897        r.touchData.singleTouchMoved = true;
 25898      }
 25899  
 25900      for (var j = 0; j < now.length; j++) {
 25901        earlier[j] = now[j];
 25902      }
 25903  
 25904      r.dragData.didDrag = false; // reset for next touchstart
 25905  
 25906      if (e.touches.length === 0) {
 25907        r.touchData.dragDelta = [];
 25908        r.touchData.startPosition = null;
 25909        r.touchData.startGPosition = null;
 25910        r.touchData.didSelect = false;
 25911      }
 25912  
 25913      if (e.touches.length < 2) {
 25914        if (e.touches.length === 1) {
 25915          // the old start global pos'n may not be the same finger that remains
 25916          r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
 25917        }
 25918  
 25919        r.pinching = false;
 25920        r.redrawHint('eles', true);
 25921        r.redraw();
 25922      } //r.redraw();
 25923  
 25924    }, false); // fallback compatibility layer for ms pointer events
 25925  
 25926    if (typeof TouchEvent === 'undefined') {
 25927      var pointers = [];
 25928  
 25929      var makeTouch = function makeTouch(e) {
 25930        return {
 25931          clientX: e.clientX,
 25932          clientY: e.clientY,
 25933          force: 1,
 25934          identifier: e.pointerId,
 25935          pageX: e.pageX,
 25936          pageY: e.pageY,
 25937          radiusX: e.width / 2,
 25938          radiusY: e.height / 2,
 25939          screenX: e.screenX,
 25940          screenY: e.screenY,
 25941          target: e.target
 25942        };
 25943      };
 25944  
 25945      var makePointer = function makePointer(e) {
 25946        return {
 25947          event: e,
 25948          touch: makeTouch(e)
 25949        };
 25950      };
 25951  
 25952      var addPointer = function addPointer(e) {
 25953        pointers.push(makePointer(e));
 25954      };
 25955  
 25956      var removePointer = function removePointer(e) {
 25957        for (var i = 0; i < pointers.length; i++) {
 25958          var p = pointers[i];
 25959  
 25960          if (p.event.pointerId === e.pointerId) {
 25961            pointers.splice(i, 1);
 25962            return;
 25963          }
 25964        }
 25965      };
 25966  
 25967      var updatePointer = function updatePointer(e) {
 25968        var p = pointers.filter(function (p) {
 25969          return p.event.pointerId === e.pointerId;
 25970        })[0];
 25971        p.event = e;
 25972        p.touch = makeTouch(e);
 25973      };
 25974  
 25975      var addTouchesToEvent = function addTouchesToEvent(e) {
 25976        e.touches = pointers.map(function (p) {
 25977          return p.touch;
 25978        });
 25979      };
 25980  
 25981      var pointerIsMouse = function pointerIsMouse(e) {
 25982        return e.pointerType === 'mouse' || e.pointerType === 4;
 25983      };
 25984  
 25985      r.registerBinding(r.container, 'pointerdown', function (e) {
 25986        if (pointerIsMouse(e)) {
 25987          return;
 25988        } // mouse already handled
 25989  
 25990  
 25991        e.preventDefault();
 25992        addPointer(e);
 25993        addTouchesToEvent(e);
 25994        touchstartHandler(e);
 25995      });
 25996      r.registerBinding(r.container, 'pointerup', function (e) {
 25997        if (pointerIsMouse(e)) {
 25998          return;
 25999        } // mouse already handled
 26000  
 26001  
 26002        removePointer(e);
 26003        addTouchesToEvent(e);
 26004        touchendHandler(e);
 26005      });
 26006      r.registerBinding(r.container, 'pointercancel', function (e) {
 26007        if (pointerIsMouse(e)) {
 26008          return;
 26009        } // mouse already handled
 26010  
 26011  
 26012        removePointer(e);
 26013        addTouchesToEvent(e);
 26014        touchcancelHandler(e);
 26015      });
 26016      r.registerBinding(r.container, 'pointermove', function (e) {
 26017        if (pointerIsMouse(e)) {
 26018          return;
 26019        } // mouse already handled
 26020  
 26021  
 26022        e.preventDefault();
 26023        updatePointer(e);
 26024        addTouchesToEvent(e);
 26025        touchmoveHandler(e);
 26026      });
 26027    }
 26028  };
 26029  
 26030  var BRp$d = {};
 26031  
 26032  BRp$d.generatePolygon = function (name, points) {
 26033    return this.nodeShapes[name] = {
 26034      renderer: this,
 26035      name: name,
 26036      points: points,
 26037      draw: function draw(context, centerX, centerY, width, height) {
 26038        this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
 26039      },
 26040      intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26041        return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
 26042      },
 26043      checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26044        return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
 26045      }
 26046    };
 26047  };
 26048  
 26049  BRp$d.generateEllipse = function () {
 26050    return this.nodeShapes['ellipse'] = {
 26051      renderer: this,
 26052      name: 'ellipse',
 26053      draw: function draw(context, centerX, centerY, width, height) {
 26054        this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
 26055      },
 26056      intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26057        return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
 26058      },
 26059      checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26060        return checkInEllipse(x, y, width, height, centerX, centerY, padding);
 26061      }
 26062    };
 26063  };
 26064  
 26065  BRp$d.generateRoundPolygon = function (name, points) {
 26066    // Pre-compute control points
 26067    // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
 26068    // the unit vectors.
 26069    // For simplicity the layout will be:
 26070    // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
 26071    var allPoints = new Array(points.length * 2);
 26072  
 26073    for (var i = 0; i < points.length / 2; i++) {
 26074      var sourceIndex = i * 2;
 26075      var destIndex = void 0;
 26076  
 26077      if (i < points.length / 2 - 1) {
 26078        destIndex = (i + 1) * 2;
 26079      } else {
 26080        destIndex = 0;
 26081      }
 26082  
 26083      allPoints[i * 4] = points[sourceIndex];
 26084      allPoints[i * 4 + 1] = points[sourceIndex + 1];
 26085      var xDest = points[destIndex] - points[sourceIndex];
 26086      var yDest = points[destIndex + 1] - points[sourceIndex + 1];
 26087      var norm = Math.sqrt(xDest * xDest + yDest * yDest);
 26088      allPoints[i * 4 + 2] = xDest / norm;
 26089      allPoints[i * 4 + 3] = yDest / norm;
 26090    }
 26091  
 26092    return this.nodeShapes[name] = {
 26093      renderer: this,
 26094      name: name,
 26095      points: allPoints,
 26096      draw: function draw(context, centerX, centerY, width, height) {
 26097        this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
 26098      },
 26099      intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26100        return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
 26101      },
 26102      checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26103        return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
 26104      }
 26105    };
 26106  };
 26107  
 26108  BRp$d.generateRoundRectangle = function () {
 26109    return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
 26110      renderer: this,
 26111      name: 'round-rectangle',
 26112      points: generateUnitNgonPointsFitToSquare(4, 0),
 26113      draw: function draw(context, centerX, centerY, width, height) {
 26114        this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
 26115      },
 26116      intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26117        return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
 26118      },
 26119      checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26120        var cornerRadius = getRoundRectangleRadius(width, height);
 26121        var diam = cornerRadius * 2; // Check hBox
 26122  
 26123        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
 26124          return true;
 26125        } // Check vBox
 26126  
 26127  
 26128        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
 26129          return true;
 26130        } // Check top left quarter circle
 26131  
 26132  
 26133        if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
 26134          return true;
 26135        } // Check top right quarter circle
 26136  
 26137  
 26138        if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
 26139          return true;
 26140        } // Check bottom right quarter circle
 26141  
 26142  
 26143        if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
 26144          return true;
 26145        } // Check bottom left quarter circle
 26146  
 26147  
 26148        if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
 26149          return true;
 26150        }
 26151  
 26152        return false;
 26153      }
 26154    };
 26155  };
 26156  
 26157  BRp$d.generateCutRectangle = function () {
 26158    return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
 26159      renderer: this,
 26160      name: 'cut-rectangle',
 26161      cornerLength: getCutRectangleCornerLength(),
 26162      points: generateUnitNgonPointsFitToSquare(4, 0),
 26163      draw: function draw(context, centerX, centerY, width, height) {
 26164        this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
 26165      },
 26166      generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
 26167        var cl = this.cornerLength;
 26168        var hh = height / 2;
 26169        var hw = width / 2;
 26170        var xBegin = centerX - hw;
 26171        var xEnd = centerX + hw;
 26172        var yBegin = centerY - hh;
 26173        var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
 26174  
 26175        return {
 26176          topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
 26177          topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
 26178          bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
 26179          bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
 26180        };
 26181      },
 26182      intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26183        var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
 26184        var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
 26185        return polygonIntersectLine(x, y, pts, nodeX, nodeY);
 26186      },
 26187      checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26188        // Check hBox
 26189        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
 26190          return true;
 26191        } // Check vBox
 26192  
 26193  
 26194        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
 26195          return true;
 26196        }
 26197  
 26198        var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
 26199        return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
 26200      }
 26201    };
 26202  };
 26203  
 26204  BRp$d.generateBarrel = function () {
 26205    return this.nodeShapes['barrel'] = {
 26206      renderer: this,
 26207      name: 'barrel',
 26208      points: generateUnitNgonPointsFitToSquare(4, 0),
 26209      draw: function draw(context, centerX, centerY, width, height) {
 26210        this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
 26211      },
 26212      intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26213        // use two fixed t values for the bezier curve approximation
 26214        var t0 = 0.15;
 26215        var t1 = 0.5;
 26216        var t2 = 0.85;
 26217        var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
 26218  
 26219        var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
 26220          // approximate curve pts based on the two t values
 26221          var m0 = qbezierPtAt({
 26222            x: pts[0],
 26223            y: pts[1]
 26224          }, {
 26225            x: pts[2],
 26226            y: pts[3]
 26227          }, {
 26228            x: pts[4],
 26229            y: pts[5]
 26230          }, t0);
 26231          var m1 = qbezierPtAt({
 26232            x: pts[0],
 26233            y: pts[1]
 26234          }, {
 26235            x: pts[2],
 26236            y: pts[3]
 26237          }, {
 26238            x: pts[4],
 26239            y: pts[5]
 26240          }, t1);
 26241          var m2 = qbezierPtAt({
 26242            x: pts[0],
 26243            y: pts[1]
 26244          }, {
 26245            x: pts[2],
 26246            y: pts[3]
 26247          }, {
 26248            x: pts[4],
 26249            y: pts[5]
 26250          }, t2);
 26251          return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
 26252        };
 26253  
 26254        var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
 26255        return polygonIntersectLine(x, y, pts, nodeX, nodeY);
 26256      },
 26257      generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
 26258        var hh = height / 2;
 26259        var hw = width / 2;
 26260        var xBegin = centerX - hw;
 26261        var xEnd = centerX + hw;
 26262        var yBegin = centerY - hh;
 26263        var yEnd = centerY + hh;
 26264        var curveConstants = getBarrelCurveConstants(width, height);
 26265        var hOffset = curveConstants.heightOffset;
 26266        var wOffset = curveConstants.widthOffset;
 26267        var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
 26268  
 26269        var pts = {
 26270          topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
 26271          topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
 26272          bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
 26273          bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
 26274        };
 26275        pts.topLeft.isTop = true;
 26276        pts.topRight.isTop = true;
 26277        pts.bottomLeft.isBottom = true;
 26278        pts.bottomRight.isBottom = true;
 26279        return pts;
 26280      },
 26281      checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26282        var curveConstants = getBarrelCurveConstants(width, height);
 26283        var hOffset = curveConstants.heightOffset;
 26284        var wOffset = curveConstants.widthOffset; // Check hBox
 26285  
 26286        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
 26287          return true;
 26288        } // Check vBox
 26289  
 26290  
 26291        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
 26292          return true;
 26293        }
 26294  
 26295        var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
 26296  
 26297        var getCurveT = function getCurveT(x, y, curvePts) {
 26298          var x0 = curvePts[4];
 26299          var x1 = curvePts[2];
 26300          var x2 = curvePts[0];
 26301          var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
 26302  
 26303          var y2 = curvePts[1];
 26304          var xMin = Math.min(x0, x2);
 26305          var xMax = Math.max(x0, x2);
 26306          var yMin = Math.min(y0, y2);
 26307          var yMax = Math.max(y0, y2);
 26308  
 26309          if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
 26310            var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
 26311            var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
 26312            var validRoots = roots.filter(function (r) {
 26313              return 0 <= r && r <= 1;
 26314            });
 26315  
 26316            if (validRoots.length > 0) {
 26317              return validRoots[0];
 26318            }
 26319          }
 26320  
 26321          return null;
 26322        };
 26323  
 26324        var curveRegions = Object.keys(barrelCurvePts);
 26325  
 26326        for (var i = 0; i < curveRegions.length; i++) {
 26327          var corner = curveRegions[i];
 26328          var cornerPts = barrelCurvePts[corner];
 26329          var t = getCurveT(x, y, cornerPts);
 26330  
 26331          if (t == null) {
 26332            continue;
 26333          }
 26334  
 26335          var y0 = cornerPts[5];
 26336          var y1 = cornerPts[3];
 26337          var y2 = cornerPts[1];
 26338          var bezY = qbezierAt(y0, y1, y2, t);
 26339  
 26340          if (cornerPts.isTop && bezY <= y) {
 26341            return true;
 26342          }
 26343  
 26344          if (cornerPts.isBottom && y <= bezY) {
 26345            return true;
 26346          }
 26347        }
 26348  
 26349        return false;
 26350      }
 26351    };
 26352  };
 26353  
 26354  BRp$d.generateBottomRoundrectangle = function () {
 26355    return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
 26356      renderer: this,
 26357      name: 'bottom-round-rectangle',
 26358      points: generateUnitNgonPointsFitToSquare(4, 0),
 26359      draw: function draw(context, centerX, centerY, width, height) {
 26360        this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
 26361      },
 26362      intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26363        var topStartX = nodeX - (width / 2 + padding);
 26364        var topStartY = nodeY - (height / 2 + padding);
 26365        var topEndY = topStartY;
 26366        var topEndX = nodeX + (width / 2 + padding);
 26367        var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
 26368  
 26369        if (topIntersections.length > 0) {
 26370          return topIntersections;
 26371        }
 26372  
 26373        return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
 26374      },
 26375      checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26376        var cornerRadius = getRoundRectangleRadius(width, height);
 26377        var diam = 2 * cornerRadius; // Check hBox
 26378  
 26379        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
 26380          return true;
 26381        } // Check vBox
 26382  
 26383  
 26384        if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
 26385          return true;
 26386        } // check non-rounded top side
 26387  
 26388  
 26389        var outerWidth = width / 2 + 2 * padding;
 26390        var outerHeight = height / 2 + 2 * padding;
 26391        var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
 26392  
 26393        if (pointInsidePolygonPoints(x, y, points)) {
 26394          return true;
 26395        } // Check bottom right quarter circle
 26396  
 26397  
 26398        if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
 26399          return true;
 26400        } // Check bottom left quarter circle
 26401  
 26402  
 26403        if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
 26404          return true;
 26405        }
 26406  
 26407        return false;
 26408      }
 26409    };
 26410  };
 26411  
 26412  BRp$d.registerNodeShapes = function () {
 26413    var nodeShapes = this.nodeShapes = {};
 26414    var renderer = this;
 26415    this.generateEllipse();
 26416    this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
 26417    this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
 26418    this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
 26419    nodeShapes['square'] = nodeShapes['rectangle'];
 26420    this.generateRoundRectangle();
 26421    this.generateCutRectangle();
 26422    this.generateBarrel();
 26423    this.generateBottomRoundrectangle();
 26424    {
 26425      var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
 26426      this.generatePolygon('diamond', diamondPoints);
 26427      this.generateRoundPolygon('round-diamond', diamondPoints);
 26428    }
 26429    this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
 26430    this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
 26431    this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
 26432    this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
 26433    this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
 26434    this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
 26435    this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
 26436    this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
 26437    var star5Points = new Array(20);
 26438    {
 26439      var outerPoints = generateUnitNgonPoints(5, 0);
 26440      var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
 26441  
 26442      var innerRadius = 0.5 * (3 - Math.sqrt(5));
 26443      innerRadius *= 1.57;
 26444  
 26445      for (var i = 0; i < innerPoints.length / 2; i++) {
 26446        innerPoints[i * 2] *= innerRadius;
 26447        innerPoints[i * 2 + 1] *= innerRadius;
 26448      }
 26449  
 26450      for (var i = 0; i < 20 / 4; i++) {
 26451        star5Points[i * 4] = outerPoints[i * 2];
 26452        star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
 26453        star5Points[i * 4 + 2] = innerPoints[i * 2];
 26454        star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
 26455      }
 26456    }
 26457    star5Points = fitPolygonToSquare(star5Points);
 26458    this.generatePolygon('star', star5Points);
 26459    this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
 26460    this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
 26461    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]);
 26462    {
 26463      var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
 26464      this.generatePolygon('tag', tagPoints);
 26465      this.generateRoundPolygon('round-tag', tagPoints);
 26466    }
 26467  
 26468    nodeShapes.makePolygon = function (points) {
 26469      // use caching on user-specified polygons so they are as fast as native shapes
 26470      var key = points.join('$');
 26471      var name = 'polygon-' + key;
 26472      var shape;
 26473  
 26474      if (shape = this[name]) {
 26475        // got cached shape
 26476        return shape;
 26477      } // create and cache new shape
 26478  
 26479  
 26480      return renderer.generatePolygon(name, points);
 26481    };
 26482  };
 26483  
 26484  var BRp$e = {};
 26485  
 26486  BRp$e.timeToRender = function () {
 26487    return this.redrawTotalTime / this.redrawCount;
 26488  };
 26489  
 26490  BRp$e.redraw = function (options) {
 26491    options = options || staticEmptyObject();
 26492    var r = this;
 26493  
 26494    if (r.averageRedrawTime === undefined) {
 26495      r.averageRedrawTime = 0;
 26496    }
 26497  
 26498    if (r.lastRedrawTime === undefined) {
 26499      r.lastRedrawTime = 0;
 26500    }
 26501  
 26502    if (r.lastDrawTime === undefined) {
 26503      r.lastDrawTime = 0;
 26504    }
 26505  
 26506    r.requestedFrame = true;
 26507    r.renderOptions = options;
 26508  };
 26509  
 26510  BRp$e.beforeRender = function (fn, priority) {
 26511    // the renderer can't add tick callbacks when destroyed
 26512    if (this.destroyed) {
 26513      return;
 26514    }
 26515  
 26516    if (priority == null) {
 26517      error('Priority is not optional for beforeRender');
 26518    }
 26519  
 26520    var cbs = this.beforeRenderCallbacks;
 26521    cbs.push({
 26522      fn: fn,
 26523      priority: priority
 26524    }); // higher priority callbacks executed first
 26525  
 26526    cbs.sort(function (a, b) {
 26527      return b.priority - a.priority;
 26528    });
 26529  };
 26530  
 26531  var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
 26532    var cbs = r.beforeRenderCallbacks;
 26533  
 26534    for (var i = 0; i < cbs.length; i++) {
 26535      cbs[i].fn(willDraw, startTime);
 26536    }
 26537  };
 26538  
 26539  BRp$e.startRenderLoop = function () {
 26540    var r = this;
 26541    var cy = r.cy;
 26542  
 26543    if (r.renderLoopStarted) {
 26544      return;
 26545    } else {
 26546      r.renderLoopStarted = true;
 26547    }
 26548  
 26549    var renderFn = function renderFn(requestTime) {
 26550      if (r.destroyed) {
 26551        return;
 26552      }
 26553  
 26554      if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
 26555        beforeRenderCallbacks(r, true, requestTime);
 26556        var startTime = performanceNow();
 26557        r.render(r.renderOptions);
 26558        var endTime = r.lastDrawTime = performanceNow();
 26559  
 26560        if (r.averageRedrawTime === undefined) {
 26561          r.averageRedrawTime = endTime - startTime;
 26562        }
 26563  
 26564        if (r.redrawCount === undefined) {
 26565          r.redrawCount = 0;
 26566        }
 26567  
 26568        r.redrawCount++;
 26569  
 26570        if (r.redrawTotalTime === undefined) {
 26571          r.redrawTotalTime = 0;
 26572        }
 26573  
 26574        var duration = endTime - startTime;
 26575        r.redrawTotalTime += duration;
 26576        r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
 26577  
 26578        r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
 26579        r.requestedFrame = false;
 26580      } else {
 26581        beforeRenderCallbacks(r, false, requestTime);
 26582      }
 26583  
 26584      r.skipFrame = false;
 26585      requestAnimationFrame(renderFn);
 26586    };
 26587  
 26588    requestAnimationFrame(renderFn);
 26589  };
 26590  
 26591  var BaseRenderer = function BaseRenderer(options) {
 26592    this.init(options);
 26593  };
 26594  
 26595  var BR = BaseRenderer;
 26596  var BRp$f = BR.prototype;
 26597  BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
 26598  
 26599  BRp$f.init = function (options) {
 26600    var r = this;
 26601    r.options = options;
 26602    r.cy = options.cy;
 26603    var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
 26604  
 26605    if (window$1) {
 26606      var document = window$1.document;
 26607      var head = document.head;
 26608      var stylesheetId = '__________cytoscape_stylesheet';
 26609      var className = '__________cytoscape_container';
 26610      var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
 26611  
 26612      if (ctr.className.indexOf(className) < 0) {
 26613        ctr.className = (ctr.className || '') + ' ' + className;
 26614      }
 26615  
 26616      if (!stylesheetAlreadyExists) {
 26617        var stylesheet = document.createElement('style');
 26618        stylesheet.id = stylesheetId;
 26619        stylesheet.innerHTML = '.' + className + ' { position: relative; }';
 26620        head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
 26621      }
 26622  
 26623      var computedStyle = window$1.getComputedStyle(ctr);
 26624      var position = computedStyle.getPropertyValue('position');
 26625  
 26626      if (position === 'static') {
 26627        warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
 26628      }
 26629    }
 26630  
 26631    r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
 26632  
 26633    r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
 26634  
 26635    r.hoverData = {
 26636      down: null,
 26637      last: null,
 26638      downTime: null,
 26639      triggerMode: null,
 26640      dragging: false,
 26641      initialPan: [null, null],
 26642      capture: false
 26643    };
 26644    r.dragData = {
 26645      possibleDragElements: []
 26646    };
 26647    r.touchData = {
 26648      start: null,
 26649      capture: false,
 26650      // These 3 fields related to tap, taphold events
 26651      startPosition: [null, null, null, null, null, null],
 26652      singleTouchStartTime: null,
 26653      singleTouchMoved: true,
 26654      now: [null, null, null, null, null, null],
 26655      earlier: [null, null, null, null, null, null]
 26656    };
 26657    r.redraws = 0;
 26658    r.showFps = options.showFps;
 26659    r.debug = options.debug;
 26660    r.hideEdgesOnViewport = options.hideEdgesOnViewport;
 26661    r.textureOnViewport = options.textureOnViewport;
 26662    r.wheelSensitivity = options.wheelSensitivity;
 26663    r.motionBlurEnabled = options.motionBlur; // on by default
 26664  
 26665    r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
 26666    r.motionBlur = options.motionBlur; // for initial kick off
 26667  
 26668    r.motionBlurOpacity = options.motionBlurOpacity;
 26669    r.motionBlurTransparency = 1 - r.motionBlurOpacity;
 26670    r.motionBlurPxRatio = 1;
 26671    r.mbPxRBlurry = 1; //0.8;
 26672  
 26673    r.minMbLowQualFrames = 4;
 26674    r.fullQualityMb = false;
 26675    r.clearedForMotionBlur = [];
 26676    r.desktopTapThreshold = options.desktopTapThreshold;
 26677    r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
 26678    r.touchTapThreshold = options.touchTapThreshold;
 26679    r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
 26680    r.tapholdDuration = 500;
 26681    r.bindings = [];
 26682    r.beforeRenderCallbacks = [];
 26683    r.beforeRenderPriorities = {
 26684      // higher priority execs before lower one
 26685      animations: 400,
 26686      eleCalcs: 300,
 26687      eleTxrDeq: 200,
 26688      lyrTxrDeq: 150,
 26689      lyrTxrSkip: 100
 26690    };
 26691    r.registerNodeShapes();
 26692    r.registerArrowShapes();
 26693    r.registerCalculationListeners();
 26694  };
 26695  
 26696  BRp$f.notify = function (eventName, eles) {
 26697    var r = this;
 26698    var cy = r.cy; // the renderer can't be notified after it's destroyed
 26699  
 26700    if (this.destroyed) {
 26701      return;
 26702    }
 26703  
 26704    if (eventName === 'init') {
 26705      r.load();
 26706      return;
 26707    }
 26708  
 26709    if (eventName === 'destroy') {
 26710      r.destroy();
 26711      return;
 26712    }
 26713  
 26714    if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
 26715      r.invalidateCachedZSortedEles();
 26716    }
 26717  
 26718    if (eventName === 'viewport') {
 26719      r.redrawHint('select', true);
 26720    }
 26721  
 26722    if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
 26723      r.invalidateContainerClientCoordsCache();
 26724      r.matchCanvasSize(r.container);
 26725    }
 26726  
 26727    r.redrawHint('eles', true);
 26728    r.redrawHint('drag', true);
 26729    this.startRenderLoop();
 26730    this.redraw();
 26731  };
 26732  
 26733  BRp$f.destroy = function () {
 26734    var r = this;
 26735    r.destroyed = true;
 26736    r.cy.stopAnimationLoop();
 26737  
 26738    for (var i = 0; i < r.bindings.length; i++) {
 26739      var binding = r.bindings[i];
 26740      var b = binding;
 26741      var tgt = b.target;
 26742      (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
 26743    }
 26744  
 26745    r.bindings = [];
 26746    r.beforeRenderCallbacks = [];
 26747    r.onUpdateEleCalcsFns = [];
 26748  
 26749    if (r.removeObserver) {
 26750      r.removeObserver.disconnect();
 26751    }
 26752  
 26753    if (r.styleObserver) {
 26754      r.styleObserver.disconnect();
 26755    }
 26756  
 26757    if (r.resizeObserver) {
 26758      r.resizeObserver.disconnect();
 26759    }
 26760  
 26761    if (r.labelCalcDiv) {
 26762      try {
 26763        document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
 26764      } catch (e) {// ie10 issue #1014
 26765      }
 26766    }
 26767  };
 26768  
 26769  BRp$f.isHeadless = function () {
 26770    return false;
 26771  };
 26772  
 26773  [BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
 26774    extend(BRp$f, props);
 26775  });
 26776  
 26777  var fullFpsTime = 1000 / 60; // assume 60 frames per second
 26778  
 26779  var defs = {
 26780    setupDequeueing: function setupDequeueing(opts) {
 26781      return function setupDequeueingImpl() {
 26782        var self = this;
 26783        var r = this.renderer;
 26784  
 26785        if (self.dequeueingSetup) {
 26786          return;
 26787        } else {
 26788          self.dequeueingSetup = true;
 26789        }
 26790  
 26791        var queueRedraw = util(function () {
 26792          r.redrawHint('eles', true);
 26793          r.redrawHint('drag', true);
 26794          r.redraw();
 26795        }, opts.deqRedrawThreshold);
 26796  
 26797        var dequeue = function dequeue(willDraw, frameStartTime) {
 26798          var startTime = performanceNow();
 26799          var avgRenderTime = r.averageRedrawTime;
 26800          var renderTime = r.lastRedrawTime;
 26801          var deqd = [];
 26802          var extent = r.cy.extent();
 26803          var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
 26804          // queue won't automatically be flushed before dequeueing starts
 26805  
 26806          if (!willDraw) {
 26807            r.flushRenderedStyleQueue();
 26808          }
 26809  
 26810          while (true) {
 26811            // eslint-disable-line no-constant-condition
 26812            var now = performanceNow();
 26813            var duration = now - startTime;
 26814            var frameDuration = now - frameStartTime;
 26815  
 26816            if (renderTime < fullFpsTime) {
 26817              // if we're rendering faster than the ideal fps, then do dequeueing
 26818              // during all of the remaining frame time
 26819              var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
 26820  
 26821              if (frameDuration >= opts.deqFastCost * timeAvailable) {
 26822                break;
 26823              }
 26824            } else {
 26825              if (willDraw) {
 26826                if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
 26827                  break;
 26828                }
 26829              } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
 26830                break;
 26831              }
 26832            }
 26833  
 26834            var thisDeqd = opts.deq(self, pixelRatio, extent);
 26835  
 26836            if (thisDeqd.length > 0) {
 26837              for (var i = 0; i < thisDeqd.length; i++) {
 26838                deqd.push(thisDeqd[i]);
 26839              }
 26840            } else {
 26841              break;
 26842            }
 26843          } // callbacks on dequeue
 26844  
 26845  
 26846          if (deqd.length > 0) {
 26847            opts.onDeqd(self, deqd);
 26848  
 26849            if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
 26850              queueRedraw();
 26851            }
 26852          }
 26853        };
 26854  
 26855        var priority = opts.priority || noop;
 26856        r.beforeRender(dequeue, priority(self));
 26857      };
 26858    }
 26859  };
 26860  
 26861  // Uses keys so elements may share the same cache.
 26862  
 26863  var ElementTextureCacheLookup =
 26864  /*#__PURE__*/
 26865  function () {
 26866    function ElementTextureCacheLookup(getKey) {
 26867      var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
 26868  
 26869      _classCallCheck(this, ElementTextureCacheLookup);
 26870  
 26871      this.idsByKey = new Map$1();
 26872      this.keyForId = new Map$1();
 26873      this.cachesByLvl = new Map$1();
 26874      this.lvls = [];
 26875      this.getKey = getKey;
 26876      this.doesEleInvalidateKey = doesEleInvalidateKey;
 26877    }
 26878  
 26879    _createClass(ElementTextureCacheLookup, [{
 26880      key: "getIdsFor",
 26881      value: function getIdsFor(key) {
 26882        if (key == null) {
 26883          error("Can not get id list for null key");
 26884        }
 26885  
 26886        var idsByKey = this.idsByKey;
 26887        var ids = this.idsByKey.get(key);
 26888  
 26889        if (!ids) {
 26890          ids = new Set$1();
 26891          idsByKey.set(key, ids);
 26892        }
 26893  
 26894        return ids;
 26895      }
 26896    }, {
 26897      key: "addIdForKey",
 26898      value: function addIdForKey(key, id) {
 26899        if (key != null) {
 26900          this.getIdsFor(key).add(id);
 26901        }
 26902      }
 26903    }, {
 26904      key: "deleteIdForKey",
 26905      value: function deleteIdForKey(key, id) {
 26906        if (key != null) {
 26907          this.getIdsFor(key)["delete"](id);
 26908        }
 26909      }
 26910    }, {
 26911      key: "getNumberOfIdsForKey",
 26912      value: function getNumberOfIdsForKey(key) {
 26913        if (key == null) {
 26914          return 0;
 26915        } else {
 26916          return this.getIdsFor(key).size;
 26917        }
 26918      }
 26919    }, {
 26920      key: "updateKeyMappingFor",
 26921      value: function updateKeyMappingFor(ele) {
 26922        var id = ele.id();
 26923        var prevKey = this.keyForId.get(id);
 26924        var currKey = this.getKey(ele);
 26925        this.deleteIdForKey(prevKey, id);
 26926        this.addIdForKey(currKey, id);
 26927        this.keyForId.set(id, currKey);
 26928      }
 26929    }, {
 26930      key: "deleteKeyMappingFor",
 26931      value: function deleteKeyMappingFor(ele) {
 26932        var id = ele.id();
 26933        var prevKey = this.keyForId.get(id);
 26934        this.deleteIdForKey(prevKey, id);
 26935        this.keyForId["delete"](id);
 26936      }
 26937    }, {
 26938      key: "keyHasChangedFor",
 26939      value: function keyHasChangedFor(ele) {
 26940        var id = ele.id();
 26941        var prevKey = this.keyForId.get(id);
 26942        var newKey = this.getKey(ele);
 26943        return prevKey !== newKey;
 26944      }
 26945    }, {
 26946      key: "isInvalid",
 26947      value: function isInvalid(ele) {
 26948        return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
 26949      }
 26950    }, {
 26951      key: "getCachesAt",
 26952      value: function getCachesAt(lvl) {
 26953        var cachesByLvl = this.cachesByLvl,
 26954            lvls = this.lvls;
 26955        var caches = cachesByLvl.get(lvl);
 26956  
 26957        if (!caches) {
 26958          caches = new Map$1();
 26959          cachesByLvl.set(lvl, caches);
 26960          lvls.push(lvl);
 26961        }
 26962  
 26963        return caches;
 26964      }
 26965    }, {
 26966      key: "getCache",
 26967      value: function getCache(key, lvl) {
 26968        return this.getCachesAt(lvl).get(key);
 26969      }
 26970    }, {
 26971      key: "get",
 26972      value: function get(ele, lvl) {
 26973        var key = this.getKey(ele);
 26974        var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
 26975  
 26976        if (cache != null) {
 26977          this.updateKeyMappingFor(ele);
 26978        }
 26979  
 26980        return cache;
 26981      }
 26982    }, {
 26983      key: "getForCachedKey",
 26984      value: function getForCachedKey(ele, lvl) {
 26985        var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
 26986  
 26987        var cache = this.getCache(key, lvl);
 26988        return cache;
 26989      }
 26990    }, {
 26991      key: "hasCache",
 26992      value: function hasCache(key, lvl) {
 26993        return this.getCachesAt(lvl).has(key);
 26994      }
 26995    }, {
 26996      key: "has",
 26997      value: function has(ele, lvl) {
 26998        var key = this.getKey(ele);
 26999        return this.hasCache(key, lvl);
 27000      }
 27001    }, {
 27002      key: "setCache",
 27003      value: function setCache(key, lvl, cache) {
 27004        cache.key = key;
 27005        this.getCachesAt(lvl).set(key, cache);
 27006      }
 27007    }, {
 27008      key: "set",
 27009      value: function set(ele, lvl, cache) {
 27010        var key = this.getKey(ele);
 27011        this.setCache(key, lvl, cache);
 27012        this.updateKeyMappingFor(ele);
 27013      }
 27014    }, {
 27015      key: "deleteCache",
 27016      value: function deleteCache(key, lvl) {
 27017        this.getCachesAt(lvl)["delete"](key);
 27018      }
 27019    }, {
 27020      key: "delete",
 27021      value: function _delete(ele, lvl) {
 27022        var key = this.getKey(ele);
 27023        this.deleteCache(key, lvl);
 27024      }
 27025    }, {
 27026      key: "invalidateKey",
 27027      value: function invalidateKey(key) {
 27028        var _this = this;
 27029  
 27030        this.lvls.forEach(function (lvl) {
 27031          return _this.deleteCache(key, lvl);
 27032        });
 27033      } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
 27034  
 27035    }, {
 27036      key: "invalidate",
 27037      value: function invalidate(ele) {
 27038        var id = ele.id();
 27039        var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
 27040  
 27041        this.deleteKeyMappingFor(ele);
 27042        var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
 27043  
 27044        if (entireKeyInvalidated) {
 27045          // clear mapping for current key
 27046          this.invalidateKey(key);
 27047        }
 27048  
 27049        return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
 27050      }
 27051    }]);
 27052  
 27053    return ElementTextureCacheLookup;
 27054  }();
 27055  
 27056  var minTxrH = 25; // the size of the texture cache for small height eles (special case)
 27057  
 27058  var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
 27059  
 27060  var minLvl = -4; // when scaling smaller than that we don't need to re-render
 27061  
 27062  var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
 27063  
 27064  var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
 27065  
 27066  var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
 27067  
 27068  var defTxrWidth = 1024; // default/minimum texture width
 27069  
 27070  var maxTxrW = 1024; // the maximum width of a texture
 27071  
 27072  var maxTxrH = 1024; // the maximum height of a texture
 27073  
 27074  var minUtility = 0.2; // if usage of texture is less than this, it is retired
 27075  
 27076  var maxFullness = 0.8; // fullness of texture after which queue removal is checked
 27077  
 27078  var maxFullnessChecks = 10; // dequeued after this many checks
 27079  
 27080  var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
 27081  
 27082  var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
 27083  
 27084  var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
 27085  
 27086  var deqFastCost = 0.9; // % of frame time to be used when >60fps
 27087  
 27088  var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
 27089  
 27090  var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
 27091  
 27092  var getTxrReasons = {
 27093    dequeue: 'dequeue',
 27094    downscale: 'downscale',
 27095    highQuality: 'highQuality'
 27096  };
 27097  var initDefaults = defaults({
 27098    getKey: null,
 27099    doesEleInvalidateKey: falsify,
 27100    drawElement: null,
 27101    getBoundingBox: null,
 27102    getRotationPoint: null,
 27103    getRotationOffset: null,
 27104    isVisible: trueify,
 27105    allowEdgeTxrCaching: true,
 27106    allowParentTxrCaching: true
 27107  });
 27108  
 27109  var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
 27110    var self = this;
 27111    self.renderer = renderer;
 27112    self.onDequeues = [];
 27113    var opts = initDefaults(initOptions);
 27114    extend(self, opts);
 27115    self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
 27116    self.setupDequeueing();
 27117  };
 27118  
 27119  var ETCp = ElementTextureCache.prototype;
 27120  ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
 27121  
 27122  ETCp.getTextureQueue = function (txrH) {
 27123    var self = this;
 27124    self.eleImgCaches = self.eleImgCaches || {};
 27125    return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
 27126  }; // the list of usused textures which can be recycled (in use in texture queue)
 27127  
 27128  
 27129  ETCp.getRetiredTextureQueue = function (txrH) {
 27130    var self = this;
 27131    var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
 27132    var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
 27133    return rtxtrQ;
 27134  }; // queue of element draw requests at different scale levels
 27135  
 27136  
 27137  ETCp.getElementQueue = function () {
 27138    var self = this;
 27139    var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) {
 27140      return b.reqs - a.reqs;
 27141    });
 27142    return q;
 27143  }; // queue of element draw requests at different scale levels (element id lookup)
 27144  
 27145  
 27146  ETCp.getElementKeyToQueue = function () {
 27147    var self = this;
 27148    var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
 27149    return k2q;
 27150  };
 27151  
 27152  ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
 27153    var self = this;
 27154    var r = this.renderer;
 27155    var zoom = r.cy.zoom();
 27156    var lookup = this.lookup;
 27157  
 27158    if (bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible()) {
 27159      return null;
 27160    }
 27161  
 27162    if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
 27163      return null;
 27164    }
 27165  
 27166    if (lvl == null) {
 27167      lvl = Math.ceil(log2(zoom * pxRatio));
 27168    }
 27169  
 27170    if (lvl < minLvl) {
 27171      lvl = minLvl;
 27172    } else if (zoom >= maxZoom || lvl > maxLvl) {
 27173      return null;
 27174    }
 27175  
 27176    var scale = Math.pow(2, lvl);
 27177    var eleScaledH = bb.h * scale;
 27178    var eleScaledW = bb.w * scale;
 27179    var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
 27180  
 27181    if (!this.isVisible(ele, scaledLabelShown)) {
 27182      return null;
 27183    }
 27184  
 27185    var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
 27186  
 27187    if (eleCache && eleCache.invalidated) {
 27188      eleCache.invalidated = false;
 27189      eleCache.texture.invalidatedWidth -= eleCache.width;
 27190    }
 27191  
 27192    if (eleCache) {
 27193      return eleCache;
 27194    }
 27195  
 27196    var txrH; // which texture height this ele belongs to
 27197  
 27198    if (eleScaledH <= minTxrH) {
 27199      txrH = minTxrH;
 27200    } else if (eleScaledH <= txrStepH) {
 27201      txrH = txrStepH;
 27202    } else {
 27203      txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
 27204    }
 27205  
 27206    if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
 27207      return null; // caching large elements is not efficient
 27208    }
 27209  
 27210    var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
 27211  
 27212    var txr = txrQ[txrQ.length - 2];
 27213  
 27214    var addNewTxr = function addNewTxr() {
 27215      return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
 27216    }; // try the last one if there is no second last one
 27217  
 27218  
 27219    if (!txr) {
 27220      txr = txrQ[txrQ.length - 1];
 27221    } // if the last one doesn't exist, we need a first one
 27222  
 27223  
 27224    if (!txr) {
 27225      txr = addNewTxr();
 27226    } // if there's no room in the current texture, we need a new one
 27227  
 27228  
 27229    if (txr.width - txr.usedWidth < eleScaledW) {
 27230      txr = addNewTxr();
 27231    }
 27232  
 27233    var scalableFrom = function scalableFrom(otherCache) {
 27234      return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
 27235    };
 27236  
 27237    var deqing = reason && reason === getTxrReasons.dequeue;
 27238    var highQualityReq = reason && reason === getTxrReasons.highQuality;
 27239    var downscaleReq = reason && reason === getTxrReasons.downscale;
 27240    var higherCache; // the nearest cache with a higher level
 27241  
 27242    for (var l = lvl + 1; l <= maxLvl; l++) {
 27243      var c = lookup.get(ele, l);
 27244  
 27245      if (c) {
 27246        higherCache = c;
 27247        break;
 27248      }
 27249    }
 27250  
 27251    var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
 27252  
 27253    var downscale = function downscale() {
 27254      txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
 27255    }; // reset ele area in texture
 27256  
 27257  
 27258    txr.context.setTransform(1, 0, 0, 1, 0, 0);
 27259    txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
 27260  
 27261    if (scalableFrom(oneUpCache)) {
 27262      // then we can relatively cheaply rescale the existing image w/o rerendering
 27263      downscale();
 27264    } else if (scalableFrom(higherCache)) {
 27265      // then use the higher cache for now and queue the next level down
 27266      // to cheaply scale towards the smaller level
 27267      if (highQualityReq) {
 27268        for (var _l = higherCache.level; _l > lvl; _l--) {
 27269          oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
 27270        }
 27271  
 27272        downscale();
 27273      } else {
 27274        self.queueElement(ele, higherCache.level - 1);
 27275        return higherCache;
 27276      }
 27277    } else {
 27278      var lowerCache; // the nearest cache with a lower level
 27279  
 27280      if (!deqing && !highQualityReq && !downscaleReq) {
 27281        for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
 27282          var _c = lookup.get(ele, _l2);
 27283  
 27284          if (_c) {
 27285            lowerCache = _c;
 27286            break;
 27287          }
 27288        }
 27289      }
 27290  
 27291      if (scalableFrom(lowerCache)) {
 27292        // then use the lower quality cache for now and queue the better one for later
 27293        self.queueElement(ele, lvl);
 27294        return lowerCache;
 27295      }
 27296  
 27297      txr.context.translate(txr.usedWidth, 0);
 27298      txr.context.scale(scale, scale);
 27299      this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
 27300      txr.context.scale(1 / scale, 1 / scale);
 27301      txr.context.translate(-txr.usedWidth, 0);
 27302    }
 27303  
 27304    eleCache = {
 27305      x: txr.usedWidth,
 27306      texture: txr,
 27307      level: lvl,
 27308      scale: scale,
 27309      width: eleScaledW,
 27310      height: eleScaledH,
 27311      scaledLabelShown: scaledLabelShown
 27312    };
 27313    txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
 27314    txr.eleCaches.push(eleCache);
 27315    lookup.set(ele, lvl, eleCache);
 27316    self.checkTextureFullness(txr);
 27317    return eleCache;
 27318  };
 27319  
 27320  ETCp.invalidateElements = function (eles) {
 27321    for (var i = 0; i < eles.length; i++) {
 27322      this.invalidateElement(eles[i]);
 27323    }
 27324  };
 27325  
 27326  ETCp.invalidateElement = function (ele) {
 27327    var self = this;
 27328    var lookup = self.lookup;
 27329    var caches = [];
 27330    var invalid = lookup.isInvalid(ele);
 27331  
 27332    if (!invalid) {
 27333      return; // override the invalidation request if the element key has not changed
 27334    }
 27335  
 27336    for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
 27337      var cache = lookup.getForCachedKey(ele, lvl);
 27338  
 27339      if (cache) {
 27340        caches.push(cache);
 27341      }
 27342    }
 27343  
 27344    var noOtherElesUseCache = lookup.invalidate(ele);
 27345  
 27346    if (noOtherElesUseCache) {
 27347      for (var i = 0; i < caches.length; i++) {
 27348        var _cache = caches[i];
 27349        var txr = _cache.texture; // remove space from the texture it belongs to
 27350  
 27351        txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
 27352  
 27353        _cache.invalidated = true; // retire the texture if its utility is low
 27354  
 27355        self.checkTextureUtility(txr);
 27356      }
 27357    } // remove from queue since the old req was for the old state
 27358  
 27359  
 27360    self.removeFromQueue(ele);
 27361  };
 27362  
 27363  ETCp.checkTextureUtility = function (txr) {
 27364    // invalidate all entries in the cache if the cache size is small
 27365    if (txr.invalidatedWidth >= minUtility * txr.width) {
 27366      this.retireTexture(txr);
 27367    }
 27368  };
 27369  
 27370  ETCp.checkTextureFullness = function (txr) {
 27371    // if texture has been mostly filled and passed over several times, remove
 27372    // it from the queue so we don't need to waste time looking at it to put new things
 27373    var self = this;
 27374    var txrQ = self.getTextureQueue(txr.height);
 27375  
 27376    if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
 27377      removeFromArray(txrQ, txr);
 27378    } else {
 27379      txr.fullnessChecks++;
 27380    }
 27381  };
 27382  
 27383  ETCp.retireTexture = function (txr) {
 27384    var self = this;
 27385    var txrH = txr.height;
 27386    var txrQ = self.getTextureQueue(txrH);
 27387    var lookup = this.lookup; // retire the texture from the active / searchable queue:
 27388  
 27389    removeFromArray(txrQ, txr);
 27390    txr.retired = true; // remove the refs from the eles to the caches:
 27391  
 27392    var eleCaches = txr.eleCaches;
 27393  
 27394    for (var i = 0; i < eleCaches.length; i++) {
 27395      var eleCache = eleCaches[i];
 27396      lookup.deleteCache(eleCache.key, eleCache.level);
 27397    }
 27398  
 27399    clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
 27400  
 27401    var rtxtrQ = self.getRetiredTextureQueue(txrH);
 27402    rtxtrQ.push(txr);
 27403  };
 27404  
 27405  ETCp.addTexture = function (txrH, minW) {
 27406    var self = this;
 27407    var txrQ = self.getTextureQueue(txrH);
 27408    var txr = {};
 27409    txrQ.push(txr);
 27410    txr.eleCaches = [];
 27411    txr.height = txrH;
 27412    txr.width = Math.max(defTxrWidth, minW);
 27413    txr.usedWidth = 0;
 27414    txr.invalidatedWidth = 0;
 27415    txr.fullnessChecks = 0;
 27416    txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
 27417    txr.context = txr.canvas.getContext('2d');
 27418    return txr;
 27419  };
 27420  
 27421  ETCp.recycleTexture = function (txrH, minW) {
 27422    var self = this;
 27423    var txrQ = self.getTextureQueue(txrH);
 27424    var rtxtrQ = self.getRetiredTextureQueue(txrH);
 27425  
 27426    for (var i = 0; i < rtxtrQ.length; i++) {
 27427      var txr = rtxtrQ[i];
 27428  
 27429      if (txr.width >= minW) {
 27430        txr.retired = false;
 27431        txr.usedWidth = 0;
 27432        txr.invalidatedWidth = 0;
 27433        txr.fullnessChecks = 0;
 27434        clearArray(txr.eleCaches);
 27435        txr.context.setTransform(1, 0, 0, 1, 0, 0);
 27436        txr.context.clearRect(0, 0, txr.width, txr.height);
 27437        removeFromArray(rtxtrQ, txr);
 27438        txrQ.push(txr);
 27439        return txr;
 27440      }
 27441    }
 27442  };
 27443  
 27444  ETCp.queueElement = function (ele, lvl) {
 27445    var self = this;
 27446    var q = self.getElementQueue();
 27447    var k2q = self.getElementKeyToQueue();
 27448    var key = this.getKey(ele);
 27449    var existingReq = k2q[key];
 27450  
 27451    if (existingReq) {
 27452      // use the max lvl b/c in between lvls are cheap to make
 27453      existingReq.level = Math.max(existingReq.level, lvl);
 27454      existingReq.eles.merge(ele);
 27455      existingReq.reqs++;
 27456      q.updateItem(existingReq);
 27457    } else {
 27458      var req = {
 27459        eles: ele.spawn().merge(ele),
 27460        level: lvl,
 27461        reqs: 1,
 27462        key: key
 27463      };
 27464      q.push(req);
 27465      k2q[key] = req;
 27466    }
 27467  };
 27468  
 27469  ETCp.dequeue = function (pxRatio
 27470  /*, extent*/
 27471  ) {
 27472    var self = this;
 27473    var q = self.getElementQueue();
 27474    var k2q = self.getElementKeyToQueue();
 27475    var dequeued = [];
 27476    var lookup = self.lookup;
 27477  
 27478    for (var i = 0; i < maxDeqSize; i++) {
 27479      if (q.size() > 0) {
 27480        var req = q.pop();
 27481        var key = req.key;
 27482        var ele = req.eles[0]; // all eles have the same key
 27483  
 27484        var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
 27485  
 27486        k2q[key] = null; // dequeueing isn't necessary with an existing cache
 27487  
 27488        if (cacheExists) {
 27489          continue;
 27490        }
 27491  
 27492        dequeued.push(req);
 27493        var bb = self.getBoundingBox(ele);
 27494        self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
 27495      } else {
 27496        break;
 27497      }
 27498    }
 27499  
 27500    return dequeued;
 27501  };
 27502  
 27503  ETCp.removeFromQueue = function (ele) {
 27504    var self = this;
 27505    var q = self.getElementQueue();
 27506    var k2q = self.getElementKeyToQueue();
 27507    var key = this.getKey(ele);
 27508    var req = k2q[key];
 27509  
 27510    if (req != null) {
 27511      if (req.eles.length === 1) {
 27512        // remove if last ele in the req
 27513        // bring to front of queue
 27514        req.reqs = MAX_INT;
 27515        q.updateItem(req);
 27516        q.pop(); // remove from queue
 27517  
 27518        k2q[key] = null; // remove from lookup map
 27519      } else {
 27520        // otherwise just remove ele from req
 27521        req.eles.unmerge(ele);
 27522      }
 27523    }
 27524  };
 27525  
 27526  ETCp.onDequeue = function (fn) {
 27527    this.onDequeues.push(fn);
 27528  };
 27529  
 27530  ETCp.offDequeue = function (fn) {
 27531    removeFromArray(this.onDequeues, fn);
 27532  };
 27533  
 27534  ETCp.setupDequeueing = defs.setupDequeueing({
 27535    deqRedrawThreshold: deqRedrawThreshold,
 27536    deqCost: deqCost,
 27537    deqAvgCost: deqAvgCost,
 27538    deqNoDrawCost: deqNoDrawCost,
 27539    deqFastCost: deqFastCost,
 27540    deq: function deq(self, pxRatio, extent) {
 27541      return self.dequeue(pxRatio, extent);
 27542    },
 27543    onDeqd: function onDeqd(self, deqd) {
 27544      for (var i = 0; i < self.onDequeues.length; i++) {
 27545        var fn = self.onDequeues[i];
 27546        fn(deqd);
 27547      }
 27548    },
 27549    shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
 27550      for (var i = 0; i < deqd.length; i++) {
 27551        var eles = deqd[i].eles;
 27552  
 27553        for (var j = 0; j < eles.length; j++) {
 27554          var bb = eles[j].boundingBox();
 27555  
 27556          if (boundingBoxesIntersect(bb, extent)) {
 27557            return true;
 27558          }
 27559        }
 27560      }
 27561  
 27562      return false;
 27563    },
 27564    priority: function priority(self) {
 27565      return self.renderer.beforeRenderPriorities.eleTxrDeq;
 27566    }
 27567  });
 27568  
 27569  var defNumLayers = 1; // default number of layers to use
 27570  
 27571  var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
 27572  
 27573  var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
 27574  
 27575  var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
 27576  
 27577  var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
 27578  
 27579  var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
 27580  
 27581  var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
 27582  
 27583  var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
 27584  
 27585  var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
 27586  
 27587  var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
 27588  
 27589  var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
 27590  
 27591  var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
 27592  
 27593  var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
 27594  
 27595  var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
 27596  // var log = function(){ console.log.apply( console, arguments ); };
 27597  
 27598  var LayeredTextureCache = function LayeredTextureCache(renderer) {
 27599    var self = this;
 27600    var r = self.renderer = renderer;
 27601    var cy = r.cy;
 27602    self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
 27603  
 27604    self.firstGet = true;
 27605    self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
 27606    self.skipping = false;
 27607    self.eleTxrDeqs = cy.collection();
 27608    self.scheduleElementRefinement = util(function () {
 27609      self.refineElementTextures(self.eleTxrDeqs);
 27610      self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
 27611    }, refineEleDebounceTime);
 27612    r.beforeRender(function (willDraw, now) {
 27613      if (now - self.lastInvalidationTime <= invalidThreshold) {
 27614        self.skipping = true;
 27615      } else {
 27616        self.skipping = false;
 27617      }
 27618    }, r.beforeRenderPriorities.lyrTxrSkip);
 27619  
 27620    var qSort = function qSort(a, b) {
 27621      return b.reqs - a.reqs;
 27622    };
 27623  
 27624    self.layersQueue = new Heap(qSort);
 27625    self.setupDequeueing();
 27626  };
 27627  
 27628  var LTCp = LayeredTextureCache.prototype;
 27629  var layerIdPool = 0;
 27630  var MAX_INT$1 = Math.pow(2, 53) - 1;
 27631  
 27632  LTCp.makeLayer = function (bb, lvl) {
 27633    var scale = Math.pow(2, lvl);
 27634    var w = Math.ceil(bb.w * scale);
 27635    var h = Math.ceil(bb.h * scale);
 27636    var canvas = this.renderer.makeOffscreenCanvas(w, h);
 27637    var layer = {
 27638      id: layerIdPool = ++layerIdPool % MAX_INT$1,
 27639      bb: bb,
 27640      level: lvl,
 27641      width: w,
 27642      height: h,
 27643      canvas: canvas,
 27644      context: canvas.getContext('2d'),
 27645      eles: [],
 27646      elesQueue: [],
 27647      reqs: 0
 27648    }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
 27649  
 27650    var cxt = layer.context;
 27651    var dx = -layer.bb.x1;
 27652    var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
 27653  
 27654    cxt.scale(scale, scale);
 27655    cxt.translate(dx, dy);
 27656    return layer;
 27657  };
 27658  
 27659  LTCp.getLayers = function (eles, pxRatio, lvl) {
 27660    var self = this;
 27661    var r = self.renderer;
 27662    var cy = r.cy;
 27663    var zoom = cy.zoom();
 27664    var firstGet = self.firstGet;
 27665    self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
 27666    //log eles.map(function(ele){ return ele.id() }) );
 27667  
 27668    if (lvl == null) {
 27669      lvl = Math.ceil(log2(zoom * pxRatio));
 27670  
 27671      if (lvl < minLvl$1) {
 27672        lvl = minLvl$1;
 27673      } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
 27674        return null;
 27675      }
 27676    }
 27677  
 27678    self.validateLayersElesOrdering(lvl, eles);
 27679    var layersByLvl = self.layersByLevel;
 27680    var scale = Math.pow(2, lvl);
 27681    var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
 27682    var bb;
 27683    var lvlComplete = self.levelIsComplete(lvl, eles);
 27684    var tmpLayers;
 27685  
 27686    var checkTempLevels = function checkTempLevels() {
 27687      var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
 27688        self.validateLayersElesOrdering(l, eles);
 27689  
 27690        if (self.levelIsComplete(l, eles)) {
 27691          tmpLayers = layersByLvl[l];
 27692          return true;
 27693        }
 27694      };
 27695  
 27696      var checkLvls = function checkLvls(dir) {
 27697        if (tmpLayers) {
 27698          return;
 27699        }
 27700  
 27701        for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
 27702          if (canUseAsTmpLvl(l)) {
 27703            break;
 27704          }
 27705        }
 27706      };
 27707  
 27708      checkLvls(+1);
 27709      checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
 27710  
 27711      for (var i = layers.length - 1; i >= 0; i--) {
 27712        var layer = layers[i];
 27713  
 27714        if (layer.invalid) {
 27715          removeFromArray(layers, layer);
 27716        }
 27717      }
 27718    };
 27719  
 27720    if (!lvlComplete) {
 27721      // if the current level is incomplete, then use the closest, best quality layerset temporarily
 27722      // and later queue the current layerset so we can get the proper quality level soon
 27723      checkTempLevels();
 27724    } else {
 27725      // log('level complete, using existing layers\n--');
 27726      return layers;
 27727    }
 27728  
 27729    var getBb = function getBb() {
 27730      if (!bb) {
 27731        bb = makeBoundingBox();
 27732  
 27733        for (var i = 0; i < eles.length; i++) {
 27734          updateBoundingBox(bb, eles[i].boundingBox());
 27735        }
 27736      }
 27737  
 27738      return bb;
 27739    };
 27740  
 27741    var makeLayer = function makeLayer(opts) {
 27742      opts = opts || {};
 27743      var after = opts.after;
 27744      getBb();
 27745      var area = bb.w * scale * (bb.h * scale);
 27746  
 27747      if (area > maxLayerArea) {
 27748        return null;
 27749      }
 27750  
 27751      var layer = self.makeLayer(bb, lvl);
 27752  
 27753      if (after != null) {
 27754        var index = layers.indexOf(after) + 1;
 27755        layers.splice(index, 0, layer);
 27756      } else if (opts.insert === undefined || opts.insert) {
 27757        // no after specified => first layer made so put at start
 27758        layers.unshift(layer);
 27759      } // if( tmpLayers ){
 27760      //self.queueLayer( layer );
 27761      // }
 27762  
 27763  
 27764      return layer;
 27765    };
 27766  
 27767    if (self.skipping && !firstGet) {
 27768      // log('skip layers');
 27769      return null;
 27770    } // log('do layers');
 27771  
 27772  
 27773    var layer = null;
 27774    var maxElesPerLayer = eles.length / defNumLayers;
 27775    var allowLazyQueueing =  !firstGet;
 27776  
 27777    for (var i = 0; i < eles.length; i++) {
 27778      var ele = eles[i];
 27779      var rs = ele._private.rscratch;
 27780      var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
 27781  
 27782      var existingLayer = caches[lvl];
 27783  
 27784      if (existingLayer) {
 27785        // reuse layer for later eles
 27786        // log('reuse layer for', ele.id());
 27787        layer = existingLayer;
 27788        continue;
 27789      }
 27790  
 27791      if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
 27792        // log('make new layer for ele %s', ele.id());
 27793        layer = makeLayer({
 27794          insert: true,
 27795          after: layer
 27796        }); // if now layer can be built then we can't use layers at this level
 27797  
 27798        if (!layer) {
 27799          return null;
 27800        } // log('new layer with id %s', layer.id);
 27801  
 27802      }
 27803  
 27804      if (tmpLayers || allowLazyQueueing) {
 27805        // log('queue ele %s in layer %s', ele.id(), layer.id);
 27806        self.queueLayer(layer, ele);
 27807      } else {
 27808        // log('draw ele %s in layer %s', ele.id(), layer.id);
 27809        self.drawEleInLayer(layer, ele, lvl, pxRatio);
 27810      }
 27811  
 27812      layer.eles.push(ele);
 27813      caches[lvl] = layer;
 27814    } // log('--');
 27815  
 27816  
 27817    if (tmpLayers) {
 27818      // then we only queued the current layerset and can't draw it yet
 27819      return tmpLayers;
 27820    }
 27821  
 27822    if (allowLazyQueueing) {
 27823      // log('lazy queue level', lvl);
 27824      return null;
 27825    }
 27826  
 27827    return layers;
 27828  }; // a layer may want to use an ele cache of a higher level to avoid blurriness
 27829  // so the layer level might not equal the ele level
 27830  
 27831  
 27832  LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
 27833    return lvl;
 27834  };
 27835  
 27836  LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
 27837    var self = this;
 27838    var r = this.renderer;
 27839    var context = layer.context;
 27840    var bb = ele.boundingBox();
 27841  
 27842    if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
 27843      return;
 27844    }
 27845  
 27846    lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
 27847  
 27848    {
 27849      r.setImgSmoothing(context, false);
 27850    }
 27851  
 27852    {
 27853      r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
 27854    }
 27855  
 27856    {
 27857      r.setImgSmoothing(context, true);
 27858    }
 27859  };
 27860  
 27861  LTCp.levelIsComplete = function (lvl, eles) {
 27862    var self = this;
 27863    var layers = self.layersByLevel[lvl];
 27864  
 27865    if (!layers || layers.length === 0) {
 27866      return false;
 27867    }
 27868  
 27869    var numElesInLayers = 0;
 27870  
 27871    for (var i = 0; i < layers.length; i++) {
 27872      var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
 27873  
 27874      if (layer.reqs > 0) {
 27875        return false;
 27876      } // if the layer is invalid, the level is not complete
 27877  
 27878  
 27879      if (layer.invalid) {
 27880        return false;
 27881      }
 27882  
 27883      numElesInLayers += layer.eles.length;
 27884    } // we should have exactly the number of eles passed in to be complete
 27885  
 27886  
 27887    if (numElesInLayers !== eles.length) {
 27888      return false;
 27889    }
 27890  
 27891    return true;
 27892  };
 27893  
 27894  LTCp.validateLayersElesOrdering = function (lvl, eles) {
 27895    var layers = this.layersByLevel[lvl];
 27896  
 27897    if (!layers) {
 27898      return;
 27899    } // if in a layer the eles are not in the same order, then the layer is invalid
 27900    // (i.e. there is an ele in between the eles in the layer)
 27901  
 27902  
 27903    for (var i = 0; i < layers.length; i++) {
 27904      var layer = layers[i];
 27905      var offset = -1; // find the offset
 27906  
 27907      for (var j = 0; j < eles.length; j++) {
 27908        if (layer.eles[0] === eles[j]) {
 27909          offset = j;
 27910          break;
 27911        }
 27912      }
 27913  
 27914      if (offset < 0) {
 27915        // then the layer has nonexistant elements and is invalid
 27916        this.invalidateLayer(layer);
 27917        continue;
 27918      } // the eles in the layer must be in the same continuous order, else the layer is invalid
 27919  
 27920  
 27921      var o = offset;
 27922  
 27923      for (var j = 0; j < layer.eles.length; j++) {
 27924        if (layer.eles[j] !== eles[o + j]) {
 27925          // log('invalidate based on ordering', layer.id);
 27926          this.invalidateLayer(layer);
 27927          break;
 27928        }
 27929      }
 27930    }
 27931  };
 27932  
 27933  LTCp.updateElementsInLayers = function (eles, update) {
 27934    var self = this;
 27935    var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
 27936    // layer itself along the way
 27937  
 27938    for (var i = 0; i < eles.length; i++) {
 27939      var req = isEles ? null : eles[i];
 27940      var ele = isEles ? eles[i] : eles[i].ele;
 27941      var rs = ele._private.rscratch;
 27942      var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
 27943  
 27944      for (var l = minLvl$1; l <= maxLvl$1; l++) {
 27945        var layer = caches[l];
 27946  
 27947        if (!layer) {
 27948          continue;
 27949        } // if update is a request from the ele cache, then it affects only
 27950        // the matching level
 27951  
 27952  
 27953        if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
 27954          continue;
 27955        }
 27956  
 27957        update(layer, ele, req);
 27958      }
 27959    }
 27960  };
 27961  
 27962  LTCp.haveLayers = function () {
 27963    var self = this;
 27964    var haveLayers = false;
 27965  
 27966    for (var l = minLvl$1; l <= maxLvl$1; l++) {
 27967      var layers = self.layersByLevel[l];
 27968  
 27969      if (layers && layers.length > 0) {
 27970        haveLayers = true;
 27971        break;
 27972      }
 27973    }
 27974  
 27975    return haveLayers;
 27976  };
 27977  
 27978  LTCp.invalidateElements = function (eles) {
 27979    var self = this;
 27980  
 27981    if (eles.length === 0) {
 27982      return;
 27983    }
 27984  
 27985    self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
 27986  
 27987    if (eles.length === 0 || !self.haveLayers()) {
 27988      return;
 27989    }
 27990  
 27991    self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
 27992      self.invalidateLayer(layer);
 27993    });
 27994  };
 27995  
 27996  LTCp.invalidateLayer = function (layer) {
 27997    // log('update invalidate layer time');
 27998    this.lastInvalidationTime = performanceNow();
 27999  
 28000    if (layer.invalid) {
 28001      return;
 28002    } // save cycles
 28003  
 28004  
 28005    var lvl = layer.level;
 28006    var eles = layer.eles;
 28007    var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
 28008  
 28009    removeFromArray(layers, layer); // layer.eles = [];
 28010  
 28011    layer.elesQueue = [];
 28012    layer.invalid = true;
 28013  
 28014    if (layer.replacement) {
 28015      layer.replacement.invalid = true;
 28016    }
 28017  
 28018    for (var i = 0; i < eles.length; i++) {
 28019      var caches = eles[i]._private.rscratch.imgLayerCaches;
 28020  
 28021      if (caches) {
 28022        caches[lvl] = null;
 28023      }
 28024    }
 28025  };
 28026  
 28027  LTCp.refineElementTextures = function (eles) {
 28028    var self = this; // log('refine', eles.length);
 28029  
 28030    self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
 28031      var rLyr = layer.replacement;
 28032  
 28033      if (!rLyr) {
 28034        rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
 28035        rLyr.replaces = layer;
 28036        rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
 28037      }
 28038  
 28039      if (!rLyr.reqs) {
 28040        for (var i = 0; i < rLyr.eles.length; i++) {
 28041          self.queueLayer(rLyr, rLyr.eles[i]);
 28042        } // log('queue replacement layer refinement', rLyr.id);
 28043  
 28044      }
 28045    });
 28046  };
 28047  
 28048  LTCp.enqueueElementRefinement = function (ele) {
 28049  
 28050    this.eleTxrDeqs.merge(ele);
 28051    this.scheduleElementRefinement();
 28052  };
 28053  
 28054  LTCp.queueLayer = function (layer, ele) {
 28055    var self = this;
 28056    var q = self.layersQueue;
 28057    var elesQ = layer.elesQueue;
 28058    var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
 28059  
 28060    if (layer.replacement) {
 28061      return;
 28062    }
 28063  
 28064    if (ele) {
 28065      if (hasId[ele.id()]) {
 28066        return;
 28067      }
 28068  
 28069      elesQ.push(ele);
 28070      hasId[ele.id()] = true;
 28071    }
 28072  
 28073    if (layer.reqs) {
 28074      layer.reqs++;
 28075      q.updateItem(layer);
 28076    } else {
 28077      layer.reqs = 1;
 28078      q.push(layer);
 28079    }
 28080  };
 28081  
 28082  LTCp.dequeue = function (pxRatio) {
 28083    var self = this;
 28084    var q = self.layersQueue;
 28085    var deqd = [];
 28086    var eleDeqs = 0;
 28087  
 28088    while (eleDeqs < maxDeqSize$1) {
 28089      if (q.size() === 0) {
 28090        break;
 28091      }
 28092  
 28093      var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
 28094  
 28095      if (layer.replacement) {
 28096        // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
 28097        q.pop();
 28098        continue;
 28099      } // if this is a replacement layer that has been superceded, then forget it
 28100  
 28101  
 28102      if (layer.replaces && layer !== layer.replaces.replacement) {
 28103        // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
 28104        q.pop();
 28105        continue;
 28106      }
 28107  
 28108      if (layer.invalid) {
 28109        // log('replacement layer %s is invalid; dequeued', layer.id);
 28110        q.pop();
 28111        continue;
 28112      }
 28113  
 28114      var ele = layer.elesQueue.shift();
 28115  
 28116      if (ele) {
 28117        // log('dequeue layer %s', layer.id);
 28118        self.drawEleInLayer(layer, ele, layer.level, pxRatio);
 28119        eleDeqs++;
 28120      }
 28121  
 28122      if (deqd.length === 0) {
 28123        // we need only one entry in deqd to queue redrawing etc
 28124        deqd.push(true);
 28125      } // if the layer has all its eles done, then remove from the queue
 28126  
 28127  
 28128      if (layer.elesQueue.length === 0) {
 28129        q.pop();
 28130        layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
 28131        // when a replacement layer is dequeued, it replaces the old layer in the level
 28132  
 28133        if (layer.replaces) {
 28134          self.applyLayerReplacement(layer);
 28135        }
 28136  
 28137        self.requestRedraw();
 28138      }
 28139    }
 28140  
 28141    return deqd;
 28142  };
 28143  
 28144  LTCp.applyLayerReplacement = function (layer) {
 28145    var self = this;
 28146    var layersInLevel = self.layersByLevel[layer.level];
 28147    var replaced = layer.replaces;
 28148    var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
 28149    // refs would be a mistake (i.e. overwriting the true active layer)
 28150  
 28151    if (index < 0 || replaced.invalid) {
 28152      // log('replacement layer would have no effect', layer.id);
 28153      return;
 28154    }
 28155  
 28156    layersInLevel[index] = layer; // replace level ref
 28157    // replace refs in eles
 28158  
 28159    for (var i = 0; i < layer.eles.length; i++) {
 28160      var _p = layer.eles[i]._private;
 28161      var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
 28162  
 28163      if (cache) {
 28164        cache[layer.level] = layer;
 28165      }
 28166    } // log('apply replacement layer %s over %s', layer.id, replaced.id);
 28167  
 28168  
 28169    self.requestRedraw();
 28170  };
 28171  
 28172  LTCp.requestRedraw = util(function () {
 28173    var r = this.renderer;
 28174    r.redrawHint('eles', true);
 28175    r.redrawHint('drag', true);
 28176    r.redraw();
 28177  }, 100);
 28178  LTCp.setupDequeueing = defs.setupDequeueing({
 28179    deqRedrawThreshold: deqRedrawThreshold$1,
 28180    deqCost: deqCost$1,
 28181    deqAvgCost: deqAvgCost$1,
 28182    deqNoDrawCost: deqNoDrawCost$1,
 28183    deqFastCost: deqFastCost$1,
 28184    deq: function deq(self, pxRatio) {
 28185      return self.dequeue(pxRatio);
 28186    },
 28187    onDeqd: noop,
 28188    shouldRedraw: trueify,
 28189    priority: function priority(self) {
 28190      return self.renderer.beforeRenderPriorities.lyrTxrDeq;
 28191    }
 28192  });
 28193  
 28194  var CRp = {};
 28195  var impl;
 28196  
 28197  function polygon(context, points) {
 28198    for (var i = 0; i < points.length; i++) {
 28199      var pt = points[i];
 28200      context.lineTo(pt.x, pt.y);
 28201    }
 28202  }
 28203  
 28204  function triangleBackcurve(context, points, controlPoint) {
 28205    var firstPt;
 28206  
 28207    for (var i = 0; i < points.length; i++) {
 28208      var pt = points[i];
 28209  
 28210      if (i === 0) {
 28211        firstPt = pt;
 28212      }
 28213  
 28214      context.lineTo(pt.x, pt.y);
 28215    }
 28216  
 28217    context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
 28218  }
 28219  
 28220  function triangleTee(context, trianglePoints, teePoints) {
 28221    if (context.beginPath) {
 28222      context.beginPath();
 28223    }
 28224  
 28225    var triPts = trianglePoints;
 28226  
 28227    for (var i = 0; i < triPts.length; i++) {
 28228      var pt = triPts[i];
 28229      context.lineTo(pt.x, pt.y);
 28230    }
 28231  
 28232    var teePts = teePoints;
 28233    var firstTeePt = teePoints[0];
 28234    context.moveTo(firstTeePt.x, firstTeePt.y);
 28235  
 28236    for (var i = 1; i < teePts.length; i++) {
 28237      var pt = teePts[i];
 28238      context.lineTo(pt.x, pt.y);
 28239    }
 28240  
 28241    if (context.closePath) {
 28242      context.closePath();
 28243    }
 28244  }
 28245  
 28246  function circle(context, rx, ry, r) {
 28247    context.arc(rx, ry, r, 0, Math.PI * 2, false);
 28248  }
 28249  
 28250  CRp.arrowShapeImpl = function (name) {
 28251    return (impl || (impl = {
 28252      'polygon': polygon,
 28253      'triangle-backcurve': triangleBackcurve,
 28254      'triangle-tee': triangleTee,
 28255      'triangle-cross': triangleTee,
 28256      'circle': circle
 28257    }))[name];
 28258  };
 28259  
 28260  var CRp$1 = {};
 28261  
 28262  CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
 28263    var r = this;
 28264  
 28265    if (ele.isNode()) {
 28266      r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
 28267    } else {
 28268      r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
 28269    }
 28270  };
 28271  
 28272  CRp$1.drawElementOverlay = function (context, ele) {
 28273    var r = this;
 28274  
 28275    if (ele.isNode()) {
 28276      r.drawNodeOverlay(context, ele);
 28277    } else {
 28278      r.drawEdgeOverlay(context, ele);
 28279    }
 28280  };
 28281  
 28282  CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
 28283    var r = this;
 28284    var bb = eleTxrCache.getBoundingBox(ele);
 28285  
 28286    if (bb.w === 0 || bb.h === 0) {
 28287      return;
 28288    } // ignore zero size case
 28289  
 28290  
 28291    var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
 28292  
 28293    if (eleCache != null) {
 28294      var opacity = getOpacity(r, ele);
 28295  
 28296      if (opacity === 0) {
 28297        return;
 28298      }
 28299  
 28300      var theta = getRotation(r, ele);
 28301      var x1 = bb.x1,
 28302          y1 = bb.y1,
 28303          w = bb.w,
 28304          h = bb.h;
 28305      var x, y, sx, sy, smooth;
 28306  
 28307      if (theta !== 0) {
 28308        var rotPt = eleTxrCache.getRotationPoint(ele);
 28309        sx = rotPt.x;
 28310        sy = rotPt.y;
 28311        context.translate(sx, sy);
 28312        context.rotate(theta);
 28313        smooth = r.getImgSmoothing(context);
 28314  
 28315        if (!smooth) {
 28316          r.setImgSmoothing(context, true);
 28317        }
 28318  
 28319        var off = eleTxrCache.getRotationOffset(ele);
 28320        x = off.x;
 28321        y = off.y;
 28322      } else {
 28323        x = x1;
 28324        y = y1;
 28325      }
 28326  
 28327      var oldGlobalAlpha;
 28328  
 28329      if (opacity !== 1) {
 28330        oldGlobalAlpha = context.globalAlpha;
 28331        context.globalAlpha = oldGlobalAlpha * opacity;
 28332      }
 28333  
 28334      context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
 28335  
 28336      if (opacity !== 1) {
 28337        context.globalAlpha = oldGlobalAlpha;
 28338      }
 28339  
 28340      if (theta !== 0) {
 28341        context.rotate(-theta);
 28342        context.translate(-sx, -sy);
 28343  
 28344        if (!smooth) {
 28345          r.setImgSmoothing(context, false);
 28346        }
 28347      }
 28348    } else {
 28349      eleTxrCache.drawElement(context, ele); // direct draw fallback
 28350    }
 28351  };
 28352  
 28353  var getZeroRotation = function getZeroRotation() {
 28354    return 0;
 28355  };
 28356  
 28357  var getLabelRotation = function getLabelRotation(r, ele) {
 28358    return r.getTextAngle(ele, null);
 28359  };
 28360  
 28361  var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
 28362    return r.getTextAngle(ele, 'source');
 28363  };
 28364  
 28365  var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
 28366    return r.getTextAngle(ele, 'target');
 28367  };
 28368  
 28369  var getOpacity = function getOpacity(r, ele) {
 28370    return ele.effectiveOpacity();
 28371  };
 28372  
 28373  var getTextOpacity = function getTextOpacity(e, ele) {
 28374    return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
 28375  };
 28376  
 28377  CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
 28378    var r = this;
 28379    var _r$data = r.data,
 28380        eleTxrCache = _r$data.eleTxrCache,
 28381        lblTxrCache = _r$data.lblTxrCache,
 28382        slbTxrCache = _r$data.slbTxrCache,
 28383        tlbTxrCache = _r$data.tlbTxrCache;
 28384    var bb = ele.boundingBox();
 28385    var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
 28386  
 28387    if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
 28388      return;
 28389    }
 28390  
 28391    if (!extent || boundingBoxesIntersect(bb, extent)) {
 28392      var isEdge = ele.isEdge();
 28393  
 28394      var badLine = ele.element()._private.rscratch.badLine;
 28395  
 28396      r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
 28397  
 28398      if (!isEdge || !badLine) {
 28399        r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
 28400      }
 28401  
 28402      if (isEdge && !badLine) {
 28403        r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
 28404        r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
 28405      }
 28406  
 28407      r.drawElementOverlay(context, ele);
 28408    }
 28409  };
 28410  
 28411  CRp$1.drawElements = function (context, eles) {
 28412    var r = this;
 28413  
 28414    for (var i = 0; i < eles.length; i++) {
 28415      var ele = eles[i];
 28416      r.drawElement(context, ele);
 28417    }
 28418  };
 28419  
 28420  CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
 28421    var r = this;
 28422  
 28423    for (var i = 0; i < eles.length; i++) {
 28424      var ele = eles[i];
 28425      r.drawCachedElement(context, ele, pxRatio, extent);
 28426    }
 28427  };
 28428  
 28429  CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
 28430    var r = this;
 28431  
 28432    for (var i = 0; i < eles.length; i++) {
 28433      var ele = eles[i];
 28434  
 28435      if (!ele.isNode()) {
 28436        continue;
 28437      }
 28438  
 28439      r.drawCachedElement(context, ele, pxRatio, extent);
 28440    }
 28441  };
 28442  
 28443  CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
 28444    var r = this;
 28445    var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
 28446  
 28447    if (layers) {
 28448      for (var i = 0; i < layers.length; i++) {
 28449        var layer = layers[i];
 28450        var bb = layer.bb;
 28451  
 28452        if (bb.w === 0 || bb.h === 0) {
 28453          continue;
 28454        }
 28455  
 28456        context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
 28457      }
 28458    } else {
 28459      // fall back on plain caching if no layers
 28460      r.drawCachedElements(context, eles, pxRatio, extent);
 28461    }
 28462  };
 28463  
 28464  /* global Path2D */
 28465  var CRp$2 = {};
 28466  
 28467  CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
 28468    var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
 28469    var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
 28470    var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
 28471    var r = this;
 28472    var rs = edge._private.rscratch;
 28473  
 28474    if (shouldDrawOpacity && !edge.visible()) {
 28475      return;
 28476    } // if bezier ctrl pts can not be calculated, then die
 28477  
 28478  
 28479    if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
 28480      // isNaN in case edge is impossible and browser bugs (e.g. safari)
 28481      return;
 28482    }
 28483  
 28484    var bb;
 28485  
 28486    if (shiftToOriginWithBb) {
 28487      bb = shiftToOriginWithBb;
 28488      context.translate(-bb.x1, -bb.y1);
 28489    }
 28490  
 28491    var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
 28492    var lineStyle = edge.pstyle('line-style').value;
 28493    var edgeWidth = edge.pstyle('width').pfValue;
 28494    var lineCap = edge.pstyle('line-cap').value;
 28495  
 28496    var drawLine = function drawLine() {
 28497      var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
 28498      context.lineWidth = edgeWidth;
 28499      context.lineCap = lineCap;
 28500      r.eleStrokeStyle(context, edge, strokeOpacity);
 28501      r.drawEdgePath(edge, context, rs.allpts, lineStyle);
 28502      context.lineCap = 'butt'; // reset for other drawing functions
 28503    };
 28504  
 28505    var drawOverlay = function drawOverlay() {
 28506      if (!shouldDrawOverlay) {
 28507        return;
 28508      }
 28509  
 28510      r.drawEdgeOverlay(context, edge);
 28511    };
 28512  
 28513    var drawArrows = function drawArrows() {
 28514      var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
 28515      r.drawArrowheads(context, edge, arrowOpacity);
 28516    };
 28517  
 28518    var drawText = function drawText() {
 28519      r.drawElementText(context, edge, null, drawLabel);
 28520    };
 28521  
 28522    context.lineJoin = 'round';
 28523    var ghost = edge.pstyle('ghost').value === 'yes';
 28524  
 28525    if (ghost) {
 28526      var gx = edge.pstyle('ghost-offset-x').pfValue;
 28527      var gy = edge.pstyle('ghost-offset-y').pfValue;
 28528      var ghostOpacity = edge.pstyle('ghost-opacity').value;
 28529      var effectiveGhostOpacity = opacity * ghostOpacity;
 28530      context.translate(gx, gy);
 28531      drawLine(effectiveGhostOpacity);
 28532      drawArrows(effectiveGhostOpacity);
 28533      context.translate(-gx, -gy);
 28534    }
 28535  
 28536    drawLine();
 28537    drawArrows();
 28538    drawOverlay();
 28539    drawText();
 28540  
 28541    if (shiftToOriginWithBb) {
 28542      context.translate(bb.x1, bb.y1);
 28543    }
 28544  };
 28545  
 28546  CRp$2.drawEdgeOverlay = function (context, edge) {
 28547    if (!edge.visible()) {
 28548      return;
 28549    }
 28550  
 28551    var overlayOpacity = edge.pstyle('overlay-opacity').value;
 28552  
 28553    if (overlayOpacity === 0) {
 28554      return;
 28555    }
 28556  
 28557    var r = this;
 28558    var usePaths = r.usePaths();
 28559    var rs = edge._private.rscratch;
 28560    var overlayPadding = edge.pstyle('overlay-padding').pfValue;
 28561    var overlayWidth = 2 * overlayPadding;
 28562    var overlayColor = edge.pstyle('overlay-color').value;
 28563    context.lineWidth = overlayWidth;
 28564  
 28565    if (rs.edgeType === 'self' && !usePaths) {
 28566      context.lineCap = 'butt';
 28567    } else {
 28568      context.lineCap = 'round';
 28569    }
 28570  
 28571    r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
 28572    r.drawEdgePath(edge, context, rs.allpts, 'solid');
 28573  };
 28574  
 28575  CRp$2.drawEdgePath = function (edge, context, pts, type) {
 28576    var rs = edge._private.rscratch;
 28577    var canvasCxt = context;
 28578    var path;
 28579    var pathCacheHit = false;
 28580    var usePaths = this.usePaths();
 28581    var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
 28582    var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
 28583  
 28584    if (usePaths) {
 28585      var pathCacheKey = pts.join('$');
 28586      var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
 28587  
 28588      if (keyMatches) {
 28589        path = context = rs.pathCache;
 28590        pathCacheHit = true;
 28591      } else {
 28592        path = context = new Path2D();
 28593        rs.pathCacheKey = pathCacheKey;
 28594        rs.pathCache = path;
 28595      }
 28596    }
 28597  
 28598    if (canvasCxt.setLineDash) {
 28599      // for very outofdate browsers
 28600      switch (type) {
 28601        case 'dotted':
 28602          canvasCxt.setLineDash([1, 1]);
 28603          break;
 28604  
 28605        case 'dashed':
 28606          canvasCxt.setLineDash(lineDashPattern);
 28607          canvasCxt.lineDashOffset = lineDashOffset;
 28608          break;
 28609  
 28610        case 'solid':
 28611          canvasCxt.setLineDash([]);
 28612          break;
 28613      }
 28614    }
 28615  
 28616    if (!pathCacheHit && !rs.badLine) {
 28617      if (context.beginPath) {
 28618        context.beginPath();
 28619      }
 28620  
 28621      context.moveTo(pts[0], pts[1]);
 28622  
 28623      switch (rs.edgeType) {
 28624        case 'bezier':
 28625        case 'self':
 28626        case 'compound':
 28627        case 'multibezier':
 28628          for (var i = 2; i + 3 < pts.length; i += 4) {
 28629            context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
 28630          }
 28631  
 28632          break;
 28633  
 28634        case 'straight':
 28635        case 'segments':
 28636        case 'haystack':
 28637          for (var _i = 2; _i + 1 < pts.length; _i += 2) {
 28638            context.lineTo(pts[_i], pts[_i + 1]);
 28639          }
 28640  
 28641          break;
 28642      }
 28643    }
 28644  
 28645    context = canvasCxt;
 28646  
 28647    if (usePaths) {
 28648      context.stroke(path);
 28649    } else {
 28650      context.stroke();
 28651    } // reset any line dashes
 28652  
 28653  
 28654    if (context.setLineDash) {
 28655      // for very outofdate browsers
 28656      context.setLineDash([]);
 28657    }
 28658  };
 28659  
 28660  CRp$2.drawArrowheads = function (context, edge, opacity) {
 28661    var rs = edge._private.rscratch;
 28662    var isHaystack = rs.edgeType === 'haystack';
 28663  
 28664    if (!isHaystack) {
 28665      this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
 28666    }
 28667  
 28668    this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
 28669    this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
 28670  
 28671    if (!isHaystack) {
 28672      this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
 28673    }
 28674  };
 28675  
 28676  CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
 28677    if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
 28678      return;
 28679    }
 28680  
 28681    var self = this;
 28682    var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
 28683  
 28684    if (arrowShape === 'none') {
 28685      return;
 28686    }
 28687  
 28688    var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
 28689    var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
 28690    var edgeWidth = edge.pstyle('width').pfValue;
 28691    var edgeOpacity = edge.pstyle('opacity').value;
 28692  
 28693    if (opacity === undefined) {
 28694      opacity = edgeOpacity;
 28695    }
 28696  
 28697    var gco = context.globalCompositeOperation;
 28698  
 28699    if (opacity !== 1 || arrowFill === 'hollow') {
 28700      // then extra clear is needed
 28701      context.globalCompositeOperation = 'destination-out';
 28702      self.colorFillStyle(context, 255, 255, 255, 1);
 28703      self.colorStrokeStyle(context, 255, 255, 255, 1);
 28704      self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
 28705      context.globalCompositeOperation = gco;
 28706    } // otherwise, the opaque arrow clears it for free :)
 28707  
 28708  
 28709    var color = edge.pstyle(prefix + '-arrow-color').value;
 28710    self.colorFillStyle(context, color[0], color[1], color[2], opacity);
 28711    self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
 28712    self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
 28713  };
 28714  
 28715  CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
 28716    var r = this;
 28717    var usePaths = this.usePaths() && shape !== 'triangle-cross';
 28718    var pathCacheHit = false;
 28719    var path;
 28720    var canvasContext = context;
 28721    var translation = {
 28722      x: x,
 28723      y: y
 28724    };
 28725    var scale = edge.pstyle('arrow-scale').value;
 28726    var size = this.getArrowWidth(edgeWidth, scale);
 28727    var shapeImpl = r.arrowShapes[shape];
 28728  
 28729    if (usePaths) {
 28730      var cache = r.arrowPathCache = r.arrowPathCache || [];
 28731      var key = hashString(shape);
 28732      var cachedPath = cache[key];
 28733  
 28734      if (cachedPath != null) {
 28735        path = context = cachedPath;
 28736        pathCacheHit = true;
 28737      } else {
 28738        path = context = new Path2D();
 28739        cache[key] = path;
 28740      }
 28741    }
 28742  
 28743    if (!pathCacheHit) {
 28744      if (context.beginPath) {
 28745        context.beginPath();
 28746      }
 28747  
 28748      if (usePaths) {
 28749        // store in the path cache with values easily manipulated later
 28750        shapeImpl.draw(context, 1, 0, {
 28751          x: 0,
 28752          y: 0
 28753        }, 1);
 28754      } else {
 28755        shapeImpl.draw(context, size, angle, translation, edgeWidth);
 28756      }
 28757  
 28758      if (context.closePath) {
 28759        context.closePath();
 28760      }
 28761    }
 28762  
 28763    context = canvasContext;
 28764  
 28765    if (usePaths) {
 28766      // set transform to arrow position/orientation
 28767      context.translate(x, y);
 28768      context.rotate(angle);
 28769      context.scale(size, size);
 28770    }
 28771  
 28772    if (fill === 'filled' || fill === 'both') {
 28773      if (usePaths) {
 28774        context.fill(path);
 28775      } else {
 28776        context.fill();
 28777      }
 28778    }
 28779  
 28780    if (fill === 'hollow' || fill === 'both') {
 28781      context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
 28782      context.lineJoin = 'miter';
 28783  
 28784      if (usePaths) {
 28785        context.stroke(path);
 28786      } else {
 28787        context.stroke();
 28788      }
 28789    }
 28790  
 28791    if (usePaths) {
 28792      // reset transform by applying inverse
 28793      context.scale(1 / size, 1 / size);
 28794      context.rotate(-angle);
 28795      context.translate(-x, -y);
 28796    }
 28797  };
 28798  
 28799  var CRp$3 = {};
 28800  
 28801  CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
 28802    // detect problematic cases for old browsers with bad images (cheaper than try-catch)
 28803    if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
 28804      return;
 28805    }
 28806  
 28807    context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
 28808  };
 28809  
 28810  CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
 28811    var r = this;
 28812    var pos = node.position();
 28813    var nodeX = pos.x;
 28814    var nodeY = pos.y;
 28815    var styleObj = node.cy().style();
 28816    var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
 28817    var fit = getIndexedStyle(node, 'background-fit', 'value', index);
 28818    var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
 28819    var nodeW = node.width();
 28820    var nodeH = node.height();
 28821    var paddingX2 = node.padding() * 2;
 28822    var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
 28823    var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
 28824    var rs = node._private.rscratch;
 28825    var clip = getIndexedStyle(node, 'background-clip', 'value', index);
 28826    var shouldClip = clip === 'node';
 28827    var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
 28828    var imgW = img.width || img.cachedW;
 28829    var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
 28830  
 28831    if (null == imgW || null == imgH) {
 28832      document.body.appendChild(img); // eslint-disable-line no-undef
 28833  
 28834      imgW = img.cachedW = img.width || img.offsetWidth;
 28835      imgH = img.cachedH = img.height || img.offsetHeight;
 28836      document.body.removeChild(img); // eslint-disable-line no-undef
 28837    }
 28838  
 28839    var w = imgW;
 28840    var h = imgH;
 28841  
 28842    if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
 28843      if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
 28844        w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
 28845      } else {
 28846        w = getIndexedStyle(node, 'background-width', 'pfValue', index);
 28847      }
 28848    }
 28849  
 28850    if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
 28851      if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
 28852        h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
 28853      } else {
 28854        h = getIndexedStyle(node, 'background-height', 'pfValue', index);
 28855      }
 28856    }
 28857  
 28858    if (w === 0 || h === 0) {
 28859      return; // no point in drawing empty image (and chrome is broken in this case)
 28860    }
 28861  
 28862    if (fit === 'contain') {
 28863      var scale = Math.min(nodeTW / w, nodeTH / h);
 28864      w *= scale;
 28865      h *= scale;
 28866    } else if (fit === 'cover') {
 28867      var scale = Math.max(nodeTW / w, nodeTH / h);
 28868      w *= scale;
 28869      h *= scale;
 28870    }
 28871  
 28872    var x = nodeX - nodeTW / 2; // left
 28873  
 28874    var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
 28875    var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
 28876  
 28877    if (posXUnits === '%') {
 28878      x += (nodeTW - w) * posXPfVal;
 28879    } else {
 28880      x += posXPfVal;
 28881    }
 28882  
 28883    var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
 28884    var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
 28885  
 28886    if (offXUnits === '%') {
 28887      x += (nodeTW - w) * offXPfVal;
 28888    } else {
 28889      x += offXPfVal;
 28890    }
 28891  
 28892    var y = nodeY - nodeTH / 2; // top
 28893  
 28894    var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
 28895    var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
 28896  
 28897    if (posYUnits === '%') {
 28898      y += (nodeTH - h) * posYPfVal;
 28899    } else {
 28900      y += posYPfVal;
 28901    }
 28902  
 28903    var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
 28904    var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
 28905  
 28906    if (offYUnits === '%') {
 28907      y += (nodeTH - h) * offYPfVal;
 28908    } else {
 28909      y += offYPfVal;
 28910    }
 28911  
 28912    if (rs.pathCache) {
 28913      x -= nodeX;
 28914      y -= nodeY;
 28915      nodeX = 0;
 28916      nodeY = 0;
 28917    }
 28918  
 28919    var gAlpha = context.globalAlpha;
 28920    context.globalAlpha = imgOpacity;
 28921  
 28922    if (repeat === 'no-repeat') {
 28923      if (shouldClip) {
 28924        context.save();
 28925  
 28926        if (rs.pathCache) {
 28927          context.clip(rs.pathCache);
 28928        } else {
 28929          r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
 28930          context.clip();
 28931        }
 28932      }
 28933  
 28934      r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
 28935  
 28936      if (shouldClip) {
 28937        context.restore();
 28938      }
 28939    } else {
 28940      var pattern = context.createPattern(img, repeat);
 28941      context.fillStyle = pattern;
 28942      r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
 28943      context.translate(x, y);
 28944      context.fill();
 28945      context.translate(-x, -y);
 28946    }
 28947  
 28948    context.globalAlpha = gAlpha;
 28949  };
 28950  
 28951  var CRp$4 = {};
 28952  
 28953  CRp$4.eleTextBiggerThanMin = function (ele, scale) {
 28954    if (!scale) {
 28955      var zoom = ele.cy().zoom();
 28956      var pxRatio = this.getPixelRatio();
 28957      var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
 28958  
 28959      scale = Math.pow(2, lvl);
 28960    }
 28961  
 28962    var computedSize = ele.pstyle('font-size').pfValue * scale;
 28963    var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
 28964  
 28965    if (computedSize < minSize) {
 28966      return false;
 28967    }
 28968  
 28969    return true;
 28970  };
 28971  
 28972  CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
 28973    var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
 28974    var r = this;
 28975  
 28976    if (force == null) {
 28977      if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
 28978        return;
 28979      }
 28980    } else if (force === false) {
 28981      return;
 28982    }
 28983  
 28984    if (ele.isNode()) {
 28985      var label = ele.pstyle('label');
 28986  
 28987      if (!label || !label.value) {
 28988        return;
 28989      }
 28990  
 28991      var justification = r.getLabelJustification(ele);
 28992      context.textAlign = justification;
 28993      context.textBaseline = 'bottom';
 28994    } else {
 28995      var badLine = ele.element()._private.rscratch.badLine;
 28996  
 28997      var _label = ele.pstyle('label');
 28998  
 28999      var srcLabel = ele.pstyle('source-label');
 29000      var tgtLabel = ele.pstyle('target-label');
 29001  
 29002      if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
 29003        return;
 29004      }
 29005  
 29006      context.textAlign = 'center';
 29007      context.textBaseline = 'bottom';
 29008    }
 29009  
 29010    var applyRotation = !shiftToOriginWithBb;
 29011    var bb;
 29012  
 29013    if (shiftToOriginWithBb) {
 29014      bb = shiftToOriginWithBb;
 29015      context.translate(-bb.x1, -bb.y1);
 29016    }
 29017  
 29018    if (prefix == null) {
 29019      r.drawText(context, ele, null, applyRotation, useEleOpacity);
 29020  
 29021      if (ele.isEdge()) {
 29022        r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
 29023        r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
 29024      }
 29025    } else {
 29026      r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
 29027    }
 29028  
 29029    if (shiftToOriginWithBb) {
 29030      context.translate(bb.x1, bb.y1);
 29031    }
 29032  };
 29033  
 29034  CRp$4.getFontCache = function (context) {
 29035    var cache;
 29036    this.fontCaches = this.fontCaches || [];
 29037  
 29038    for (var i = 0; i < this.fontCaches.length; i++) {
 29039      cache = this.fontCaches[i];
 29040  
 29041      if (cache.context === context) {
 29042        return cache;
 29043      }
 29044    }
 29045  
 29046    cache = {
 29047      context: context
 29048    };
 29049    this.fontCaches.push(cache);
 29050    return cache;
 29051  }; // set up canvas context with font
 29052  // returns transformed text string
 29053  
 29054  
 29055  CRp$4.setupTextStyle = function (context, ele) {
 29056    var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
 29057    // Font style
 29058    var labelStyle = ele.pstyle('font-style').strValue;
 29059    var labelSize = ele.pstyle('font-size').pfValue + 'px';
 29060    var labelFamily = ele.pstyle('font-family').strValue;
 29061    var labelWeight = ele.pstyle('font-weight').strValue;
 29062    var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
 29063    var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
 29064    var color = ele.pstyle('color').value;
 29065    var outlineColor = ele.pstyle('text-outline-color').value;
 29066    context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
 29067    context.lineJoin = 'round'; // so text outlines aren't jagged
 29068  
 29069    this.colorFillStyle(context, color[0], color[1], color[2], opacity);
 29070    this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
 29071  }; // TODO ensure re-used
 29072  
 29073  
 29074  function roundRect(ctx, x, y, width, height) {
 29075    var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
 29076    ctx.beginPath();
 29077    ctx.moveTo(x + radius, y);
 29078    ctx.lineTo(x + width - radius, y);
 29079    ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
 29080    ctx.lineTo(x + width, y + height - radius);
 29081    ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
 29082    ctx.lineTo(x + radius, y + height);
 29083    ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
 29084    ctx.lineTo(x, y + radius);
 29085    ctx.quadraticCurveTo(x, y, x + radius, y);
 29086    ctx.closePath();
 29087    ctx.fill();
 29088  }
 29089  
 29090  CRp$4.getTextAngle = function (ele, prefix) {
 29091    var theta;
 29092    var _p = ele._private;
 29093    var rscratch = _p.rscratch;
 29094    var pdash = prefix ? prefix + '-' : '';
 29095    var rotation = ele.pstyle(pdash + 'text-rotation');
 29096    var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
 29097  
 29098    if (rotation.strValue === 'autorotate') {
 29099      theta = ele.isEdge() ? textAngle : 0;
 29100    } else if (rotation.strValue === 'none') {
 29101      theta = 0;
 29102    } else {
 29103      theta = rotation.pfValue;
 29104    }
 29105  
 29106    return theta;
 29107  };
 29108  
 29109  CRp$4.drawText = function (context, ele, prefix) {
 29110    var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
 29111    var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
 29112    var _p = ele._private;
 29113    var rscratch = _p.rscratch;
 29114    var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
 29115  
 29116    if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
 29117      return;
 29118    } // use 'main' as an alias for the main label (i.e. null prefix)
 29119  
 29120  
 29121    if (prefix === 'main') {
 29122      prefix = null;
 29123    }
 29124  
 29125    var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
 29126    var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
 29127    var orgTextX, orgTextY; // used for rotation
 29128  
 29129    var text = this.getLabelText(ele, prefix);
 29130  
 29131    if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
 29132      this.setupTextStyle(context, ele, useEleOpacity);
 29133      var pdash = prefix ? prefix + '-' : '';
 29134      var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
 29135      var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
 29136      var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
 29137      var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
 29138      var isEdge = ele.isEdge();
 29139      var halign = ele.pstyle('text-halign').value;
 29140      var valign = ele.pstyle('text-valign').value;
 29141  
 29142      if (isEdge) {
 29143        halign = 'center';
 29144        valign = 'center';
 29145      }
 29146  
 29147      textX += marginX;
 29148      textY += marginY;
 29149      var theta;
 29150  
 29151      if (!applyRotation) {
 29152        theta = 0;
 29153      } else {
 29154        theta = this.getTextAngle(ele, prefix);
 29155      }
 29156  
 29157      if (theta !== 0) {
 29158        orgTextX = textX;
 29159        orgTextY = textY;
 29160        context.translate(orgTextX, orgTextY);
 29161        context.rotate(theta);
 29162        textX = 0;
 29163        textY = 0;
 29164      }
 29165  
 29166      switch (valign) {
 29167        case 'top':
 29168          break;
 29169  
 29170        case 'center':
 29171          textY += textH / 2;
 29172          break;
 29173  
 29174        case 'bottom':
 29175          textY += textH;
 29176          break;
 29177      }
 29178  
 29179      var backgroundOpacity = ele.pstyle('text-background-opacity').value;
 29180      var borderOpacity = ele.pstyle('text-border-opacity').value;
 29181      var textBorderWidth = ele.pstyle('text-border-width').pfValue;
 29182      var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
 29183  
 29184      if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
 29185        var bgX = textX - backgroundPadding;
 29186  
 29187        switch (halign) {
 29188          case 'left':
 29189            bgX -= textW;
 29190            break;
 29191  
 29192          case 'center':
 29193            bgX -= textW / 2;
 29194            break;
 29195        }
 29196  
 29197        var bgY = textY - textH - backgroundPadding;
 29198        var bgW = textW + 2 * backgroundPadding;
 29199        var bgH = textH + 2 * backgroundPadding;
 29200  
 29201        if (backgroundOpacity > 0) {
 29202          var textFill = context.fillStyle;
 29203          var textBackgroundColor = ele.pstyle('text-background-color').value;
 29204          context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
 29205          var styleShape = ele.pstyle('text-background-shape').strValue;
 29206  
 29207          if (styleShape.indexOf('round') === 0) {
 29208            roundRect(context, bgX, bgY, bgW, bgH, 2);
 29209          } else {
 29210            context.fillRect(bgX, bgY, bgW, bgH);
 29211          }
 29212  
 29213          context.fillStyle = textFill;
 29214        }
 29215  
 29216        if (textBorderWidth > 0 && borderOpacity > 0) {
 29217          var textStroke = context.strokeStyle;
 29218          var textLineWidth = context.lineWidth;
 29219          var textBorderColor = ele.pstyle('text-border-color').value;
 29220          var textBorderStyle = ele.pstyle('text-border-style').value;
 29221          context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
 29222          context.lineWidth = textBorderWidth;
 29223  
 29224          if (context.setLineDash) {
 29225            // for very outofdate browsers
 29226            switch (textBorderStyle) {
 29227              case 'dotted':
 29228                context.setLineDash([1, 1]);
 29229                break;
 29230  
 29231              case 'dashed':
 29232                context.setLineDash([4, 2]);
 29233                break;
 29234  
 29235              case 'double':
 29236                context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
 29237  
 29238                context.setLineDash([]);
 29239                break;
 29240  
 29241              case 'solid':
 29242                context.setLineDash([]);
 29243                break;
 29244            }
 29245          }
 29246  
 29247          context.strokeRect(bgX, bgY, bgW, bgH);
 29248  
 29249          if (textBorderStyle === 'double') {
 29250            var whiteWidth = textBorderWidth / 2;
 29251            context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
 29252          }
 29253  
 29254          if (context.setLineDash) {
 29255            // for very outofdate browsers
 29256            context.setLineDash([]);
 29257          }
 29258  
 29259          context.lineWidth = textLineWidth;
 29260          context.strokeStyle = textStroke;
 29261        }
 29262      }
 29263  
 29264      var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
 29265  
 29266      if (lineWidth > 0) {
 29267        context.lineWidth = lineWidth;
 29268      }
 29269  
 29270      if (ele.pstyle('text-wrap').value === 'wrap') {
 29271        var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
 29272        var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
 29273        var halfTextW = textW / 2;
 29274        var justification = this.getLabelJustification(ele);
 29275  
 29276        if (justification === 'auto') ; else if (halign === 'left') {
 29277          // auto justification : right
 29278          if (justification === 'left') {
 29279            textX += -textW;
 29280          } else if (justification === 'center') {
 29281            textX += -halfTextW;
 29282          } // else same as auto
 29283  
 29284        } else if (halign === 'center') {
 29285          // auto justfication : center
 29286          if (justification === 'left') {
 29287            textX += -halfTextW;
 29288          } else if (justification === 'right') {
 29289            textX += halfTextW;
 29290          } // else same as auto
 29291  
 29292        } else if (halign === 'right') {
 29293          // auto justification : left
 29294          if (justification === 'center') {
 29295            textX += halfTextW;
 29296          } else if (justification === 'right') {
 29297            textX += textW;
 29298          } // else same as auto
 29299  
 29300        }
 29301  
 29302        switch (valign) {
 29303          case 'top':
 29304            textY -= (lines.length - 1) * lineHeight;
 29305            break;
 29306  
 29307          case 'center':
 29308          case 'bottom':
 29309            textY -= (lines.length - 1) * lineHeight;
 29310            break;
 29311        }
 29312  
 29313        for (var l = 0; l < lines.length; l++) {
 29314          if (lineWidth > 0) {
 29315            context.strokeText(lines[l], textX, textY);
 29316          }
 29317  
 29318          context.fillText(lines[l], textX, textY);
 29319          textY += lineHeight;
 29320        }
 29321      } else {
 29322        if (lineWidth > 0) {
 29323          context.strokeText(text, textX, textY);
 29324        }
 29325  
 29326        context.fillText(text, textX, textY);
 29327      }
 29328  
 29329      if (theta !== 0) {
 29330        context.rotate(-theta);
 29331        context.translate(-orgTextX, -orgTextY);
 29332      }
 29333    }
 29334  };
 29335  
 29336  /* global Path2D */
 29337  var CRp$5 = {};
 29338  
 29339  CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
 29340    var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
 29341    var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
 29342    var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
 29343    var r = this;
 29344    var nodeWidth, nodeHeight;
 29345    var _p = node._private;
 29346    var rs = _p.rscratch;
 29347    var pos = node.position();
 29348  
 29349    if (!number(pos.x) || !number(pos.y)) {
 29350      return; // can't draw node with undefined position
 29351    }
 29352  
 29353    if (shouldDrawOpacity && !node.visible()) {
 29354      return;
 29355    }
 29356  
 29357    var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
 29358    var usePaths = r.usePaths();
 29359    var path;
 29360    var pathCacheHit = false;
 29361    var padding = node.padding();
 29362    nodeWidth = node.width() + 2 * padding;
 29363    nodeHeight = node.height() + 2 * padding; //
 29364    // setup shift
 29365  
 29366    var bb;
 29367  
 29368    if (shiftToOriginWithBb) {
 29369      bb = shiftToOriginWithBb;
 29370      context.translate(-bb.x1, -bb.y1);
 29371    } //
 29372    // load bg image
 29373  
 29374  
 29375    var bgImgProp = node.pstyle('background-image');
 29376    var urls = bgImgProp.value;
 29377    var urlDefined = new Array(urls.length);
 29378    var image = new Array(urls.length);
 29379    var numImages = 0;
 29380  
 29381    for (var i = 0; i < urls.length; i++) {
 29382      var url = urls[i];
 29383      var defd = urlDefined[i] = url != null && url !== 'none';
 29384  
 29385      if (defd) {
 29386        var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
 29387        numImages++; // get image, and if not loaded then ask to redraw when later loaded
 29388  
 29389        image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
 29390          _p.backgroundTimestamp = Date.now();
 29391          node.emitAndNotify('background');
 29392        });
 29393      }
 29394    } //
 29395    // setup styles
 29396  
 29397  
 29398    var darkness = node.pstyle('background-blacken').value;
 29399    var borderWidth = node.pstyle('border-width').pfValue;
 29400    var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
 29401    var borderColor = node.pstyle('border-color').value;
 29402    var borderStyle = node.pstyle('border-style').value;
 29403    var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
 29404    context.lineJoin = 'miter'; // so borders are square with the node shape
 29405  
 29406    var setupShapeColor = function setupShapeColor() {
 29407      var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
 29408      r.eleFillStyle(context, node, bgOpy);
 29409    };
 29410  
 29411    var setupBorderColor = function setupBorderColor() {
 29412      var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
 29413      r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
 29414    }; //
 29415    // setup shape
 29416  
 29417  
 29418    var styleShape = node.pstyle('shape').strValue;
 29419    var shapePts = node.pstyle('shape-polygon-points').pfValue;
 29420  
 29421    if (usePaths) {
 29422      context.translate(pos.x, pos.y);
 29423      var pathCache = r.nodePathCache = r.nodePathCache || [];
 29424      var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
 29425      var cachedPath = pathCache[key];
 29426  
 29427      if (cachedPath != null) {
 29428        path = cachedPath;
 29429        pathCacheHit = true;
 29430        rs.pathCache = path;
 29431      } else {
 29432        path = new Path2D();
 29433        pathCache[key] = rs.pathCache = path;
 29434      }
 29435    }
 29436  
 29437    var drawShape = function drawShape() {
 29438      if (!pathCacheHit) {
 29439        var npos = pos;
 29440  
 29441        if (usePaths) {
 29442          npos = {
 29443            x: 0,
 29444            y: 0
 29445          };
 29446        }
 29447  
 29448        r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
 29449      }
 29450  
 29451      if (usePaths) {
 29452        context.fill(path);
 29453      } else {
 29454        context.fill();
 29455      }
 29456    };
 29457  
 29458    var drawImages = function drawImages() {
 29459      var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
 29460      var prevBging = _p.backgrounding;
 29461      var totalCompleted = 0;
 29462  
 29463      for (var _i = 0; _i < image.length; _i++) {
 29464        if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
 29465          totalCompleted++;
 29466          r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
 29467        }
 29468      }
 29469  
 29470      _p.backgrounding = !(totalCompleted === numImages);
 29471  
 29472      if (prevBging !== _p.backgrounding) {
 29473        // update style b/c :backgrounding state changed
 29474        node.updateStyle(false);
 29475      }
 29476    };
 29477  
 29478    var drawPie = function drawPie() {
 29479      var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
 29480      var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
 29481  
 29482      if (r.hasPie(node)) {
 29483        r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
 29484  
 29485        if (redrawShape) {
 29486          if (!usePaths) {
 29487            r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
 29488          }
 29489        }
 29490      }
 29491    };
 29492  
 29493    var darken = function darken() {
 29494      var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
 29495      var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
 29496      var c = darkness > 0 ? 0 : 255;
 29497  
 29498      if (darkness !== 0) {
 29499        r.colorFillStyle(context, c, c, c, opacity);
 29500  
 29501        if (usePaths) {
 29502          context.fill(path);
 29503        } else {
 29504          context.fill();
 29505        }
 29506      }
 29507    };
 29508  
 29509    var drawBorder = function drawBorder() {
 29510      if (borderWidth > 0) {
 29511        context.lineWidth = borderWidth;
 29512        context.lineCap = 'butt';
 29513  
 29514        if (context.setLineDash) {
 29515          // for very outofdate browsers
 29516          switch (borderStyle) {
 29517            case 'dotted':
 29518              context.setLineDash([1, 1]);
 29519              break;
 29520  
 29521            case 'dashed':
 29522              context.setLineDash([4, 2]);
 29523              break;
 29524  
 29525            case 'solid':
 29526            case 'double':
 29527              context.setLineDash([]);
 29528              break;
 29529          }
 29530        }
 29531  
 29532        if (usePaths) {
 29533          context.stroke(path);
 29534        } else {
 29535          context.stroke();
 29536        }
 29537  
 29538        if (borderStyle === 'double') {
 29539          context.lineWidth = borderWidth / 3;
 29540          var gco = context.globalCompositeOperation;
 29541          context.globalCompositeOperation = 'destination-out';
 29542  
 29543          if (usePaths) {
 29544            context.stroke(path);
 29545          } else {
 29546            context.stroke();
 29547          }
 29548  
 29549          context.globalCompositeOperation = gco;
 29550        } // reset in case we changed the border style
 29551  
 29552  
 29553        if (context.setLineDash) {
 29554          // for very outofdate browsers
 29555          context.setLineDash([]);
 29556        }
 29557      }
 29558    };
 29559  
 29560    var drawOverlay = function drawOverlay() {
 29561      if (shouldDrawOverlay) {
 29562        r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
 29563      }
 29564    };
 29565  
 29566    var drawText = function drawText() {
 29567      r.drawElementText(context, node, null, drawLabel);
 29568    };
 29569  
 29570    var ghost = node.pstyle('ghost').value === 'yes';
 29571  
 29572    if (ghost) {
 29573      var gx = node.pstyle('ghost-offset-x').pfValue;
 29574      var gy = node.pstyle('ghost-offset-y').pfValue;
 29575      var ghostOpacity = node.pstyle('ghost-opacity').value;
 29576      var effGhostOpacity = ghostOpacity * eleOpacity;
 29577      context.translate(gx, gy);
 29578      setupShapeColor(ghostOpacity * bgOpacity);
 29579      drawShape();
 29580      drawImages(effGhostOpacity);
 29581      drawPie(darkness !== 0 || borderWidth !== 0);
 29582      darken(effGhostOpacity);
 29583      setupBorderColor(ghostOpacity * borderOpacity);
 29584      drawBorder();
 29585      context.translate(-gx, -gy);
 29586    }
 29587  
 29588    setupShapeColor();
 29589    drawShape();
 29590    drawImages();
 29591    drawPie(darkness !== 0 || borderWidth !== 0);
 29592    darken();
 29593    setupBorderColor();
 29594    drawBorder();
 29595  
 29596    if (usePaths) {
 29597      context.translate(-pos.x, -pos.y);
 29598    }
 29599  
 29600    drawText();
 29601    drawOverlay(); //
 29602    // clean up shift
 29603  
 29604    if (shiftToOriginWithBb) {
 29605      context.translate(bb.x1, bb.y1);
 29606    }
 29607  };
 29608  
 29609  CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
 29610    var r = this;
 29611  
 29612    if (!node.visible()) {
 29613      return;
 29614    }
 29615  
 29616    var overlayPadding = node.pstyle('overlay-padding').pfValue;
 29617    var overlayOpacity = node.pstyle('overlay-opacity').value;
 29618    var overlayColor = node.pstyle('overlay-color').value;
 29619  
 29620    if (overlayOpacity > 0) {
 29621      pos = pos || node.position();
 29622  
 29623      if (nodeWidth == null || nodeHeight == null) {
 29624        var padding = node.padding();
 29625        nodeWidth = node.width() + 2 * padding;
 29626        nodeHeight = node.height() + 2 * padding;
 29627      }
 29628  
 29629      r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
 29630      r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
 29631      context.fill();
 29632    }
 29633  }; // does the node have at least one pie piece?
 29634  
 29635  
 29636  CRp$5.hasPie = function (node) {
 29637    node = node[0]; // ensure ele ref
 29638  
 29639    return node._private.hasPie;
 29640  };
 29641  
 29642  CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
 29643    node = node[0]; // ensure ele ref
 29644  
 29645    pos = pos || node.position();
 29646    var cyStyle = node.cy().style();
 29647    var pieSize = node.pstyle('pie-size');
 29648    var x = pos.x;
 29649    var y = pos.y;
 29650    var nodeW = node.width();
 29651    var nodeH = node.height();
 29652    var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
 29653  
 29654    var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
 29655  
 29656    var usePaths = this.usePaths();
 29657  
 29658    if (usePaths) {
 29659      x = 0;
 29660      y = 0;
 29661    }
 29662  
 29663    if (pieSize.units === '%') {
 29664      radius = radius * pieSize.pfValue;
 29665    } else if (pieSize.pfValue !== undefined) {
 29666      radius = pieSize.pfValue / 2;
 29667    }
 29668  
 29669    for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
 29670      // 1..N
 29671      var size = node.pstyle('pie-' + i + '-background-size').value;
 29672      var color = node.pstyle('pie-' + i + '-background-color').value;
 29673      var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
 29674      var percent = size / 100; // map integer range [0, 100] to [0, 1]
 29675      // percent can't push beyond 1
 29676  
 29677      if (percent + lastPercent > 1) {
 29678        percent = 1 - lastPercent;
 29679      }
 29680  
 29681      var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
 29682  
 29683      var angleDelta = 2 * Math.PI * percent;
 29684      var angleEnd = angleStart + angleDelta; // ignore if
 29685      // - zero size
 29686      // - we're already beyond the full circle
 29687      // - adding the current slice would go beyond the full circle
 29688  
 29689      if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
 29690        continue;
 29691      }
 29692  
 29693      context.beginPath();
 29694      context.moveTo(x, y);
 29695      context.arc(x, y, radius, angleStart, angleEnd);
 29696      context.closePath();
 29697      this.colorFillStyle(context, color[0], color[1], color[2], opacity);
 29698      context.fill();
 29699      lastPercent += percent;
 29700    }
 29701  };
 29702  
 29703  var CRp$6 = {};
 29704  var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
 29705  
 29706  CRp$6.getPixelRatio = function () {
 29707    var context = this.data.contexts[0];
 29708  
 29709    if (this.forcedPixelRatio != null) {
 29710      return this.forcedPixelRatio;
 29711    }
 29712  
 29713    var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
 29714    return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
 29715  };
 29716  
 29717  CRp$6.paintCache = function (context) {
 29718    var caches = this.paintCaches = this.paintCaches || [];
 29719    var needToCreateCache = true;
 29720    var cache;
 29721  
 29722    for (var i = 0; i < caches.length; i++) {
 29723      cache = caches[i];
 29724  
 29725      if (cache.context === context) {
 29726        needToCreateCache = false;
 29727        break;
 29728      }
 29729    }
 29730  
 29731    if (needToCreateCache) {
 29732      cache = {
 29733        context: context
 29734      };
 29735      caches.push(cache);
 29736    }
 29737  
 29738    return cache;
 29739  };
 29740  
 29741  CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
 29742    var gradientStyle;
 29743    var usePaths = this.usePaths();
 29744    var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
 29745        positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
 29746  
 29747    if (fill === 'radial-gradient') {
 29748      if (ele.isEdge()) {
 29749        var start = ele.sourceEndpoint(),
 29750            end = ele.targetEndpoint(),
 29751            mid = ele.midpoint();
 29752        var d1 = dist(start, mid);
 29753        var d2 = dist(end, mid);
 29754        gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
 29755      } else {
 29756        var pos = usePaths ? {
 29757          x: 0,
 29758          y: 0
 29759        } : ele.position(),
 29760            width = ele.paddedWidth(),
 29761            height = ele.paddedHeight();
 29762        gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
 29763      }
 29764    } else {
 29765      if (ele.isEdge()) {
 29766        var _start = ele.sourceEndpoint(),
 29767            _end = ele.targetEndpoint();
 29768  
 29769        gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
 29770      } else {
 29771        var _pos = usePaths ? {
 29772          x: 0,
 29773          y: 0
 29774        } : ele.position(),
 29775            _width = ele.paddedWidth(),
 29776            _height = ele.paddedHeight(),
 29777            halfWidth = _width / 2,
 29778            halfHeight = _height / 2;
 29779  
 29780        var direction = ele.pstyle('background-gradient-direction').value;
 29781  
 29782        switch (direction) {
 29783          case 'to-bottom':
 29784            gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
 29785            break;
 29786  
 29787          case 'to-top':
 29788            gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
 29789            break;
 29790  
 29791          case 'to-left':
 29792            gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
 29793            break;
 29794  
 29795          case 'to-right':
 29796            gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
 29797            break;
 29798  
 29799          case 'to-bottom-right':
 29800          case 'to-right-bottom':
 29801            gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
 29802            break;
 29803  
 29804          case 'to-top-right':
 29805          case 'to-right-top':
 29806            gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
 29807            break;
 29808  
 29809          case 'to-bottom-left':
 29810          case 'to-left-bottom':
 29811            gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
 29812            break;
 29813  
 29814          case 'to-top-left':
 29815          case 'to-left-top':
 29816            gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
 29817            break;
 29818        }
 29819      }
 29820    }
 29821  
 29822    if (!gradientStyle) return null; // invalid gradient style
 29823  
 29824    var hasPositions = positions.length === colors.length;
 29825    var length = colors.length;
 29826  
 29827    for (var i = 0; i < length; i++) {
 29828      gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
 29829    }
 29830  
 29831    return gradientStyle;
 29832  };
 29833  
 29834  CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
 29835    var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
 29836    if (!gradientStyle) return null; // error
 29837  
 29838    context.fillStyle = gradientStyle;
 29839  };
 29840  
 29841  CRp$6.colorFillStyle = function (context, r, g, b, a) {
 29842    context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
 29843    // var cache = this.paintCache(context);
 29844    // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
 29845    // if( cache.fillStyle !== fillStyle ){
 29846    //   context.fillStyle = cache.fillStyle = fillStyle;
 29847    // }
 29848  };
 29849  
 29850  CRp$6.eleFillStyle = function (context, ele, opacity) {
 29851    var backgroundFill = ele.pstyle('background-fill').value;
 29852  
 29853    if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
 29854      this.gradientFillStyle(context, ele, backgroundFill, opacity);
 29855    } else {
 29856      var backgroundColor = ele.pstyle('background-color').value;
 29857      this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
 29858    }
 29859  };
 29860  
 29861  CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
 29862    var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
 29863    if (!gradientStyle) return null; // error
 29864  
 29865    context.strokeStyle = gradientStyle;
 29866  };
 29867  
 29868  CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
 29869    context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
 29870    // var cache = this.paintCache(context);
 29871    // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
 29872    // if( cache.strokeStyle !== strokeStyle ){
 29873    //   context.strokeStyle = cache.strokeStyle = strokeStyle;
 29874    // }
 29875  };
 29876  
 29877  CRp$6.eleStrokeStyle = function (context, ele, opacity) {
 29878    var lineFill = ele.pstyle('line-fill').value;
 29879  
 29880    if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
 29881      this.gradientStrokeStyle(context, ele, lineFill, opacity);
 29882    } else {
 29883      var lineColor = ele.pstyle('line-color').value;
 29884      this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
 29885    }
 29886  }; // Resize canvas
 29887  
 29888  
 29889  CRp$6.matchCanvasSize = function (container) {
 29890    var r = this;
 29891    var data = r.data;
 29892    var bb = r.findContainerClientCoords();
 29893    var width = bb[2];
 29894    var height = bb[3];
 29895    var pixelRatio = r.getPixelRatio();
 29896    var mbPxRatio = r.motionBlurPxRatio;
 29897  
 29898    if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
 29899      pixelRatio = mbPxRatio;
 29900    }
 29901  
 29902    var canvasWidth = width * pixelRatio;
 29903    var canvasHeight = height * pixelRatio;
 29904    var canvas;
 29905  
 29906    if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
 29907      return; // save cycles if same
 29908    }
 29909  
 29910    r.fontCaches = null; // resizing resets the style
 29911  
 29912    var canvasContainer = data.canvasContainer;
 29913    canvasContainer.style.width = width + 'px';
 29914    canvasContainer.style.height = height + 'px';
 29915  
 29916    for (var i = 0; i < r.CANVAS_LAYERS; i++) {
 29917      canvas = data.canvases[i];
 29918      canvas.width = canvasWidth;
 29919      canvas.height = canvasHeight;
 29920      canvas.style.width = width + 'px';
 29921      canvas.style.height = height + 'px';
 29922    }
 29923  
 29924    for (var i = 0; i < r.BUFFER_COUNT; i++) {
 29925      canvas = data.bufferCanvases[i];
 29926      canvas.width = canvasWidth;
 29927      canvas.height = canvasHeight;
 29928      canvas.style.width = width + 'px';
 29929      canvas.style.height = height + 'px';
 29930    }
 29931  
 29932    r.textureMult = 1;
 29933  
 29934    if (pixelRatio <= 1) {
 29935      canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
 29936      r.textureMult = 2;
 29937      canvas.width = canvasWidth * r.textureMult;
 29938      canvas.height = canvasHeight * r.textureMult;
 29939    }
 29940  
 29941    r.canvasWidth = canvasWidth;
 29942    r.canvasHeight = canvasHeight;
 29943  };
 29944  
 29945  CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
 29946    this.render({
 29947      forcedContext: cxt,
 29948      forcedZoom: zoom,
 29949      forcedPan: pan,
 29950      drawAllLayers: true,
 29951      forcedPxRatio: pxRatio
 29952    });
 29953  };
 29954  
 29955  CRp$6.render = function (options) {
 29956    options = options || staticEmptyObject();
 29957    var forcedContext = options.forcedContext;
 29958    var drawAllLayers = options.drawAllLayers;
 29959    var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
 29960    var forcedZoom = options.forcedZoom;
 29961    var forcedPan = options.forcedPan;
 29962    var r = this;
 29963    var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
 29964    var cy = r.cy;
 29965    var data = r.data;
 29966    var needDraw = data.canvasNeedsRedraw;
 29967    var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
 29968    var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
 29969    var mbPxRatio = r.motionBlurPxRatio;
 29970    var hasCompoundNodes = cy.hasCompoundNodes();
 29971    var inNodeDragGesture = r.hoverData.draggingEles;
 29972    var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
 29973    motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
 29974    var motionBlurFadeEffect = motionBlur;
 29975  
 29976    if (!forcedContext) {
 29977      if (r.prevPxRatio !== pixelRatio) {
 29978        r.invalidateContainerClientCoordsCache();
 29979        r.matchCanvasSize(r.container);
 29980        r.redrawHint('eles', true);
 29981        r.redrawHint('drag', true);
 29982      }
 29983  
 29984      r.prevPxRatio = pixelRatio;
 29985    }
 29986  
 29987    if (!forcedContext && r.motionBlurTimeout) {
 29988      clearTimeout(r.motionBlurTimeout);
 29989    }
 29990  
 29991    if (motionBlur) {
 29992      if (r.mbFrames == null) {
 29993        r.mbFrames = 0;
 29994      }
 29995  
 29996      r.mbFrames++;
 29997  
 29998      if (r.mbFrames < 3) {
 29999        // need several frames before even high quality motionblur
 30000        motionBlurFadeEffect = false;
 30001      } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
 30002  
 30003  
 30004      if (r.mbFrames > r.minMbLowQualFrames) {
 30005        //r.fullQualityMb = false;
 30006        r.motionBlurPxRatio = r.mbPxRBlurry;
 30007      }
 30008    }
 30009  
 30010    if (r.clearingMotionBlur) {
 30011      r.motionBlurPxRatio = 1;
 30012    } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
 30013    // because a rogue async texture frame would clear needDraw
 30014  
 30015  
 30016    if (r.textureDrawLastFrame && !textureDraw) {
 30017      needDraw[r.NODE] = true;
 30018      needDraw[r.SELECT_BOX] = true;
 30019    }
 30020  
 30021    var style = cy.style();
 30022    var zoom = cy.zoom();
 30023    var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
 30024    var pan = cy.pan();
 30025    var effectivePan = {
 30026      x: pan.x,
 30027      y: pan.y
 30028    };
 30029    var vp = {
 30030      zoom: zoom,
 30031      pan: {
 30032        x: pan.x,
 30033        y: pan.y
 30034      }
 30035    };
 30036    var prevVp = r.prevViewport;
 30037    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)
 30038  
 30039    if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
 30040      r.motionBlurPxRatio = 1;
 30041    }
 30042  
 30043    if (forcedPan) {
 30044      effectivePan = forcedPan;
 30045    } // apply pixel ratio
 30046  
 30047  
 30048    effectiveZoom *= pixelRatio;
 30049    effectivePan.x *= pixelRatio;
 30050    effectivePan.y *= pixelRatio;
 30051    var eles = r.getCachedZSortedEles();
 30052  
 30053    function mbclear(context, x, y, w, h) {
 30054      var gco = context.globalCompositeOperation;
 30055      context.globalCompositeOperation = 'destination-out';
 30056      r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
 30057      context.fillRect(x, y, w, h);
 30058      context.globalCompositeOperation = gco;
 30059    }
 30060  
 30061    function setContextTransform(context, clear) {
 30062      var ePan, eZoom, w, h;
 30063  
 30064      if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
 30065        ePan = {
 30066          x: pan.x * mbPxRatio,
 30067          y: pan.y * mbPxRatio
 30068        };
 30069        eZoom = zoom * mbPxRatio;
 30070        w = r.canvasWidth * mbPxRatio;
 30071        h = r.canvasHeight * mbPxRatio;
 30072      } else {
 30073        ePan = effectivePan;
 30074        eZoom = effectiveZoom;
 30075        w = r.canvasWidth;
 30076        h = r.canvasHeight;
 30077      }
 30078  
 30079      context.setTransform(1, 0, 0, 1, 0, 0);
 30080  
 30081      if (clear === 'motionBlur') {
 30082        mbclear(context, 0, 0, w, h);
 30083      } else if (!forcedContext && (clear === undefined || clear)) {
 30084        context.clearRect(0, 0, w, h);
 30085      }
 30086  
 30087      if (!drawAllLayers) {
 30088        context.translate(ePan.x, ePan.y);
 30089        context.scale(eZoom, eZoom);
 30090      }
 30091  
 30092      if (forcedPan) {
 30093        context.translate(forcedPan.x, forcedPan.y);
 30094      }
 30095  
 30096      if (forcedZoom) {
 30097        context.scale(forcedZoom, forcedZoom);
 30098      }
 30099    }
 30100  
 30101    if (!textureDraw) {
 30102      r.textureDrawLastFrame = false;
 30103    }
 30104  
 30105    if (textureDraw) {
 30106      r.textureDrawLastFrame = true;
 30107  
 30108      if (!r.textureCache) {
 30109        r.textureCache = {};
 30110        r.textureCache.bb = cy.mutableElements().boundingBox();
 30111        r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
 30112        var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
 30113        cxt.setTransform(1, 0, 0, 1, 0, 0);
 30114        cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
 30115        r.render({
 30116          forcedContext: cxt,
 30117          drawOnlyNodeLayer: true,
 30118          forcedPxRatio: pixelRatio * r.textureMult
 30119        });
 30120        var vp = r.textureCache.viewport = {
 30121          zoom: cy.zoom(),
 30122          pan: cy.pan(),
 30123          width: r.canvasWidth,
 30124          height: r.canvasHeight
 30125        };
 30126        vp.mpan = {
 30127          x: (0 - vp.pan.x) / vp.zoom,
 30128          y: (0 - vp.pan.y) / vp.zoom
 30129        };
 30130      }
 30131  
 30132      needDraw[r.DRAG] = false;
 30133      needDraw[r.NODE] = false;
 30134      var context = data.contexts[r.NODE];
 30135      var texture = r.textureCache.texture;
 30136      var vp = r.textureCache.viewport;
 30137      context.setTransform(1, 0, 0, 1, 0, 0);
 30138  
 30139      if (motionBlur) {
 30140        mbclear(context, 0, 0, vp.width, vp.height);
 30141      } else {
 30142        context.clearRect(0, 0, vp.width, vp.height);
 30143      }
 30144  
 30145      var outsideBgColor = style.core('outside-texture-bg-color').value;
 30146      var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
 30147      r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
 30148      context.fillRect(0, 0, vp.width, vp.height);
 30149      var zoom = cy.zoom();
 30150      setContextTransform(context, false);
 30151      context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
 30152      context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
 30153    } else if (r.textureOnViewport && !forcedContext) {
 30154      // clear the cache since we don't need it
 30155      r.textureCache = null;
 30156    }
 30157  
 30158    var extent = cy.extent();
 30159    var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
 30160    var hideEdges = r.hideEdgesOnViewport && vpManip;
 30161    var needMbClear = [];
 30162    needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
 30163  
 30164    if (needMbClear[r.NODE]) {
 30165      r.clearedForMotionBlur[r.NODE] = true;
 30166    }
 30167  
 30168    needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
 30169  
 30170    if (needMbClear[r.DRAG]) {
 30171      r.clearedForMotionBlur[r.DRAG] = true;
 30172    }
 30173  
 30174    if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
 30175      var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
 30176      var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
 30177      var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
 30178      setContextTransform(context, clear);
 30179  
 30180      if (hideEdges) {
 30181        r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
 30182      } else {
 30183        r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
 30184      }
 30185  
 30186      if (r.debug) {
 30187        r.drawDebugPoints(context, eles.nondrag);
 30188      }
 30189  
 30190      if (!drawAllLayers && !motionBlur) {
 30191        needDraw[r.NODE] = false;
 30192      }
 30193    }
 30194  
 30195    if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
 30196      var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
 30197      var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
 30198      setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
 30199  
 30200      if (hideEdges) {
 30201        r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
 30202      } else {
 30203        r.drawCachedElements(context, eles.drag, pixelRatio, extent);
 30204      }
 30205  
 30206      if (r.debug) {
 30207        r.drawDebugPoints(context, eles.drag);
 30208      }
 30209  
 30210      if (!drawAllLayers && !motionBlur) {
 30211        needDraw[r.DRAG] = false;
 30212      }
 30213    }
 30214  
 30215    if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
 30216      var context = forcedContext || data.contexts[r.SELECT_BOX];
 30217      setContextTransform(context);
 30218  
 30219      if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
 30220        var zoom = r.cy.zoom();
 30221        var borderWidth = style.core('selection-box-border-width').value / zoom;
 30222        context.lineWidth = borderWidth;
 30223        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 + ')';
 30224        context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
 30225  
 30226        if (borderWidth > 0) {
 30227          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 + ')';
 30228          context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
 30229        }
 30230      }
 30231  
 30232      if (data.bgActivePosistion && !r.hoverData.selecting) {
 30233        var zoom = r.cy.zoom();
 30234        var pos = data.bgActivePosistion;
 30235        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 + ')';
 30236        context.beginPath();
 30237        context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
 30238        context.fill();
 30239      }
 30240  
 30241      var timeToRender = r.lastRedrawTime;
 30242  
 30243      if (r.showFps && timeToRender) {
 30244        timeToRender = Math.round(timeToRender);
 30245        var fps = Math.round(1000 / timeToRender);
 30246        context.setTransform(1, 0, 0, 1, 0, 0);
 30247        context.fillStyle = 'rgba(255, 0, 0, 0.75)';
 30248        context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
 30249        context.lineWidth = 1;
 30250        context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
 30251        var maxFps = 60;
 30252        context.strokeRect(0, 30, 250, 20);
 30253        context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
 30254      }
 30255  
 30256      if (!drawAllLayers) {
 30257        needDraw[r.SELECT_BOX] = false;
 30258      }
 30259    } // motionblur: blit rendered blurry frames
 30260  
 30261  
 30262    if (motionBlur && mbPxRatio !== 1) {
 30263      var cxtNode = data.contexts[r.NODE];
 30264      var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
 30265      var cxtDrag = data.contexts[r.DRAG];
 30266      var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
 30267  
 30268      var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
 30269        cxt.setTransform(1, 0, 0, 1, 0, 0);
 30270  
 30271        if (needClear || !motionBlurFadeEffect) {
 30272          cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
 30273        } else {
 30274          mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
 30275        }
 30276  
 30277        var pxr = mbPxRatio;
 30278        cxt.drawImage(txt, // img
 30279        0, 0, // sx, sy
 30280        r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
 30281        0, 0, // x, y
 30282        r.canvasWidth, r.canvasHeight // w, h
 30283        );
 30284      };
 30285  
 30286      if (needDraw[r.NODE] || needMbClear[r.NODE]) {
 30287        drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
 30288        needDraw[r.NODE] = false;
 30289      }
 30290  
 30291      if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
 30292        drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
 30293        needDraw[r.DRAG] = false;
 30294      }
 30295    }
 30296  
 30297    r.prevViewport = vp;
 30298  
 30299    if (r.clearingMotionBlur) {
 30300      r.clearingMotionBlur = false;
 30301      r.motionBlurCleared = true;
 30302      r.motionBlur = true;
 30303    }
 30304  
 30305    if (motionBlur) {
 30306      r.motionBlurTimeout = setTimeout(function () {
 30307        r.motionBlurTimeout = null;
 30308        r.clearedForMotionBlur[r.NODE] = false;
 30309        r.clearedForMotionBlur[r.DRAG] = false;
 30310        r.motionBlur = false;
 30311        r.clearingMotionBlur = !textureDraw;
 30312        r.mbFrames = 0;
 30313        needDraw[r.NODE] = true;
 30314        needDraw[r.DRAG] = true;
 30315        r.redraw();
 30316      }, motionBlurDelay);
 30317    }
 30318  
 30319    if (!forcedContext) {
 30320      cy.emit('render');
 30321    }
 30322  };
 30323  
 30324  var CRp$7 = {}; // @O Polygon drawing
 30325  
 30326  CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
 30327    var halfW = width / 2;
 30328    var halfH = height / 2;
 30329  
 30330    if (context.beginPath) {
 30331      context.beginPath();
 30332    }
 30333  
 30334    context.moveTo(x + halfW * points[0], y + halfH * points[1]);
 30335  
 30336    for (var i = 1; i < points.length / 2; i++) {
 30337      context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
 30338    }
 30339  
 30340    context.closePath();
 30341  };
 30342  
 30343  CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
 30344    var halfW = width / 2;
 30345    var halfH = height / 2;
 30346    var cornerRadius = getRoundPolygonRadius(width, height);
 30347  
 30348    if (context.beginPath) {
 30349      context.beginPath();
 30350    }
 30351  
 30352    for (var _i = 0; _i < points.length / 4; _i++) {
 30353      var sourceUv = void 0,
 30354          destUv = void 0;
 30355  
 30356      if (_i === 0) {
 30357        sourceUv = points.length - 2;
 30358      } else {
 30359        sourceUv = _i * 4 - 2;
 30360      }
 30361  
 30362      destUv = _i * 4 + 2;
 30363      var px = x + halfW * points[_i * 4];
 30364      var py = y + halfH * points[_i * 4 + 1];
 30365      var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
 30366      var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
 30367      var cp0x = px - offset * points[sourceUv];
 30368      var cp0y = py - offset * points[sourceUv + 1];
 30369      var cp1x = px + offset * points[destUv];
 30370      var cp1y = py + offset * points[destUv + 1];
 30371  
 30372      if (_i === 0) {
 30373        context.moveTo(cp0x, cp0y);
 30374      } else {
 30375        context.lineTo(cp0x, cp0y);
 30376      }
 30377  
 30378      context.arcTo(px, py, cp1x, cp1y, cornerRadius);
 30379    }
 30380  
 30381    context.closePath();
 30382  }; // Round rectangle drawing
 30383  
 30384  
 30385  CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
 30386    var halfWidth = width / 2;
 30387    var halfHeight = height / 2;
 30388    var cornerRadius = getRoundRectangleRadius(width, height);
 30389  
 30390    if (context.beginPath) {
 30391      context.beginPath();
 30392    } // Start at top middle
 30393  
 30394  
 30395    context.moveTo(x, y - halfHeight); // Arc from middle top to right side
 30396  
 30397    context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
 30398  
 30399    context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
 30400  
 30401    context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
 30402  
 30403    context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
 30404  
 30405    context.lineTo(x, y - halfHeight);
 30406    context.closePath();
 30407  };
 30408  
 30409  CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
 30410    var halfWidth = width / 2;
 30411    var halfHeight = height / 2;
 30412    var cornerRadius = getRoundRectangleRadius(width, height);
 30413  
 30414    if (context.beginPath) {
 30415      context.beginPath();
 30416    } // Start at top middle
 30417  
 30418  
 30419    context.moveTo(x, y - halfHeight);
 30420    context.lineTo(x + halfWidth, y - halfHeight);
 30421    context.lineTo(x + halfWidth, y);
 30422    context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
 30423    context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
 30424    context.lineTo(x - halfWidth, y - halfHeight);
 30425    context.lineTo(x, y - halfHeight);
 30426    context.closePath();
 30427  };
 30428  
 30429  CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
 30430    var halfWidth = width / 2;
 30431    var halfHeight = height / 2;
 30432    var cornerLength = getCutRectangleCornerLength();
 30433  
 30434    if (context.beginPath) {
 30435      context.beginPath();
 30436    }
 30437  
 30438    context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
 30439    context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
 30440    context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
 30441    context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
 30442    context.lineTo(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.closePath();
 30447  };
 30448  
 30449  CRp$7.drawBarrelPath = function (context, x, y, width, height) {
 30450    var halfWidth = width / 2;
 30451    var halfHeight = height / 2;
 30452    var xBegin = x - halfWidth;
 30453    var xEnd = x + halfWidth;
 30454    var yBegin = y - halfHeight;
 30455    var yEnd = y + halfHeight;
 30456    var barrelCurveConstants = getBarrelCurveConstants(width, height);
 30457    var wOffset = barrelCurveConstants.widthOffset;
 30458    var hOffset = barrelCurveConstants.heightOffset;
 30459    var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
 30460  
 30461    if (context.beginPath) {
 30462      context.beginPath();
 30463    }
 30464  
 30465    context.moveTo(xBegin, yBegin + hOffset);
 30466    context.lineTo(xBegin, yEnd - hOffset);
 30467    context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
 30468    context.lineTo(xEnd - wOffset, yEnd);
 30469    context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
 30470    context.lineTo(xEnd, yBegin + hOffset);
 30471    context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
 30472    context.lineTo(xBegin + wOffset, yBegin);
 30473    context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
 30474    context.closePath();
 30475  };
 30476  
 30477  var sin0 = Math.sin(0);
 30478  var cos0 = Math.cos(0);
 30479  var sin = {};
 30480  var cos = {};
 30481  var ellipseStepSize = Math.PI / 40;
 30482  
 30483  for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
 30484    sin[i] = Math.sin(i);
 30485    cos[i] = Math.cos(i);
 30486  }
 30487  
 30488  CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
 30489    if (context.beginPath) {
 30490      context.beginPath();
 30491    }
 30492  
 30493    if (context.ellipse) {
 30494      context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
 30495    } else {
 30496      var xPos, yPos;
 30497      var rw = width / 2;
 30498      var rh = height / 2;
 30499  
 30500      for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
 30501        xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
 30502        yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
 30503  
 30504        if (i === 0) {
 30505          context.moveTo(xPos, yPos);
 30506        } else {
 30507          context.lineTo(xPos, yPos);
 30508        }
 30509      }
 30510    }
 30511  
 30512    context.closePath();
 30513  };
 30514  
 30515  /* global atob, ArrayBuffer, Uint8Array, Blob */
 30516  var CRp$8 = {};
 30517  
 30518  CRp$8.createBuffer = function (w, h) {
 30519    var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
 30520  
 30521    buffer.width = w;
 30522    buffer.height = h;
 30523    return [buffer, buffer.getContext('2d')];
 30524  };
 30525  
 30526  CRp$8.bufferCanvasImage = function (options) {
 30527    var cy = this.cy;
 30528    var eles = cy.mutableElements();
 30529    var bb = eles.boundingBox();
 30530    var ctrRect = this.findContainerClientCoords();
 30531    var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
 30532    var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
 30533    var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
 30534    var pxRatio = this.getPixelRatio();
 30535    var scale = 1;
 30536  
 30537    if (options.scale !== undefined) {
 30538      width *= options.scale;
 30539      height *= options.scale;
 30540      scale = options.scale;
 30541    } else if (specdMaxDims) {
 30542      var maxScaleW = Infinity;
 30543      var maxScaleH = Infinity;
 30544  
 30545      if (number(options.maxWidth)) {
 30546        maxScaleW = scale * options.maxWidth / width;
 30547      }
 30548  
 30549      if (number(options.maxHeight)) {
 30550        maxScaleH = scale * options.maxHeight / height;
 30551      }
 30552  
 30553      scale = Math.min(maxScaleW, maxScaleH);
 30554      width *= scale;
 30555      height *= scale;
 30556    }
 30557  
 30558    if (!specdMaxDims) {
 30559      width *= pxRatio;
 30560      height *= pxRatio;
 30561      scale *= pxRatio;
 30562    }
 30563  
 30564    var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
 30565  
 30566    buffCanvas.width = width;
 30567    buffCanvas.height = height;
 30568    buffCanvas.style.width = width + 'px';
 30569    buffCanvas.style.height = height + 'px';
 30570    var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
 30571  
 30572    if (width > 0 && height > 0) {
 30573      buffCxt.clearRect(0, 0, width, height);
 30574      buffCxt.globalCompositeOperation = 'source-over';
 30575      var zsortedEles = this.getCachedZSortedEles();
 30576  
 30577      if (options.full) {
 30578        // draw the full bounds of the graph
 30579        buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
 30580        buffCxt.scale(scale, scale);
 30581        this.drawElements(buffCxt, zsortedEles);
 30582        buffCxt.scale(1 / scale, 1 / scale);
 30583        buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
 30584      } else {
 30585        // draw the current view
 30586        var pan = cy.pan();
 30587        var translation = {
 30588          x: pan.x * scale,
 30589          y: pan.y * scale
 30590        };
 30591        scale *= cy.zoom();
 30592        buffCxt.translate(translation.x, translation.y);
 30593        buffCxt.scale(scale, scale);
 30594        this.drawElements(buffCxt, zsortedEles);
 30595        buffCxt.scale(1 / scale, 1 / scale);
 30596        buffCxt.translate(-translation.x, -translation.y);
 30597      } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
 30598  
 30599  
 30600      if (options.bg) {
 30601        buffCxt.globalCompositeOperation = 'destination-over';
 30602        buffCxt.fillStyle = options.bg;
 30603        buffCxt.rect(0, 0, width, height);
 30604        buffCxt.fill();
 30605      }
 30606    }
 30607  
 30608    return buffCanvas;
 30609  };
 30610  
 30611  function b64ToBlob(b64, mimeType) {
 30612    var bytes = atob(b64);
 30613    var buff = new ArrayBuffer(bytes.length);
 30614    var buffUint8 = new Uint8Array(buff);
 30615  
 30616    for (var i = 0; i < bytes.length; i++) {
 30617      buffUint8[i] = bytes.charCodeAt(i);
 30618    }
 30619  
 30620    return new Blob([buff], {
 30621      type: mimeType
 30622    });
 30623  }
 30624  
 30625  function b64UriToB64(b64uri) {
 30626    var i = b64uri.indexOf(',');
 30627    return b64uri.substr(i + 1);
 30628  }
 30629  
 30630  function output(options, canvas, mimeType) {
 30631    var getB64Uri = function getB64Uri() {
 30632      return canvas.toDataURL(mimeType, options.quality);
 30633    };
 30634  
 30635    switch (options.output) {
 30636      case 'blob-promise':
 30637        return new Promise$1(function (resolve, reject) {
 30638          try {
 30639            canvas.toBlob(function (blob) {
 30640              if (blob != null) {
 30641                resolve(blob);
 30642              } else {
 30643                reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
 30644              }
 30645            }, mimeType, options.quality);
 30646          } catch (err) {
 30647            reject(err);
 30648          }
 30649        });
 30650  
 30651      case 'blob':
 30652        return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
 30653  
 30654      case 'base64':
 30655        return b64UriToB64(getB64Uri());
 30656  
 30657      case 'base64uri':
 30658      default:
 30659        return getB64Uri();
 30660    }
 30661  }
 30662  
 30663  CRp$8.png = function (options) {
 30664    return output(options, this.bufferCanvasImage(options), 'image/png');
 30665  };
 30666  
 30667  CRp$8.jpg = function (options) {
 30668    return output(options, this.bufferCanvasImage(options), 'image/jpeg');
 30669  };
 30670  
 30671  var CRp$9 = {};
 30672  
 30673  CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
 30674    switch (name) {
 30675      case 'ellipse':
 30676        return this.drawEllipsePath(context, centerX, centerY, width, height);
 30677  
 30678      case 'polygon':
 30679        return this.drawPolygonPath(context, centerX, centerY, width, height, points);
 30680  
 30681      case 'round-polygon':
 30682        return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
 30683  
 30684      case 'roundrectangle':
 30685      case 'round-rectangle':
 30686        return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
 30687  
 30688      case 'cutrectangle':
 30689      case 'cut-rectangle':
 30690        return this.drawCutRectanglePath(context, centerX, centerY, width, height);
 30691  
 30692      case 'bottomroundrectangle':
 30693      case 'bottom-round-rectangle':
 30694        return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
 30695  
 30696      case 'barrel':
 30697        return this.drawBarrelPath(context, centerX, centerY, width, height);
 30698    }
 30699  };
 30700  
 30701  var CR = CanvasRenderer;
 30702  var CRp$a = CanvasRenderer.prototype;
 30703  CRp$a.CANVAS_LAYERS = 3; //
 30704  
 30705  CRp$a.SELECT_BOX = 0;
 30706  CRp$a.DRAG = 1;
 30707  CRp$a.NODE = 2;
 30708  CRp$a.BUFFER_COUNT = 3; //
 30709  
 30710  CRp$a.TEXTURE_BUFFER = 0;
 30711  CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
 30712  CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
 30713  
 30714  function CanvasRenderer(options) {
 30715    var r = this;
 30716    r.data = {
 30717      canvases: new Array(CRp$a.CANVAS_LAYERS),
 30718      contexts: new Array(CRp$a.CANVAS_LAYERS),
 30719      canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
 30720      bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
 30721      bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
 30722    };
 30723    var tapHlOffAttr = '-webkit-tap-highlight-color';
 30724    var tapHlOffStyle = 'rgba(0,0,0,0)';
 30725    r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
 30726  
 30727    var containerStyle = r.data.canvasContainer.style;
 30728    r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
 30729    containerStyle.position = 'relative';
 30730    containerStyle.zIndex = '0';
 30731    containerStyle.overflow = 'hidden';
 30732    var container = options.cy.container();
 30733    container.appendChild(r.data.canvasContainer);
 30734    container.style[tapHlOffAttr] = tapHlOffStyle;
 30735    var styleMap = {
 30736      '-webkit-user-select': 'none',
 30737      '-moz-user-select': '-moz-none',
 30738      'user-select': 'none',
 30739      '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
 30740      'outline-style': 'none'
 30741    };
 30742  
 30743    if (ms()) {
 30744      styleMap['-ms-touch-action'] = 'none';
 30745      styleMap['touch-action'] = 'none';
 30746    }
 30747  
 30748    for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
 30749      var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
 30750  
 30751      r.data.contexts[i] = canvas.getContext('2d');
 30752      Object.keys(styleMap).forEach(function (k) {
 30753        canvas.style[k] = styleMap[k];
 30754      });
 30755      canvas.style.position = 'absolute';
 30756      canvas.setAttribute('data-id', 'layer' + i);
 30757      canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
 30758      r.data.canvasContainer.appendChild(canvas);
 30759      r.data.canvasNeedsRedraw[i] = false;
 30760    }
 30761  
 30762    r.data.topCanvas = r.data.canvases[0];
 30763    r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
 30764    r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
 30765    r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
 30766  
 30767    for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
 30768      r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
 30769  
 30770      r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
 30771      r.data.bufferCanvases[i].style.position = 'absolute';
 30772      r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
 30773      r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
 30774      r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
 30775    }
 30776  
 30777    r.pathsEnabled = true;
 30778    var emptyBb = makeBoundingBox();
 30779  
 30780    var getBoxCenter = function getBoxCenter(bb) {
 30781      return {
 30782        x: (bb.x1 + bb.x2) / 2,
 30783        y: (bb.y1 + bb.y2) / 2
 30784      };
 30785    };
 30786  
 30787    var getCenterOffset = function getCenterOffset(bb) {
 30788      return {
 30789        x: -bb.w / 2,
 30790        y: -bb.h / 2
 30791      };
 30792    };
 30793  
 30794    var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
 30795      var _p = ele[0]._private;
 30796      var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
 30797      return !same;
 30798    };
 30799  
 30800    var getStyleKey = function getStyleKey(ele) {
 30801      return ele[0]._private.nodeKey;
 30802    };
 30803  
 30804    var getLabelKey = function getLabelKey(ele) {
 30805      return ele[0]._private.labelStyleKey;
 30806    };
 30807  
 30808    var getSourceLabelKey = function getSourceLabelKey(ele) {
 30809      return ele[0]._private.sourceLabelStyleKey;
 30810    };
 30811  
 30812    var getTargetLabelKey = function getTargetLabelKey(ele) {
 30813      return ele[0]._private.targetLabelStyleKey;
 30814    };
 30815  
 30816    var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
 30817      return r.drawElement(context, ele, bb, false, false, useEleOpacity);
 30818    };
 30819  
 30820    var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
 30821      return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
 30822    };
 30823  
 30824    var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
 30825      return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
 30826    };
 30827  
 30828    var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
 30829      return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
 30830    };
 30831  
 30832    var getElementBox = function getElementBox(ele) {
 30833      ele.boundingBox();
 30834      return ele[0]._private.bodyBounds;
 30835    };
 30836  
 30837    var getLabelBox = function getLabelBox(ele) {
 30838      ele.boundingBox();
 30839      return ele[0]._private.labelBounds.main || emptyBb;
 30840    };
 30841  
 30842    var getSourceLabelBox = function getSourceLabelBox(ele) {
 30843      ele.boundingBox();
 30844      return ele[0]._private.labelBounds.source || emptyBb;
 30845    };
 30846  
 30847    var getTargetLabelBox = function getTargetLabelBox(ele) {
 30848      ele.boundingBox();
 30849      return ele[0]._private.labelBounds.target || emptyBb;
 30850    };
 30851  
 30852    var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
 30853      return scaledLabelShown;
 30854    };
 30855  
 30856    var getElementRotationPoint = function getElementRotationPoint(ele) {
 30857      return getBoxCenter(getElementBox(ele));
 30858    };
 30859  
 30860    var addTextMargin = function addTextMargin(prefix, pt, ele) {
 30861      var pre = prefix ? prefix + '-' : '';
 30862      return {
 30863        x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
 30864        y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
 30865      };
 30866    };
 30867  
 30868    var getRsPt = function getRsPt(ele, x, y) {
 30869      var rs = ele[0]._private.rscratch;
 30870      return {
 30871        x: rs[x],
 30872        y: rs[y]
 30873      };
 30874    };
 30875  
 30876    var getLabelRotationPoint = function getLabelRotationPoint(ele) {
 30877      return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
 30878    };
 30879  
 30880    var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
 30881      return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
 30882    };
 30883  
 30884    var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
 30885      return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
 30886    };
 30887  
 30888    var getElementRotationOffset = function getElementRotationOffset(ele) {
 30889      return getCenterOffset(getElementBox(ele));
 30890    };
 30891  
 30892    var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
 30893      return getCenterOffset(getSourceLabelBox(ele));
 30894    };
 30895  
 30896    var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
 30897      return getCenterOffset(getTargetLabelBox(ele));
 30898    };
 30899  
 30900    var getLabelRotationOffset = function getLabelRotationOffset(ele) {
 30901      var bb = getLabelBox(ele);
 30902      var p = getCenterOffset(getLabelBox(ele));
 30903  
 30904      if (ele.isNode()) {
 30905        switch (ele.pstyle('text-halign').value) {
 30906          case 'left':
 30907            p.x = -bb.w;
 30908            break;
 30909  
 30910          case 'right':
 30911            p.x = 0;
 30912            break;
 30913        }
 30914  
 30915        switch (ele.pstyle('text-valign').value) {
 30916          case 'top':
 30917            p.y = -bb.h;
 30918            break;
 30919  
 30920          case 'bottom':
 30921            p.y = 0;
 30922            break;
 30923        }
 30924      }
 30925  
 30926      return p;
 30927    };
 30928  
 30929    var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
 30930      getKey: getStyleKey,
 30931      doesEleInvalidateKey: backgroundTimestampHasChanged,
 30932      drawElement: drawElement,
 30933      getBoundingBox: getElementBox,
 30934      getRotationPoint: getElementRotationPoint,
 30935      getRotationOffset: getElementRotationOffset,
 30936      allowEdgeTxrCaching: false,
 30937      allowParentTxrCaching: false
 30938    });
 30939    var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
 30940      getKey: getLabelKey,
 30941      drawElement: drawLabel,
 30942      getBoundingBox: getLabelBox,
 30943      getRotationPoint: getLabelRotationPoint,
 30944      getRotationOffset: getLabelRotationOffset,
 30945      isVisible: isLabelVisibleAtScale
 30946    });
 30947    var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
 30948      getKey: getSourceLabelKey,
 30949      drawElement: drawSourceLabel,
 30950      getBoundingBox: getSourceLabelBox,
 30951      getRotationPoint: getSourceLabelRotationPoint,
 30952      getRotationOffset: getSourceLabelRotationOffset,
 30953      isVisible: isLabelVisibleAtScale
 30954    });
 30955    var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
 30956      getKey: getTargetLabelKey,
 30957      drawElement: drawTargetLabel,
 30958      getBoundingBox: getTargetLabelBox,
 30959      getRotationPoint: getTargetLabelRotationPoint,
 30960      getRotationOffset: getTargetLabelRotationOffset,
 30961      isVisible: isLabelVisibleAtScale
 30962    });
 30963    var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
 30964    r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
 30965      // each cache should check for sub-key diff to see that the update affects that cache particularly
 30966      eleTxrCache.invalidateElements(eles);
 30967      lblTxrCache.invalidateElements(eles);
 30968      slbTxrCache.invalidateElements(eles);
 30969      tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
 30970  
 30971      lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
 30972  
 30973      for (var _i = 0; _i < eles.length; _i++) {
 30974        var _p = eles[_i]._private;
 30975        _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
 30976      }
 30977    });
 30978  
 30979    var refineInLayers = function refineInLayers(reqs) {
 30980      for (var i = 0; i < reqs.length; i++) {
 30981        lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
 30982      }
 30983    };
 30984  
 30985    eleTxrCache.onDequeue(refineInLayers);
 30986    lblTxrCache.onDequeue(refineInLayers);
 30987    slbTxrCache.onDequeue(refineInLayers);
 30988    tlbTxrCache.onDequeue(refineInLayers);
 30989  }
 30990  
 30991  CRp$a.redrawHint = function (group, bool) {
 30992    var r = this;
 30993  
 30994    switch (group) {
 30995      case 'eles':
 30996        r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
 30997        break;
 30998  
 30999      case 'drag':
 31000        r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
 31001        break;
 31002  
 31003      case 'select':
 31004        r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
 31005        break;
 31006    }
 31007  }; // whether to use Path2D caching for drawing
 31008  
 31009  
 31010  var pathsImpld = typeof Path2D !== 'undefined';
 31011  
 31012  CRp$a.path2dEnabled = function (on) {
 31013    if (on === undefined) {
 31014      return this.pathsEnabled;
 31015    }
 31016  
 31017    this.pathsEnabled = on ? true : false;
 31018  };
 31019  
 31020  CRp$a.usePaths = function () {
 31021    return pathsImpld && this.pathsEnabled;
 31022  };
 31023  
 31024  CRp$a.setImgSmoothing = function (context, bool) {
 31025    if (context.imageSmoothingEnabled != null) {
 31026      context.imageSmoothingEnabled = bool;
 31027    } else {
 31028      context.webkitImageSmoothingEnabled = bool;
 31029      context.mozImageSmoothingEnabled = bool;
 31030      context.msImageSmoothingEnabled = bool;
 31031    }
 31032  };
 31033  
 31034  CRp$a.getImgSmoothing = function (context) {
 31035    if (context.imageSmoothingEnabled != null) {
 31036      return context.imageSmoothingEnabled;
 31037    } else {
 31038      return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
 31039    }
 31040  };
 31041  
 31042  CRp$a.makeOffscreenCanvas = function (width, height) {
 31043    var canvas;
 31044  
 31045    if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
 31046      canvas = new OffscreenCanvas(width, height);
 31047    } else {
 31048      canvas = document.createElement('canvas'); // eslint-disable-line no-undef
 31049  
 31050      canvas.width = width;
 31051      canvas.height = height;
 31052    }
 31053  
 31054    return canvas;
 31055  };
 31056  
 31057  [CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
 31058    extend(CRp$a, props);
 31059  });
 31060  
 31061  var renderer = [{
 31062    name: 'null',
 31063    impl: NullRenderer
 31064  }, {
 31065    name: 'base',
 31066    impl: BR
 31067  }, {
 31068    name: 'canvas',
 31069    impl: CR
 31070  }];
 31071  
 31072  var incExts = [{
 31073    type: 'layout',
 31074    extensions: layout
 31075  }, {
 31076    type: 'renderer',
 31077    extensions: renderer
 31078  }];
 31079  
 31080  var extensions = {}; // registered modules for extensions, indexed by name
 31081  
 31082  var modules = {};
 31083  
 31084  function setExtension(type, name, registrant) {
 31085    var ext = registrant;
 31086  
 31087    var overrideErr = function overrideErr(field) {
 31088      error('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
 31089    };
 31090  
 31091    if (type === 'core') {
 31092      if (Core.prototype[name]) {
 31093        return overrideErr(name);
 31094      } else {
 31095        Core.prototype[name] = registrant;
 31096      }
 31097    } else if (type === 'collection') {
 31098      if (Collection.prototype[name]) {
 31099        return overrideErr(name);
 31100      } else {
 31101        Collection.prototype[name] = registrant;
 31102      }
 31103    } else if (type === 'layout') {
 31104      // fill in missing layout functions in the prototype
 31105      var Layout = function Layout(options) {
 31106        this.options = options;
 31107        registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
 31108  
 31109        if (!plainObject(this._private)) {
 31110          this._private = {};
 31111        }
 31112  
 31113        this._private.cy = options.cy;
 31114        this._private.listeners = [];
 31115        this.createEmitter();
 31116      };
 31117  
 31118      var layoutProto = Layout.prototype = Object.create(registrant.prototype);
 31119      var optLayoutFns = [];
 31120  
 31121      for (var i = 0; i < optLayoutFns.length; i++) {
 31122        var fnName = optLayoutFns[i];
 31123  
 31124        layoutProto[fnName] = layoutProto[fnName] || function () {
 31125          return this;
 31126        };
 31127      } // either .start() or .run() is defined, so autogen the other
 31128  
 31129  
 31130      if (layoutProto.start && !layoutProto.run) {
 31131        layoutProto.run = function () {
 31132          this.start();
 31133          return this;
 31134        };
 31135      } else if (!layoutProto.start && layoutProto.run) {
 31136        layoutProto.start = function () {
 31137          this.run();
 31138          return this;
 31139        };
 31140      }
 31141  
 31142      var regStop = registrant.prototype.stop;
 31143  
 31144      layoutProto.stop = function () {
 31145        var opts = this.options;
 31146  
 31147        if (opts && opts.animate) {
 31148          var anis = this.animations;
 31149  
 31150          if (anis) {
 31151            for (var _i = 0; _i < anis.length; _i++) {
 31152              anis[_i].stop();
 31153            }
 31154          }
 31155        }
 31156  
 31157        if (regStop) {
 31158          regStop.call(this);
 31159        } else {
 31160          this.emit('layoutstop');
 31161        }
 31162  
 31163        return this;
 31164      };
 31165  
 31166      if (!layoutProto.destroy) {
 31167        layoutProto.destroy = function () {
 31168          return this;
 31169        };
 31170      }
 31171  
 31172      layoutProto.cy = function () {
 31173        return this._private.cy;
 31174      };
 31175  
 31176      var getCy = function getCy(layout) {
 31177        return layout._private.cy;
 31178      };
 31179  
 31180      var emitterOpts = {
 31181        addEventFields: function addEventFields(layout, evt) {
 31182          evt.layout = layout;
 31183          evt.cy = getCy(layout);
 31184          evt.target = layout;
 31185        },
 31186        bubble: function bubble() {
 31187          return true;
 31188        },
 31189        parent: function parent(layout) {
 31190          return getCy(layout);
 31191        }
 31192      };
 31193      extend(layoutProto, {
 31194        createEmitter: function createEmitter() {
 31195          this._private.emitter = new Emitter(emitterOpts, this);
 31196          return this;
 31197        },
 31198        emitter: function emitter() {
 31199          return this._private.emitter;
 31200        },
 31201        on: function on(evt, cb) {
 31202          this.emitter().on(evt, cb);
 31203          return this;
 31204        },
 31205        one: function one(evt, cb) {
 31206          this.emitter().one(evt, cb);
 31207          return this;
 31208        },
 31209        once: function once(evt, cb) {
 31210          this.emitter().one(evt, cb);
 31211          return this;
 31212        },
 31213        removeListener: function removeListener(evt, cb) {
 31214          this.emitter().removeListener(evt, cb);
 31215          return this;
 31216        },
 31217        removeAllListeners: function removeAllListeners() {
 31218          this.emitter().removeAllListeners();
 31219          return this;
 31220        },
 31221        emit: function emit(evt, params) {
 31222          this.emitter().emit(evt, params);
 31223          return this;
 31224        }
 31225      });
 31226      define$3.eventAliasesOn(layoutProto);
 31227      ext = Layout; // replace with our wrapped layout
 31228    } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
 31229      // user registered renderers inherit from base
 31230      var BaseRenderer = getExtension('renderer', 'base');
 31231      var bProto = BaseRenderer.prototype;
 31232      var RegistrantRenderer = registrant;
 31233      var rProto = registrant.prototype;
 31234  
 31235      var Renderer = function Renderer() {
 31236        BaseRenderer.apply(this, arguments);
 31237        RegistrantRenderer.apply(this, arguments);
 31238      };
 31239  
 31240      var proto = Renderer.prototype;
 31241  
 31242      for (var pName in bProto) {
 31243        var pVal = bProto[pName];
 31244        var existsInR = rProto[pName] != null;
 31245  
 31246        if (existsInR) {
 31247          return overrideErr(pName);
 31248        }
 31249  
 31250        proto[pName] = pVal; // take impl from base
 31251      }
 31252  
 31253      for (var _pName in rProto) {
 31254        proto[_pName] = rProto[_pName]; // take impl from registrant
 31255      }
 31256  
 31257      bProto.clientFunctions.forEach(function (name) {
 31258        proto[name] = proto[name] || function () {
 31259          error('Renderer does not implement `renderer.' + name + '()` on its prototype');
 31260        };
 31261      });
 31262      ext = Renderer;
 31263    }
 31264  
 31265    return setMap({
 31266      map: extensions,
 31267      keys: [type, name],
 31268      value: ext
 31269    });
 31270  }
 31271  
 31272  function getExtension(type, name) {
 31273    return getMap({
 31274      map: extensions,
 31275      keys: [type, name]
 31276    });
 31277  }
 31278  
 31279  function setModule(type, name, moduleType, moduleName, registrant) {
 31280    return setMap({
 31281      map: modules,
 31282      keys: [type, name, moduleType, moduleName],
 31283      value: registrant
 31284    });
 31285  }
 31286  
 31287  function getModule(type, name, moduleType, moduleName) {
 31288    return getMap({
 31289      map: modules,
 31290      keys: [type, name, moduleType, moduleName]
 31291    });
 31292  }
 31293  
 31294  var extension = function extension() {
 31295    // e.g. extension('renderer', 'svg')
 31296    if (arguments.length === 2) {
 31297      return getExtension.apply(null, arguments);
 31298    } // e.g. extension('renderer', 'svg', { ... })
 31299    else if (arguments.length === 3) {
 31300        return setExtension.apply(null, arguments);
 31301      } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
 31302      else if (arguments.length === 4) {
 31303          return getModule.apply(null, arguments);
 31304        } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
 31305        else if (arguments.length === 5) {
 31306            return setModule.apply(null, arguments);
 31307          } else {
 31308            error('Invalid extension access syntax');
 31309          }
 31310  }; // allows a core instance to access extensions internally
 31311  
 31312  
 31313  Core.prototype.extension = extension; // included extensions
 31314  
 31315  incExts.forEach(function (group) {
 31316    group.extensions.forEach(function (ext) {
 31317      setExtension(group.type, ext.name, ext.impl);
 31318    });
 31319  });
 31320  
 31321  // (useful for init)
 31322  
 31323  var Stylesheet = function Stylesheet() {
 31324    if (!(this instanceof Stylesheet)) {
 31325      return new Stylesheet();
 31326    }
 31327  
 31328    this.length = 0;
 31329  };
 31330  
 31331  var sheetfn = Stylesheet.prototype;
 31332  
 31333  sheetfn.instanceString = function () {
 31334    return 'stylesheet';
 31335  }; // just store the selector to be parsed later
 31336  
 31337  
 31338  sheetfn.selector = function (selector) {
 31339    var i = this.length++;
 31340    this[i] = {
 31341      selector: selector,
 31342      properties: []
 31343    };
 31344    return this; // chaining
 31345  }; // just store the property to be parsed later
 31346  
 31347  
 31348  sheetfn.css = function (name, value) {
 31349    var i = this.length - 1;
 31350  
 31351    if (string(name)) {
 31352      this[i].properties.push({
 31353        name: name,
 31354        value: value
 31355      });
 31356    } else if (plainObject(name)) {
 31357      var map = name;
 31358      var propNames = Object.keys(map);
 31359  
 31360      for (var j = 0; j < propNames.length; j++) {
 31361        var key = propNames[j];
 31362        var mapVal = map[key];
 31363  
 31364        if (mapVal == null) {
 31365          continue;
 31366        }
 31367  
 31368        var prop = Style.properties[key] || Style.properties[dash2camel(key)];
 31369  
 31370        if (prop == null) {
 31371          continue;
 31372        }
 31373  
 31374        var _name = prop.name;
 31375        var _value = mapVal;
 31376        this[i].properties.push({
 31377          name: _name,
 31378          value: _value
 31379        });
 31380      }
 31381    }
 31382  
 31383    return this; // chaining
 31384  };
 31385  
 31386  sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
 31387  
 31388  sheetfn.generateStyle = function (cy) {
 31389    var style = new Style(cy);
 31390    return this.appendToStyle(style);
 31391  }; // append a dummy stylesheet object on a real style object
 31392  
 31393  
 31394  sheetfn.appendToStyle = function (style) {
 31395    for (var i = 0; i < this.length; i++) {
 31396      var context = this[i];
 31397      var selector = context.selector;
 31398      var props = context.properties;
 31399      style.selector(selector); // apply selector
 31400  
 31401      for (var j = 0; j < props.length; j++) {
 31402        var prop = props[j];
 31403        style.css(prop.name, prop.value); // apply property
 31404      }
 31405    }
 31406  
 31407    return style;
 31408  };
 31409  
 31410  var version = "3.13.3";
 31411  
 31412  var cytoscape = function cytoscape(options) {
 31413    // if no options specified, use default
 31414    if (options === undefined) {
 31415      options = {};
 31416    } // create instance
 31417  
 31418  
 31419    if (plainObject(options)) {
 31420      return new Core(options);
 31421    } // allow for registration of extensions
 31422    else if (string(options)) {
 31423        return extension.apply(extension, arguments);
 31424      }
 31425  }; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
 31426  
 31427  
 31428  cytoscape.use = function (ext) {
 31429    var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
 31430  
 31431    args.unshift(cytoscape); // cytoscape is first arg to ext
 31432  
 31433    ext.apply(null, args);
 31434    return this;
 31435  };
 31436  
 31437  cytoscape.warnings = function (bool) {
 31438    return warnings(bool);
 31439  }; // replaced by build system
 31440  
 31441  
 31442  cytoscape.version = version; // expose public apis (mostly for extensions)
 31443  
 31444  cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
 31445  
 31446  export default cytoscape;