github.com/mweagle/Sparta@v1.15.0/resources/describe/cytoscape.js/dist/cytoscape.umd.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  (function (global, factory) {
    24    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    25    typeof define === 'function' && define.amd ? define(factory) :
    26    (global = global || self, global.cytoscape = factory());
    27  }(this, (function () { 'use strict';
    28  
    29    function _typeof(obj) {
    30      if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
    31        _typeof = function (obj) {
    32          return typeof obj;
    33        };
    34      } else {
    35        _typeof = function (obj) {
    36          return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
    37        };
    38      }
    39  
    40      return _typeof(obj);
    41    }
    42  
    43    function _classCallCheck(instance, Constructor) {
    44      if (!(instance instanceof Constructor)) {
    45        throw new TypeError("Cannot call a class as a function");
    46      }
    47    }
    48  
    49    function _defineProperties(target, props) {
    50      for (var i = 0; i < props.length; i++) {
    51        var descriptor = props[i];
    52        descriptor.enumerable = descriptor.enumerable || false;
    53        descriptor.configurable = true;
    54        if ("value" in descriptor) descriptor.writable = true;
    55        Object.defineProperty(target, descriptor.key, descriptor);
    56      }
    57    }
    58  
    59    function _createClass(Constructor, protoProps, staticProps) {
    60      if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    61      if (staticProps) _defineProperties(Constructor, staticProps);
    62      return Constructor;
    63    }
    64  
    65    function _defineProperty(obj, key, value) {
    66      if (key in obj) {
    67        Object.defineProperty(obj, key, {
    68          value: value,
    69          enumerable: true,
    70          configurable: true,
    71          writable: true
    72        });
    73      } else {
    74        obj[key] = value;
    75      }
    76  
    77      return obj;
    78    }
    79  
    80    function _slicedToArray(arr, i) {
    81      return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
    82    }
    83  
    84    function _arrayWithHoles(arr) {
    85      if (Array.isArray(arr)) return arr;
    86    }
    87  
    88    function _iterableToArrayLimit(arr, i) {
    89      var _arr = [];
    90      var _n = true;
    91      var _d = false;
    92      var _e = undefined;
    93  
    94      try {
    95        for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
    96          _arr.push(_s.value);
    97  
    98          if (i && _arr.length === i) break;
    99        }
   100      } catch (err) {
   101        _d = true;
   102        _e = err;
   103      } finally {
   104        try {
   105          if (!_n && _i["return"] != null) _i["return"]();
   106        } finally {
   107          if (_d) throw _e;
   108        }
   109      }
   110  
   111      return _arr;
   112    }
   113  
   114    function _nonIterableRest() {
   115      throw new TypeError("Invalid attempt to destructure non-iterable instance");
   116    }
   117  
   118    var window$1 = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
   119  
   120    var navigator = window$1 ? window$1.navigator : null;
   121    var document$1 = window$1 ? window$1.document : null;
   122  
   123    var typeofstr = _typeof('');
   124  
   125    var typeofobj = _typeof({});
   126  
   127    var typeoffn = _typeof(function () {});
   128  
   129    var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
   130  
   131    var instanceStr = function instanceStr(obj) {
   132      return obj && obj.instanceString && fn(obj.instanceString) ? obj.instanceString() : null;
   133    };
   134  
   135    var string = function string(obj) {
   136      return obj != null && _typeof(obj) == typeofstr;
   137    };
   138    var fn = function fn(obj) {
   139      return obj != null && _typeof(obj) === typeoffn;
   140    };
   141    var array = function array(obj) {
   142      return Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array;
   143    };
   144    var plainObject = function plainObject(obj) {
   145      return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
   146    };
   147    var object = function object(obj) {
   148      return obj != null && _typeof(obj) === typeofobj;
   149    };
   150    var number = function number(obj) {
   151      return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
   152    };
   153    var integer = function integer(obj) {
   154      return number(obj) && Math.floor(obj) === obj;
   155    };
   156    var htmlElement = function htmlElement(obj) {
   157      if ('undefined' === typeofhtmlele) {
   158        return undefined;
   159      } else {
   160        return null != obj && obj instanceof HTMLElement;
   161      }
   162    };
   163    var elementOrCollection = function elementOrCollection(obj) {
   164      return element(obj) || collection(obj);
   165    };
   166    var element = function element(obj) {
   167      return instanceStr(obj) === 'collection' && obj._private.single;
   168    };
   169    var collection = function collection(obj) {
   170      return instanceStr(obj) === 'collection' && !obj._private.single;
   171    };
   172    var core = function core(obj) {
   173      return instanceStr(obj) === 'core';
   174    };
   175    var stylesheet = function stylesheet(obj) {
   176      return instanceStr(obj) === 'stylesheet';
   177    };
   178    var event = function event(obj) {
   179      return instanceStr(obj) === 'event';
   180    };
   181    var emptyString = function emptyString(obj) {
   182      if (obj === undefined || obj === null) {
   183        // null is empty
   184        return true;
   185      } else if (obj === '' || obj.match(/^\s+$/)) {
   186        return true; // empty string is empty
   187      }
   188  
   189      return false; // otherwise, we don't know what we've got
   190    };
   191    var domElement = function domElement(obj) {
   192      if (typeof HTMLElement === 'undefined') {
   193        return false; // we're not in a browser so it doesn't matter
   194      } else {
   195        return obj instanceof HTMLElement;
   196      }
   197    };
   198    var boundingBox = function boundingBox(obj) {
   199      return plainObject(obj) && number(obj.x1) && number(obj.x2) && number(obj.y1) && number(obj.y2);
   200    };
   201    var promise = function promise(obj) {
   202      return object(obj) && fn(obj.then);
   203    };
   204    var ms = function ms() {
   205      return navigator && navigator.userAgent.match(/msie|trident|edge/i);
   206    }; // probably a better way to detect this...
   207  
   208    var memoize = function memoize(fn, keyFn) {
   209      if (!keyFn) {
   210        keyFn = function keyFn() {
   211          if (arguments.length === 1) {
   212            return arguments[0];
   213          } else if (arguments.length === 0) {
   214            return 'undefined';
   215          }
   216  
   217          var args = [];
   218  
   219          for (var i = 0; i < arguments.length; i++) {
   220            args.push(arguments[i]);
   221          }
   222  
   223          return args.join('$');
   224        };
   225      }
   226  
   227      var memoizedFn = function memoizedFn() {
   228        var self = this;
   229        var args = arguments;
   230        var ret;
   231        var k = keyFn.apply(self, args);
   232        var cache = memoizedFn.cache;
   233  
   234        if (!(ret = cache[k])) {
   235          ret = cache[k] = fn.apply(self, args);
   236        }
   237  
   238        return ret;
   239      };
   240  
   241      memoizedFn.cache = {};
   242      return memoizedFn;
   243    };
   244  
   245    var camel2dash = memoize(function (str) {
   246      return str.replace(/([A-Z])/g, function (v) {
   247        return '-' + v.toLowerCase();
   248      });
   249    });
   250    var dash2camel = memoize(function (str) {
   251      return str.replace(/(-\w)/g, function (v) {
   252        return v[1].toUpperCase();
   253      });
   254    });
   255    var prependCamel = memoize(function (prefix, str) {
   256      return prefix + str[0].toUpperCase() + str.substring(1);
   257    }, function (prefix, str) {
   258      return prefix + '$' + str;
   259    });
   260    var capitalize = function capitalize(str) {
   261      if (emptyString(str)) {
   262        return str;
   263      }
   264  
   265      return str.charAt(0).toUpperCase() + str.substring(1);
   266    };
   267  
   268    var number$1 = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
   269    var rgba = 'rgb[a]?\\((' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)(?:\\s*,\\s*(' + number$1 + '))?\\)';
   270    var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
   271    var hsla = 'hsl[a]?\\((' + number$1 + ')\\s*,\\s*(' + number$1 + '[%])\\s*,\\s*(' + number$1 + '[%])(?:\\s*,\\s*(' + number$1 + '))?\\)';
   272    var hslaNoBackRefs = 'hsl[a]?\\((?:' + number$1 + ')\\s*,\\s*(?:' + number$1 + '[%])\\s*,\\s*(?:' + number$1 + '[%])(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
   273    var hex3 = '\\#[0-9a-fA-F]{3}';
   274    var hex6 = '\\#[0-9a-fA-F]{6}';
   275  
   276    var ascending = function ascending(a, b) {
   277      if (a < b) {
   278        return -1;
   279      } else if (a > b) {
   280        return 1;
   281      } else {
   282        return 0;
   283      }
   284    };
   285    var descending = function descending(a, b) {
   286      return -1 * ascending(a, b);
   287    };
   288  
   289    var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
   290      var args = arguments;
   291  
   292      for (var i = 1; i < args.length; i++) {
   293        var obj = args[i];
   294  
   295        if (obj == null) {
   296          continue;
   297        }
   298  
   299        var keys = Object.keys(obj);
   300  
   301        for (var j = 0; j < keys.length; j++) {
   302          var k = keys[j];
   303          tgt[k] = obj[k];
   304        }
   305      }
   306  
   307      return tgt;
   308    };
   309  
   310    var hex2tuple = function hex2tuple(hex) {
   311      if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
   312        return;
   313      }
   314  
   315      var shortHex = hex.length === 4;
   316      var r, g, b;
   317      var base = 16;
   318  
   319      if (shortHex) {
   320        r = parseInt(hex[1] + hex[1], base);
   321        g = parseInt(hex[2] + hex[2], base);
   322        b = parseInt(hex[3] + hex[3], base);
   323      } else {
   324        r = parseInt(hex[1] + hex[2], base);
   325        g = parseInt(hex[3] + hex[4], base);
   326        b = parseInt(hex[5] + hex[6], base);
   327      }
   328  
   329      return [r, g, b];
   330    }; // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
   331  
   332    var hsl2tuple = function hsl2tuple(hsl) {
   333      var ret;
   334      var h, s, l, a, r, g, b;
   335  
   336      function hue2rgb(p, q, t) {
   337        if (t < 0) t += 1;
   338        if (t > 1) t -= 1;
   339        if (t < 1 / 6) return p + (q - p) * 6 * t;
   340        if (t < 1 / 2) return q;
   341        if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
   342        return p;
   343      }
   344  
   345      var m = new RegExp('^' + hsla + '$').exec(hsl);
   346  
   347      if (m) {
   348        // get hue
   349        h = parseInt(m[1]);
   350  
   351        if (h < 0) {
   352          h = (360 - -1 * h % 360) % 360;
   353        } else if (h > 360) {
   354          h = h % 360;
   355        }
   356  
   357        h /= 360; // normalise on [0, 1]
   358  
   359        s = parseFloat(m[2]);
   360  
   361        if (s < 0 || s > 100) {
   362          return;
   363        } // saturation is [0, 100]
   364  
   365  
   366        s = s / 100; // normalise on [0, 1]
   367  
   368        l = parseFloat(m[3]);
   369  
   370        if (l < 0 || l > 100) {
   371          return;
   372        } // lightness is [0, 100]
   373  
   374  
   375        l = l / 100; // normalise on [0, 1]
   376  
   377        a = m[4];
   378  
   379        if (a !== undefined) {
   380          a = parseFloat(a);
   381  
   382          if (a < 0 || a > 1) {
   383            return;
   384          } // alpha is [0, 1]
   385  
   386        } // now, convert to rgb
   387        // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
   388  
   389  
   390        if (s === 0) {
   391          r = g = b = Math.round(l * 255); // achromatic
   392        } else {
   393          var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
   394          var p = 2 * l - q;
   395          r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
   396          g = Math.round(255 * hue2rgb(p, q, h));
   397          b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
   398        }
   399  
   400        ret = [r, g, b, a];
   401      }
   402  
   403      return ret;
   404    }; // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
   405  
   406    var rgb2tuple = function rgb2tuple(rgb) {
   407      var ret;
   408      var m = new RegExp('^' + rgba + '$').exec(rgb);
   409  
   410      if (m) {
   411        ret = [];
   412        var isPct = [];
   413  
   414        for (var i = 1; i <= 3; i++) {
   415          var channel = m[i];
   416  
   417          if (channel[channel.length - 1] === '%') {
   418            isPct[i] = true;
   419          }
   420  
   421          channel = parseFloat(channel);
   422  
   423          if (isPct[i]) {
   424            channel = channel / 100 * 255; // normalise to [0, 255]
   425          }
   426  
   427          if (channel < 0 || channel > 255) {
   428            return;
   429          } // invalid channel value
   430  
   431  
   432          ret.push(Math.floor(channel));
   433        }
   434  
   435        var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
   436        var allArePct = isPct[1] && isPct[2] && isPct[3];
   437  
   438        if (atLeastOneIsPct && !allArePct) {
   439          return;
   440        } // must all be percent values if one is
   441  
   442  
   443        var alpha = m[4];
   444  
   445        if (alpha !== undefined) {
   446          alpha = parseFloat(alpha);
   447  
   448          if (alpha < 0 || alpha > 1) {
   449            return;
   450          } // invalid alpha value
   451  
   452  
   453          ret.push(alpha);
   454        }
   455      }
   456  
   457      return ret;
   458    };
   459    var colorname2tuple = function colorname2tuple(color) {
   460      return colors[color.toLowerCase()];
   461    };
   462    var color2tuple = function color2tuple(color) {
   463      return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
   464    };
   465    var colors = {
   466      // special colour names
   467      transparent: [0, 0, 0, 0],
   468      // NB alpha === 0
   469      // regular colours
   470      aliceblue: [240, 248, 255],
   471      antiquewhite: [250, 235, 215],
   472      aqua: [0, 255, 255],
   473      aquamarine: [127, 255, 212],
   474      azure: [240, 255, 255],
   475      beige: [245, 245, 220],
   476      bisque: [255, 228, 196],
   477      black: [0, 0, 0],
   478      blanchedalmond: [255, 235, 205],
   479      blue: [0, 0, 255],
   480      blueviolet: [138, 43, 226],
   481      brown: [165, 42, 42],
   482      burlywood: [222, 184, 135],
   483      cadetblue: [95, 158, 160],
   484      chartreuse: [127, 255, 0],
   485      chocolate: [210, 105, 30],
   486      coral: [255, 127, 80],
   487      cornflowerblue: [100, 149, 237],
   488      cornsilk: [255, 248, 220],
   489      crimson: [220, 20, 60],
   490      cyan: [0, 255, 255],
   491      darkblue: [0, 0, 139],
   492      darkcyan: [0, 139, 139],
   493      darkgoldenrod: [184, 134, 11],
   494      darkgray: [169, 169, 169],
   495      darkgreen: [0, 100, 0],
   496      darkgrey: [169, 169, 169],
   497      darkkhaki: [189, 183, 107],
   498      darkmagenta: [139, 0, 139],
   499      darkolivegreen: [85, 107, 47],
   500      darkorange: [255, 140, 0],
   501      darkorchid: [153, 50, 204],
   502      darkred: [139, 0, 0],
   503      darksalmon: [233, 150, 122],
   504      darkseagreen: [143, 188, 143],
   505      darkslateblue: [72, 61, 139],
   506      darkslategray: [47, 79, 79],
   507      darkslategrey: [47, 79, 79],
   508      darkturquoise: [0, 206, 209],
   509      darkviolet: [148, 0, 211],
   510      deeppink: [255, 20, 147],
   511      deepskyblue: [0, 191, 255],
   512      dimgray: [105, 105, 105],
   513      dimgrey: [105, 105, 105],
   514      dodgerblue: [30, 144, 255],
   515      firebrick: [178, 34, 34],
   516      floralwhite: [255, 250, 240],
   517      forestgreen: [34, 139, 34],
   518      fuchsia: [255, 0, 255],
   519      gainsboro: [220, 220, 220],
   520      ghostwhite: [248, 248, 255],
   521      gold: [255, 215, 0],
   522      goldenrod: [218, 165, 32],
   523      gray: [128, 128, 128],
   524      grey: [128, 128, 128],
   525      green: [0, 128, 0],
   526      greenyellow: [173, 255, 47],
   527      honeydew: [240, 255, 240],
   528      hotpink: [255, 105, 180],
   529      indianred: [205, 92, 92],
   530      indigo: [75, 0, 130],
   531      ivory: [255, 255, 240],
   532      khaki: [240, 230, 140],
   533      lavender: [230, 230, 250],
   534      lavenderblush: [255, 240, 245],
   535      lawngreen: [124, 252, 0],
   536      lemonchiffon: [255, 250, 205],
   537      lightblue: [173, 216, 230],
   538      lightcoral: [240, 128, 128],
   539      lightcyan: [224, 255, 255],
   540      lightgoldenrodyellow: [250, 250, 210],
   541      lightgray: [211, 211, 211],
   542      lightgreen: [144, 238, 144],
   543      lightgrey: [211, 211, 211],
   544      lightpink: [255, 182, 193],
   545      lightsalmon: [255, 160, 122],
   546      lightseagreen: [32, 178, 170],
   547      lightskyblue: [135, 206, 250],
   548      lightslategray: [119, 136, 153],
   549      lightslategrey: [119, 136, 153],
   550      lightsteelblue: [176, 196, 222],
   551      lightyellow: [255, 255, 224],
   552      lime: [0, 255, 0],
   553      limegreen: [50, 205, 50],
   554      linen: [250, 240, 230],
   555      magenta: [255, 0, 255],
   556      maroon: [128, 0, 0],
   557      mediumaquamarine: [102, 205, 170],
   558      mediumblue: [0, 0, 205],
   559      mediumorchid: [186, 85, 211],
   560      mediumpurple: [147, 112, 219],
   561      mediumseagreen: [60, 179, 113],
   562      mediumslateblue: [123, 104, 238],
   563      mediumspringgreen: [0, 250, 154],
   564      mediumturquoise: [72, 209, 204],
   565      mediumvioletred: [199, 21, 133],
   566      midnightblue: [25, 25, 112],
   567      mintcream: [245, 255, 250],
   568      mistyrose: [255, 228, 225],
   569      moccasin: [255, 228, 181],
   570      navajowhite: [255, 222, 173],
   571      navy: [0, 0, 128],
   572      oldlace: [253, 245, 230],
   573      olive: [128, 128, 0],
   574      olivedrab: [107, 142, 35],
   575      orange: [255, 165, 0],
   576      orangered: [255, 69, 0],
   577      orchid: [218, 112, 214],
   578      palegoldenrod: [238, 232, 170],
   579      palegreen: [152, 251, 152],
   580      paleturquoise: [175, 238, 238],
   581      palevioletred: [219, 112, 147],
   582      papayawhip: [255, 239, 213],
   583      peachpuff: [255, 218, 185],
   584      peru: [205, 133, 63],
   585      pink: [255, 192, 203],
   586      plum: [221, 160, 221],
   587      powderblue: [176, 224, 230],
   588      purple: [128, 0, 128],
   589      red: [255, 0, 0],
   590      rosybrown: [188, 143, 143],
   591      royalblue: [65, 105, 225],
   592      saddlebrown: [139, 69, 19],
   593      salmon: [250, 128, 114],
   594      sandybrown: [244, 164, 96],
   595      seagreen: [46, 139, 87],
   596      seashell: [255, 245, 238],
   597      sienna: [160, 82, 45],
   598      silver: [192, 192, 192],
   599      skyblue: [135, 206, 235],
   600      slateblue: [106, 90, 205],
   601      slategray: [112, 128, 144],
   602      slategrey: [112, 128, 144],
   603      snow: [255, 250, 250],
   604      springgreen: [0, 255, 127],
   605      steelblue: [70, 130, 180],
   606      tan: [210, 180, 140],
   607      teal: [0, 128, 128],
   608      thistle: [216, 191, 216],
   609      tomato: [255, 99, 71],
   610      turquoise: [64, 224, 208],
   611      violet: [238, 130, 238],
   612      wheat: [245, 222, 179],
   613      white: [255, 255, 255],
   614      whitesmoke: [245, 245, 245],
   615      yellow: [255, 255, 0],
   616      yellowgreen: [154, 205, 50]
   617    };
   618  
   619    var setMap = function setMap(options) {
   620      var obj = options.map;
   621      var keys = options.keys;
   622      var l = keys.length;
   623  
   624      for (var i = 0; i < l; i++) {
   625        var key = keys[i];
   626  
   627        if (plainObject(key)) {
   628          throw Error('Tried to set map with object key');
   629        }
   630  
   631        if (i < keys.length - 1) {
   632          // extend the map if necessary
   633          if (obj[key] == null) {
   634            obj[key] = {};
   635          }
   636  
   637          obj = obj[key];
   638        } else {
   639          // set the value
   640          obj[key] = options.value;
   641        }
   642      }
   643    }; // gets the value in a map even if it's not built in places
   644  
   645    var getMap = function getMap(options) {
   646      var obj = options.map;
   647      var keys = options.keys;
   648      var l = keys.length;
   649  
   650      for (var i = 0; i < l; i++) {
   651        var key = keys[i];
   652  
   653        if (plainObject(key)) {
   654          throw Error('Tried to get map with object key');
   655        }
   656  
   657        obj = obj[key];
   658  
   659        if (obj == null) {
   660          return obj;
   661        }
   662      }
   663  
   664      return obj;
   665    }; // deletes the entry in the map
   666  
   667    var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
   668  
   669    function createCommonjsModule(fn, module) {
   670    	return module = { exports: {} }, fn(module, module.exports), module.exports;
   671    }
   672  
   673    /**
   674     * lodash (Custom Build) <https://lodash.com/>
   675     * Build: `lodash modularize exports="npm" -o ./`
   676     * Copyright jQuery Foundation and other contributors <https://jquery.org/>
   677     * Released under MIT license <https://lodash.com/license>
   678     * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
   679     * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
   680     */
   681  
   682    /** Used as the `TypeError` message for "Functions" methods. */
   683    var FUNC_ERROR_TEXT = 'Expected a function';
   684  
   685    /** Used as references for various `Number` constants. */
   686    var NAN = 0 / 0;
   687  
   688    /** `Object#toString` result references. */
   689    var symbolTag = '[object Symbol]';
   690  
   691    /** Used to match leading and trailing whitespace. */
   692    var reTrim = /^\s+|\s+$/g;
   693  
   694    /** Used to detect bad signed hexadecimal string values. */
   695    var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
   696  
   697    /** Used to detect binary string values. */
   698    var reIsBinary = /^0b[01]+$/i;
   699  
   700    /** Used to detect octal string values. */
   701    var reIsOctal = /^0o[0-7]+$/i;
   702  
   703    /** Built-in method references without a dependency on `root`. */
   704    var freeParseInt = parseInt;
   705  
   706    /** Detect free variable `global` from Node.js. */
   707    var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
   708  
   709    /** Detect free variable `self`. */
   710    var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
   711  
   712    /** Used as a reference to the global object. */
   713    var root = freeGlobal || freeSelf || Function('return this')();
   714  
   715    /** Used for built-in method references. */
   716    var objectProto = Object.prototype;
   717  
   718    /**
   719     * Used to resolve the
   720     * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
   721     * of values.
   722     */
   723    var objectToString = objectProto.toString;
   724  
   725    /* Built-in method references for those with the same name as other `lodash` methods. */
   726    var nativeMax = Math.max,
   727        nativeMin = Math.min;
   728  
   729    /**
   730     * Gets the timestamp of the number of milliseconds that have elapsed since
   731     * the Unix epoch (1 January 1970 00:00:00 UTC).
   732     *
   733     * @static
   734     * @memberOf _
   735     * @since 2.4.0
   736     * @category Date
   737     * @returns {number} Returns the timestamp.
   738     * @example
   739     *
   740     * _.defer(function(stamp) {
   741     *   console.log(_.now() - stamp);
   742     * }, _.now());
   743     * // => Logs the number of milliseconds it took for the deferred invocation.
   744     */
   745    var now = function() {
   746      return root.Date.now();
   747    };
   748  
   749    /**
   750     * Creates a debounced function that delays invoking `func` until after `wait`
   751     * milliseconds have elapsed since the last time the debounced function was
   752     * invoked. The debounced function comes with a `cancel` method to cancel
   753     * delayed `func` invocations and a `flush` method to immediately invoke them.
   754     * Provide `options` to indicate whether `func` should be invoked on the
   755     * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
   756     * with the last arguments provided to the debounced function. Subsequent
   757     * calls to the debounced function return the result of the last `func`
   758     * invocation.
   759     *
   760     * **Note:** If `leading` and `trailing` options are `true`, `func` is
   761     * invoked on the trailing edge of the timeout only if the debounced function
   762     * is invoked more than once during the `wait` timeout.
   763     *
   764     * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
   765     * until to the next tick, similar to `setTimeout` with a timeout of `0`.
   766     *
   767     * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
   768     * for details over the differences between `_.debounce` and `_.throttle`.
   769     *
   770     * @static
   771     * @memberOf _
   772     * @since 0.1.0
   773     * @category Function
   774     * @param {Function} func The function to debounce.
   775     * @param {number} [wait=0] The number of milliseconds to delay.
   776     * @param {Object} [options={}] The options object.
   777     * @param {boolean} [options.leading=false]
   778     *  Specify invoking on the leading edge of the timeout.
   779     * @param {number} [options.maxWait]
   780     *  The maximum time `func` is allowed to be delayed before it's invoked.
   781     * @param {boolean} [options.trailing=true]
   782     *  Specify invoking on the trailing edge of the timeout.
   783     * @returns {Function} Returns the new debounced function.
   784     * @example
   785     *
   786     * // Avoid costly calculations while the window size is in flux.
   787     * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
   788     *
   789     * // Invoke `sendMail` when clicked, debouncing subsequent calls.
   790     * jQuery(element).on('click', _.debounce(sendMail, 300, {
   791     *   'leading': true,
   792     *   'trailing': false
   793     * }));
   794     *
   795     * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
   796     * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
   797     * var source = new EventSource('/stream');
   798     * jQuery(source).on('message', debounced);
   799     *
   800     * // Cancel the trailing debounced invocation.
   801     * jQuery(window).on('popstate', debounced.cancel);
   802     */
   803    function debounce(func, wait, options) {
   804      var lastArgs,
   805          lastThis,
   806          maxWait,
   807          result,
   808          timerId,
   809          lastCallTime,
   810          lastInvokeTime = 0,
   811          leading = false,
   812          maxing = false,
   813          trailing = true;
   814  
   815      if (typeof func != 'function') {
   816        throw new TypeError(FUNC_ERROR_TEXT);
   817      }
   818      wait = toNumber(wait) || 0;
   819      if (isObject(options)) {
   820        leading = !!options.leading;
   821        maxing = 'maxWait' in options;
   822        maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
   823        trailing = 'trailing' in options ? !!options.trailing : trailing;
   824      }
   825  
   826      function invokeFunc(time) {
   827        var args = lastArgs,
   828            thisArg = lastThis;
   829  
   830        lastArgs = lastThis = undefined;
   831        lastInvokeTime = time;
   832        result = func.apply(thisArg, args);
   833        return result;
   834      }
   835  
   836      function leadingEdge(time) {
   837        // Reset any `maxWait` timer.
   838        lastInvokeTime = time;
   839        // Start the timer for the trailing edge.
   840        timerId = setTimeout(timerExpired, wait);
   841        // Invoke the leading edge.
   842        return leading ? invokeFunc(time) : result;
   843      }
   844  
   845      function remainingWait(time) {
   846        var timeSinceLastCall = time - lastCallTime,
   847            timeSinceLastInvoke = time - lastInvokeTime,
   848            result = wait - timeSinceLastCall;
   849  
   850        return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
   851      }
   852  
   853      function shouldInvoke(time) {
   854        var timeSinceLastCall = time - lastCallTime,
   855            timeSinceLastInvoke = time - lastInvokeTime;
   856  
   857        // Either this is the first call, activity has stopped and we're at the
   858        // trailing edge, the system time has gone backwards and we're treating
   859        // it as the trailing edge, or we've hit the `maxWait` limit.
   860        return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
   861          (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
   862      }
   863  
   864      function timerExpired() {
   865        var time = now();
   866        if (shouldInvoke(time)) {
   867          return trailingEdge(time);
   868        }
   869        // Restart the timer.
   870        timerId = setTimeout(timerExpired, remainingWait(time));
   871      }
   872  
   873      function trailingEdge(time) {
   874        timerId = undefined;
   875  
   876        // Only invoke if we have `lastArgs` which means `func` has been
   877        // debounced at least once.
   878        if (trailing && lastArgs) {
   879          return invokeFunc(time);
   880        }
   881        lastArgs = lastThis = undefined;
   882        return result;
   883      }
   884  
   885      function cancel() {
   886        if (timerId !== undefined) {
   887          clearTimeout(timerId);
   888        }
   889        lastInvokeTime = 0;
   890        lastArgs = lastCallTime = lastThis = timerId = undefined;
   891      }
   892  
   893      function flush() {
   894        return timerId === undefined ? result : trailingEdge(now());
   895      }
   896  
   897      function debounced() {
   898        var time = now(),
   899            isInvoking = shouldInvoke(time);
   900  
   901        lastArgs = arguments;
   902        lastThis = this;
   903        lastCallTime = time;
   904  
   905        if (isInvoking) {
   906          if (timerId === undefined) {
   907            return leadingEdge(lastCallTime);
   908          }
   909          if (maxing) {
   910            // Handle invocations in a tight loop.
   911            timerId = setTimeout(timerExpired, wait);
   912            return invokeFunc(lastCallTime);
   913          }
   914        }
   915        if (timerId === undefined) {
   916          timerId = setTimeout(timerExpired, wait);
   917        }
   918        return result;
   919      }
   920      debounced.cancel = cancel;
   921      debounced.flush = flush;
   922      return debounced;
   923    }
   924  
   925    /**
   926     * Checks if `value` is the
   927     * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
   928     * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
   929     *
   930     * @static
   931     * @memberOf _
   932     * @since 0.1.0
   933     * @category Lang
   934     * @param {*} value The value to check.
   935     * @returns {boolean} Returns `true` if `value` is an object, else `false`.
   936     * @example
   937     *
   938     * _.isObject({});
   939     * // => true
   940     *
   941     * _.isObject([1, 2, 3]);
   942     * // => true
   943     *
   944     * _.isObject(_.noop);
   945     * // => true
   946     *
   947     * _.isObject(null);
   948     * // => false
   949     */
   950    function isObject(value) {
   951      var type = typeof value;
   952      return !!value && (type == 'object' || type == 'function');
   953    }
   954  
   955    /**
   956     * Checks if `value` is object-like. A value is object-like if it's not `null`
   957     * and has a `typeof` result of "object".
   958     *
   959     * @static
   960     * @memberOf _
   961     * @since 4.0.0
   962     * @category Lang
   963     * @param {*} value The value to check.
   964     * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
   965     * @example
   966     *
   967     * _.isObjectLike({});
   968     * // => true
   969     *
   970     * _.isObjectLike([1, 2, 3]);
   971     * // => true
   972     *
   973     * _.isObjectLike(_.noop);
   974     * // => false
   975     *
   976     * _.isObjectLike(null);
   977     * // => false
   978     */
   979    function isObjectLike(value) {
   980      return !!value && typeof value == 'object';
   981    }
   982  
   983    /**
   984     * Checks if `value` is classified as a `Symbol` primitive or object.
   985     *
   986     * @static
   987     * @memberOf _
   988     * @since 4.0.0
   989     * @category Lang
   990     * @param {*} value The value to check.
   991     * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
   992     * @example
   993     *
   994     * _.isSymbol(Symbol.iterator);
   995     * // => true
   996     *
   997     * _.isSymbol('abc');
   998     * // => false
   999     */
  1000    function isSymbol(value) {
  1001      return typeof value == 'symbol' ||
  1002        (isObjectLike(value) && objectToString.call(value) == symbolTag);
  1003    }
  1004  
  1005    /**
  1006     * Converts `value` to a number.
  1007     *
  1008     * @static
  1009     * @memberOf _
  1010     * @since 4.0.0
  1011     * @category Lang
  1012     * @param {*} value The value to process.
  1013     * @returns {number} Returns the number.
  1014     * @example
  1015     *
  1016     * _.toNumber(3.2);
  1017     * // => 3.2
  1018     *
  1019     * _.toNumber(Number.MIN_VALUE);
  1020     * // => 5e-324
  1021     *
  1022     * _.toNumber(Infinity);
  1023     * // => Infinity
  1024     *
  1025     * _.toNumber('3.2');
  1026     * // => 3.2
  1027     */
  1028    function toNumber(value) {
  1029      if (typeof value == 'number') {
  1030        return value;
  1031      }
  1032      if (isSymbol(value)) {
  1033        return NAN;
  1034      }
  1035      if (isObject(value)) {
  1036        var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
  1037        value = isObject(other) ? (other + '') : other;
  1038      }
  1039      if (typeof value != 'string') {
  1040        return value === 0 ? value : +value;
  1041      }
  1042      value = value.replace(reTrim, '');
  1043      var isBinary = reIsBinary.test(value);
  1044      return (isBinary || reIsOctal.test(value))
  1045        ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
  1046        : (reIsBadHex.test(value) ? NAN : +value);
  1047    }
  1048  
  1049    var lodash_debounce = debounce;
  1050  
  1051    var performance = window$1 ? window$1.performance : null;
  1052    var pnow = performance && performance.now ? function () {
  1053      return performance.now();
  1054    } : function () {
  1055      return Date.now();
  1056    };
  1057  
  1058    var raf = function () {
  1059      if (window$1) {
  1060        if (window$1.requestAnimationFrame) {
  1061          return function (fn) {
  1062            window$1.requestAnimationFrame(fn);
  1063          };
  1064        } else if (window$1.mozRequestAnimationFrame) {
  1065          return function (fn) {
  1066            window$1.mozRequestAnimationFrame(fn);
  1067          };
  1068        } else if (window$1.webkitRequestAnimationFrame) {
  1069          return function (fn) {
  1070            window$1.webkitRequestAnimationFrame(fn);
  1071          };
  1072        } else if (window$1.msRequestAnimationFrame) {
  1073          return function (fn) {
  1074            window$1.msRequestAnimationFrame(fn);
  1075          };
  1076        }
  1077      }
  1078  
  1079      return function (fn) {
  1080        if (fn) {
  1081          setTimeout(function () {
  1082            fn(pnow());
  1083          }, 1000 / 60);
  1084        }
  1085      };
  1086    }();
  1087  
  1088    var requestAnimationFrame = function requestAnimationFrame(fn) {
  1089      return raf(fn);
  1090    };
  1091    var performanceNow = pnow;
  1092  
  1093    var DEFAULT_SEED = 5381;
  1094    var hashIterableInts = function hashIterableInts(iterator) {
  1095      var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_SEED;
  1096      // djb2/string-hash
  1097      var hash = seed;
  1098      var entry;
  1099  
  1100      for (;;) {
  1101        entry = iterator.next();
  1102  
  1103        if (entry.done) {
  1104          break;
  1105        }
  1106  
  1107        hash = (hash << 5) + hash + entry.value | 0;
  1108      }
  1109  
  1110      return hash;
  1111    };
  1112    var hashInt = function hashInt(num) {
  1113      var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_SEED;
  1114      // djb2/string-hash
  1115      return (seed << 5) + seed + num | 0;
  1116    };
  1117    var hashIntsArray = function hashIntsArray(ints, seed) {
  1118      var entry = {
  1119        value: 0,
  1120        done: false
  1121      };
  1122      var i = 0;
  1123      var length = ints.length;
  1124      var iterator = {
  1125        next: function next() {
  1126          if (i < length) {
  1127            entry.value = ints[i++];
  1128          } else {
  1129            entry.done = true;
  1130          }
  1131  
  1132          return entry;
  1133        }
  1134      };
  1135      return hashIterableInts(iterator, seed);
  1136    };
  1137    var hashString = function hashString(str, seed) {
  1138      var entry = {
  1139        value: 0,
  1140        done: false
  1141      };
  1142      var i = 0;
  1143      var length = str.length;
  1144      var iterator = {
  1145        next: function next() {
  1146          if (i < length) {
  1147            entry.value = str.charCodeAt(i++);
  1148          } else {
  1149            entry.done = true;
  1150          }
  1151  
  1152          return entry;
  1153        }
  1154      };
  1155      return hashIterableInts(iterator, seed);
  1156    };
  1157    var hashStrings = function hashStrings() {
  1158      return hashStringsArray(arguments);
  1159    };
  1160    var hashStringsArray = function hashStringsArray(strs) {
  1161      var hash;
  1162  
  1163      for (var i = 0; i < strs.length; i++) {
  1164        var str = strs[i];
  1165  
  1166        if (i === 0) {
  1167          hash = hashString(str);
  1168        } else {
  1169          hash = hashString(str, hash);
  1170        }
  1171      }
  1172  
  1173      return hash;
  1174    };
  1175  
  1176    /*global console */
  1177    var warningsEnabled = true;
  1178    var warnSupported = console.warn != null; // eslint-disable-line no-console
  1179  
  1180    var traceSupported = console.trace != null; // eslint-disable-line no-console
  1181  
  1182    var MAX_INT = Number.MAX_SAFE_INTEGER || 9007199254740991;
  1183    var trueify = function trueify() {
  1184      return true;
  1185    };
  1186    var falsify = function falsify() {
  1187      return false;
  1188    };
  1189    var zeroify = function zeroify() {
  1190      return 0;
  1191    };
  1192    var noop = function noop() {};
  1193    var error = function error(msg) {
  1194      throw new Error(msg);
  1195    };
  1196    var warnings = function warnings(enabled) {
  1197      if (enabled !== undefined) {
  1198        warningsEnabled = !!enabled;
  1199      } else {
  1200        return warningsEnabled;
  1201      }
  1202    };
  1203    var warn = function warn(msg) {
  1204      /* eslint-disable no-console */
  1205      if (!warnings()) {
  1206        return;
  1207      }
  1208  
  1209      if (warnSupported) {
  1210        console.warn(msg);
  1211      } else {
  1212        console.log(msg);
  1213  
  1214        if (traceSupported) {
  1215          console.trace();
  1216        }
  1217      }
  1218    };
  1219    /* eslint-enable */
  1220  
  1221    var clone = function clone(obj) {
  1222      return extend({}, obj);
  1223    }; // gets a shallow copy of the argument
  1224  
  1225    var copy = function copy(obj) {
  1226      if (obj == null) {
  1227        return obj;
  1228      }
  1229  
  1230      if (array(obj)) {
  1231        return obj.slice();
  1232      } else if (plainObject(obj)) {
  1233        return clone(obj);
  1234      } else {
  1235        return obj;
  1236      }
  1237    };
  1238    var copyArray = function copyArray(arr) {
  1239      return arr.slice();
  1240    };
  1241    var uuid = function uuid(a, b
  1242    /* placeholders */
  1243    ) {
  1244      for ( // loop :)
  1245      b = a = ''; // b - result , a - numeric letiable
  1246      a++ < 36; //
  1247      b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
  1248      ? //  return a random number or 4
  1249      (a ^ 15 // if "a" is not 15
  1250      ? // genetate a random number from 0 to 15
  1251      8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
  1252      : 4 //  otherwise 4
  1253      ).toString(16) : '-' //  in other cases (if "a" is 9,14,19,24) insert "-"
  1254      ) {
  1255      }
  1256  
  1257      return b;
  1258    };
  1259    var _staticEmptyObject = {};
  1260    var staticEmptyObject = function staticEmptyObject() {
  1261      return _staticEmptyObject;
  1262    };
  1263    var defaults = function defaults(_defaults) {
  1264      var keys = Object.keys(_defaults);
  1265      return function (opts) {
  1266        var filledOpts = {};
  1267  
  1268        for (var i = 0; i < keys.length; i++) {
  1269          var key = keys[i];
  1270          var optVal = opts == null ? undefined : opts[key];
  1271          filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
  1272        }
  1273  
  1274        return filledOpts;
  1275      };
  1276    };
  1277    var removeFromArray = function removeFromArray(arr, ele, manyCopies) {
  1278      for (var i = arr.length; i >= 0; i--) {
  1279        if (arr[i] === ele) {
  1280          arr.splice(i, 1);
  1281  
  1282          if (!manyCopies) {
  1283            break;
  1284          }
  1285        }
  1286      }
  1287    };
  1288    var clearArray = function clearArray(arr) {
  1289      arr.splice(0, arr.length);
  1290    };
  1291    var push = function push(arr, otherArr) {
  1292      for (var i = 0; i < otherArr.length; i++) {
  1293        var el = otherArr[i];
  1294        arr.push(el);
  1295      }
  1296    };
  1297    var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
  1298      if (prefix) {
  1299        propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
  1300      }
  1301  
  1302      return obj[propName];
  1303    };
  1304    var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
  1305      if (prefix) {
  1306        propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
  1307      }
  1308  
  1309      obj[propName] = value;
  1310    };
  1311  
  1312    /* global Map */
  1313    var ObjectMap =
  1314    /*#__PURE__*/
  1315    function () {
  1316      function ObjectMap() {
  1317        _classCallCheck(this, ObjectMap);
  1318  
  1319        this._obj = {};
  1320      }
  1321  
  1322      _createClass(ObjectMap, [{
  1323        key: "set",
  1324        value: function set(key, val) {
  1325          this._obj[key] = val;
  1326          return this;
  1327        }
  1328      }, {
  1329        key: "delete",
  1330        value: function _delete(key) {
  1331          this._obj[key] = undefined;
  1332          return this;
  1333        }
  1334      }, {
  1335        key: "clear",
  1336        value: function clear() {
  1337          this._obj = {};
  1338        }
  1339      }, {
  1340        key: "has",
  1341        value: function has(key) {
  1342          return this._obj[key] !== undefined;
  1343        }
  1344      }, {
  1345        key: "get",
  1346        value: function get(key) {
  1347          return this._obj[key];
  1348        }
  1349      }]);
  1350  
  1351      return ObjectMap;
  1352    }();
  1353  
  1354    var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
  1355  
  1356    /* global Set */
  1357    var undef =  "undefined" ;
  1358  
  1359    var ObjectSet =
  1360    /*#__PURE__*/
  1361    function () {
  1362      function ObjectSet(arrayOrObjectSet) {
  1363        _classCallCheck(this, ObjectSet);
  1364  
  1365        this._obj = Object.create(null);
  1366        this.size = 0;
  1367  
  1368        if (arrayOrObjectSet != null) {
  1369          var arr;
  1370  
  1371          if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
  1372            arr = arrayOrObjectSet.toArray();
  1373          } else {
  1374            arr = arrayOrObjectSet;
  1375          }
  1376  
  1377          for (var i = 0; i < arr.length; i++) {
  1378            this.add(arr[i]);
  1379          }
  1380        }
  1381      }
  1382  
  1383      _createClass(ObjectSet, [{
  1384        key: "instanceString",
  1385        value: function instanceString() {
  1386          return 'set';
  1387        }
  1388      }, {
  1389        key: "add",
  1390        value: function add(val) {
  1391          var o = this._obj;
  1392  
  1393          if (o[val] !== 1) {
  1394            o[val] = 1;
  1395            this.size++;
  1396          }
  1397        }
  1398      }, {
  1399        key: "delete",
  1400        value: function _delete(val) {
  1401          var o = this._obj;
  1402  
  1403          if (o[val] === 1) {
  1404            o[val] = 0;
  1405            this.size--;
  1406          }
  1407        }
  1408      }, {
  1409        key: "clear",
  1410        value: function clear() {
  1411          this._obj = Object.create(null);
  1412        }
  1413      }, {
  1414        key: "has",
  1415        value: function has(val) {
  1416          return this._obj[val] === 1;
  1417        }
  1418      }, {
  1419        key: "toArray",
  1420        value: function toArray() {
  1421          var _this = this;
  1422  
  1423          return Object.keys(this._obj).filter(function (key) {
  1424            return _this.has(key);
  1425          });
  1426        }
  1427      }, {
  1428        key: "forEach",
  1429        value: function forEach(callback, thisArg) {
  1430          return this.toArray().forEach(callback, thisArg);
  1431        }
  1432      }]);
  1433  
  1434      return ObjectSet;
  1435    }();
  1436  
  1437    var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
  1438  
  1439    var Element = function Element(cy, params, restore) {
  1440      restore = restore === undefined || restore ? true : false;
  1441  
  1442      if (cy === undefined || params === undefined || !core(cy)) {
  1443        error('An element must have a core reference and parameters set');
  1444        return;
  1445      }
  1446  
  1447      var group = params.group; // try to automatically infer the group if unspecified
  1448  
  1449      if (group == null) {
  1450        if (params.data && params.data.source != null && params.data.target != null) {
  1451          group = 'edges';
  1452        } else {
  1453          group = 'nodes';
  1454        }
  1455      } // validate group
  1456  
  1457  
  1458      if (group !== 'nodes' && group !== 'edges') {
  1459        error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
  1460        return;
  1461      } // make the element array-like, just like a collection
  1462  
  1463  
  1464      this.length = 1;
  1465      this[0] = this; // NOTE: when something is added here, add also to ele.json()
  1466  
  1467      var _p = this._private = {
  1468        cy: cy,
  1469        single: true,
  1470        // indicates this is an element
  1471        data: params.data || {},
  1472        // data object
  1473        position: params.position || {
  1474          x: 0,
  1475          y: 0
  1476        },
  1477        // (x, y) position pair
  1478        autoWidth: undefined,
  1479        // width and height of nodes calculated by the renderer when set to special 'auto' value
  1480        autoHeight: undefined,
  1481        autoPadding: undefined,
  1482        compoundBoundsClean: false,
  1483        // whether the compound dimensions need to be recalculated the next time dimensions are read
  1484        listeners: [],
  1485        // array of bound listeners
  1486        group: group,
  1487        // string; 'nodes' or 'edges'
  1488        style: {},
  1489        // properties as set by the style
  1490        rstyle: {},
  1491        // properties for style sent from the renderer to the core
  1492        styleCxts: [],
  1493        // applied style contexts from the styler
  1494        styleKeys: {},
  1495        // per-group keys of style property values
  1496        removed: true,
  1497        // whether it's inside the vis; true if removed (set true here since we call restore)
  1498        selected: params.selected ? true : false,
  1499        // whether it's selected
  1500        selectable: params.selectable === undefined ? true : params.selectable ? true : false,
  1501        // whether it's selectable
  1502        locked: params.locked ? true : false,
  1503        // whether the element is locked (cannot be moved)
  1504        grabbed: false,
  1505        // whether the element is grabbed by the mouse; renderer sets this privately
  1506        grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
  1507        // whether the element can be grabbed
  1508        pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
  1509        // whether the element has passthrough panning enabled
  1510        active: false,
  1511        // whether the element is active from user interaction
  1512        classes: new Set$1(),
  1513        // map ( className => true )
  1514        animation: {
  1515          // object for currently-running animations
  1516          current: [],
  1517          queue: []
  1518        },
  1519        rscratch: {},
  1520        // object in which the renderer can store information
  1521        scratch: params.scratch || {},
  1522        // scratch objects
  1523        edges: [],
  1524        // array of connected edges
  1525        children: [],
  1526        // array of children
  1527        parent: null,
  1528        // parent ref
  1529        traversalCache: {},
  1530        // cache of output of traversal functions
  1531        backgrounding: false,
  1532        // whether background images are loading
  1533        bbCache: null,
  1534        // cache of the current bounding box
  1535        bbCacheShift: {
  1536          x: 0,
  1537          y: 0
  1538        },
  1539        // shift applied to cached bb to be applied on next get
  1540        bodyBounds: null,
  1541        // bounds cache of element body, w/o overlay
  1542        overlayBounds: null,
  1543        // bounds cache of element body, including overlay
  1544        labelBounds: {
  1545          // bounds cache of labels
  1546          all: null,
  1547          source: null,
  1548          target: null,
  1549          main: null
  1550        },
  1551        arrowBounds: {
  1552          // bounds cache of edge arrows
  1553          source: null,
  1554          target: null,
  1555          'mid-source': null,
  1556          'mid-target': null
  1557        }
  1558      };
  1559  
  1560      if (_p.position.x == null) {
  1561        _p.position.x = 0;
  1562      }
  1563  
  1564      if (_p.position.y == null) {
  1565        _p.position.y = 0;
  1566      } // renderedPosition overrides if specified
  1567  
  1568  
  1569      if (params.renderedPosition) {
  1570        var rpos = params.renderedPosition;
  1571        var pan = cy.pan();
  1572        var zoom = cy.zoom();
  1573        _p.position = {
  1574          x: (rpos.x - pan.x) / zoom,
  1575          y: (rpos.y - pan.y) / zoom
  1576        };
  1577      }
  1578  
  1579      var classes = [];
  1580  
  1581      if (array(params.classes)) {
  1582        classes = params.classes;
  1583      } else if (string(params.classes)) {
  1584        classes = params.classes.split(/\s+/);
  1585      }
  1586  
  1587      for (var i = 0, l = classes.length; i < l; i++) {
  1588        var cls = classes[i];
  1589  
  1590        if (!cls || cls === '') {
  1591          continue;
  1592        }
  1593  
  1594        _p.classes.add(cls);
  1595      }
  1596  
  1597      this.createEmitter();
  1598      var bypass = params.style || params.css;
  1599  
  1600      if (bypass) {
  1601        warn('Setting a `style` bypass at element creation is deprecated');
  1602        this.style(bypass);
  1603      }
  1604  
  1605      if (restore === undefined || restore) {
  1606        this.restore();
  1607      }
  1608    };
  1609  
  1610    var defineSearch = function defineSearch(params) {
  1611      params = {
  1612        bfs: params.bfs || !params.dfs,
  1613        dfs: params.dfs || !params.bfs
  1614      }; // from pseudocode on wikipedia
  1615  
  1616      return function searchFn(roots, fn$1, directed) {
  1617        var options;
  1618  
  1619        if (plainObject(roots) && !elementOrCollection(roots)) {
  1620          options = roots;
  1621          roots = options.roots || options.root;
  1622          fn$1 = options.visit;
  1623          directed = options.directed;
  1624        }
  1625  
  1626        directed = arguments.length === 2 && !fn(fn$1) ? fn$1 : directed;
  1627        fn$1 = fn(fn$1) ? fn$1 : function () {};
  1628        var cy = this._private.cy;
  1629        var v = roots = string(roots) ? this.filter(roots) : roots;
  1630        var Q = [];
  1631        var connectedNodes = [];
  1632        var connectedBy = {};
  1633        var id2depth = {};
  1634        var V = {};
  1635        var j = 0;
  1636        var found;
  1637  
  1638        var _this$byGroup = this.byGroup(),
  1639            nodes = _this$byGroup.nodes,
  1640            edges = _this$byGroup.edges; // enqueue v
  1641  
  1642  
  1643        for (var i = 0; i < v.length; i++) {
  1644          var vi = v[i];
  1645          var viId = vi.id();
  1646  
  1647          if (vi.isNode()) {
  1648            Q.unshift(vi);
  1649  
  1650            if (params.bfs) {
  1651              V[viId] = true;
  1652              connectedNodes.push(vi);
  1653            }
  1654  
  1655            id2depth[viId] = 0;
  1656          }
  1657        }
  1658  
  1659        var _loop2 = function _loop2() {
  1660          var v = params.bfs ? Q.shift() : Q.pop();
  1661          var vId = v.id();
  1662  
  1663          if (params.dfs) {
  1664            if (V[vId]) {
  1665              return "continue";
  1666            }
  1667  
  1668            V[vId] = true;
  1669            connectedNodes.push(v);
  1670          }
  1671  
  1672          var depth = id2depth[vId];
  1673          var prevEdge = connectedBy[vId];
  1674          var src = prevEdge != null ? prevEdge.source() : null;
  1675          var tgt = prevEdge != null ? prevEdge.target() : null;
  1676          var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
  1677          var ret = void 0;
  1678          ret = fn$1(v, prevEdge, prevNode, j++, depth);
  1679  
  1680          if (ret === true) {
  1681            found = v;
  1682            return "break";
  1683          }
  1684  
  1685          if (ret === false) {
  1686            return "break";
  1687          }
  1688  
  1689          var vwEdges = v.connectedEdges().filter(function (e) {
  1690            return (!directed || e.source().same(v)) && edges.has(e);
  1691          });
  1692  
  1693          for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
  1694            var e = vwEdges[_i2];
  1695            var w = e.connectedNodes().filter(function (n) {
  1696              return !n.same(v) && nodes.has(n);
  1697            });
  1698            var wId = w.id();
  1699  
  1700            if (w.length !== 0 && !V[wId]) {
  1701              w = w[0];
  1702              Q.push(w);
  1703  
  1704              if (params.bfs) {
  1705                V[wId] = true;
  1706                connectedNodes.push(w);
  1707              }
  1708  
  1709              connectedBy[wId] = e;
  1710              id2depth[wId] = id2depth[vId] + 1;
  1711            }
  1712          }
  1713        };
  1714  
  1715        _loop: while (Q.length !== 0) {
  1716          var _ret = _loop2();
  1717  
  1718          switch (_ret) {
  1719            case "continue":
  1720              continue;
  1721  
  1722            case "break":
  1723              break _loop;
  1724          }
  1725        }
  1726  
  1727        var connectedEles = cy.collection();
  1728  
  1729        for (var _i = 0; _i < connectedNodes.length; _i++) {
  1730          var node = connectedNodes[_i];
  1731          var edge = connectedBy[node.id()];
  1732  
  1733          if (edge != null) {
  1734            connectedEles.merge(edge);
  1735          }
  1736  
  1737          connectedEles.merge(node);
  1738        }
  1739  
  1740        return {
  1741          path: cy.collection(connectedEles),
  1742          found: cy.collection(found)
  1743        };
  1744      };
  1745    }; // search, spanning trees, etc
  1746  
  1747  
  1748    var elesfn = {
  1749      breadthFirstSearch: defineSearch({
  1750        bfs: true
  1751      }),
  1752      depthFirstSearch: defineSearch({
  1753        dfs: true
  1754      })
  1755    }; // nice, short mathemathical alias
  1756  
  1757    elesfn.bfs = elesfn.breadthFirstSearch;
  1758    elesfn.dfs = elesfn.depthFirstSearch;
  1759  
  1760    var heap = createCommonjsModule(function (module, exports) {
  1761    // Generated by CoffeeScript 1.8.0
  1762    (function() {
  1763      var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup;
  1764  
  1765      floor = Math.floor, min = Math.min;
  1766  
  1767  
  1768      /*
  1769      Default comparison function to be used
  1770       */
  1771  
  1772      defaultCmp = function(x, y) {
  1773        if (x < y) {
  1774          return -1;
  1775        }
  1776        if (x > y) {
  1777          return 1;
  1778        }
  1779        return 0;
  1780      };
  1781  
  1782  
  1783      /*
  1784      Insert item x in list a, and keep it sorted assuming a is sorted.
  1785      
  1786      If x is already in a, insert it to the right of the rightmost x.
  1787      
  1788      Optional args lo (default 0) and hi (default a.length) bound the slice
  1789      of a to be searched.
  1790       */
  1791  
  1792      insort = function(a, x, lo, hi, cmp) {
  1793        var mid;
  1794        if (lo == null) {
  1795          lo = 0;
  1796        }
  1797        if (cmp == null) {
  1798          cmp = defaultCmp;
  1799        }
  1800        if (lo < 0) {
  1801          throw new Error('lo must be non-negative');
  1802        }
  1803        if (hi == null) {
  1804          hi = a.length;
  1805        }
  1806        while (lo < hi) {
  1807          mid = floor((lo + hi) / 2);
  1808          if (cmp(x, a[mid]) < 0) {
  1809            hi = mid;
  1810          } else {
  1811            lo = mid + 1;
  1812          }
  1813        }
  1814        return ([].splice.apply(a, [lo, lo - lo].concat(x)), x);
  1815      };
  1816  
  1817  
  1818      /*
  1819      Push item onto heap, maintaining the heap invariant.
  1820       */
  1821  
  1822      heappush = function(array, item, cmp) {
  1823        if (cmp == null) {
  1824          cmp = defaultCmp;
  1825        }
  1826        array.push(item);
  1827        return _siftdown(array, 0, array.length - 1, cmp);
  1828      };
  1829  
  1830  
  1831      /*
  1832      Pop the smallest item off the heap, maintaining the heap invariant.
  1833       */
  1834  
  1835      heappop = function(array, cmp) {
  1836        var lastelt, returnitem;
  1837        if (cmp == null) {
  1838          cmp = defaultCmp;
  1839        }
  1840        lastelt = array.pop();
  1841        if (array.length) {
  1842          returnitem = array[0];
  1843          array[0] = lastelt;
  1844          _siftup(array, 0, cmp);
  1845        } else {
  1846          returnitem = lastelt;
  1847        }
  1848        return returnitem;
  1849      };
  1850  
  1851  
  1852      /*
  1853      Pop and return the current smallest value, and add the new item.
  1854      
  1855      This is more efficient than heappop() followed by heappush(), and can be
  1856      more appropriate when using a fixed size heap. Note that the value
  1857      returned may be larger than item! That constrains reasonable use of
  1858      this routine unless written as part of a conditional replacement:
  1859          if item > array[0]
  1860            item = heapreplace(array, item)
  1861       */
  1862  
  1863      heapreplace = function(array, item, cmp) {
  1864        var returnitem;
  1865        if (cmp == null) {
  1866          cmp = defaultCmp;
  1867        }
  1868        returnitem = array[0];
  1869        array[0] = item;
  1870        _siftup(array, 0, cmp);
  1871        return returnitem;
  1872      };
  1873  
  1874  
  1875      /*
  1876      Fast version of a heappush followed by a heappop.
  1877       */
  1878  
  1879      heappushpop = function(array, item, cmp) {
  1880        var _ref;
  1881        if (cmp == null) {
  1882          cmp = defaultCmp;
  1883        }
  1884        if (array.length && cmp(array[0], item) < 0) {
  1885          _ref = [array[0], item], item = _ref[0], array[0] = _ref[1];
  1886          _siftup(array, 0, cmp);
  1887        }
  1888        return item;
  1889      };
  1890  
  1891  
  1892      /*
  1893      Transform list into a heap, in-place, in O(array.length) time.
  1894       */
  1895  
  1896      heapify = function(array, cmp) {
  1897        var i, _i, _len, _ref1, _results, _results1;
  1898        if (cmp == null) {
  1899          cmp = defaultCmp;
  1900        }
  1901        _ref1 = (function() {
  1902          _results1 = [];
  1903          for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--){ _results1.push(_j); }
  1904          return _results1;
  1905        }).apply(this).reverse();
  1906        _results = [];
  1907        for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
  1908          i = _ref1[_i];
  1909          _results.push(_siftup(array, i, cmp));
  1910        }
  1911        return _results;
  1912      };
  1913  
  1914  
  1915      /*
  1916      Update the position of the given item in the heap.
  1917      This function should be called every time the item is being modified.
  1918       */
  1919  
  1920      updateItem = function(array, item, cmp) {
  1921        var pos;
  1922        if (cmp == null) {
  1923          cmp = defaultCmp;
  1924        }
  1925        pos = array.indexOf(item);
  1926        if (pos === -1) {
  1927          return;
  1928        }
  1929        _siftdown(array, 0, pos, cmp);
  1930        return _siftup(array, pos, cmp);
  1931      };
  1932  
  1933  
  1934      /*
  1935      Find the n largest elements in a dataset.
  1936       */
  1937  
  1938      nlargest = function(array, n, cmp) {
  1939        var elem, result, _i, _len, _ref;
  1940        if (cmp == null) {
  1941          cmp = defaultCmp;
  1942        }
  1943        result = array.slice(0, n);
  1944        if (!result.length) {
  1945          return result;
  1946        }
  1947        heapify(result, cmp);
  1948        _ref = array.slice(n);
  1949        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  1950          elem = _ref[_i];
  1951          heappushpop(result, elem, cmp);
  1952        }
  1953        return result.sort(cmp).reverse();
  1954      };
  1955  
  1956  
  1957      /*
  1958      Find the n smallest elements in a dataset.
  1959       */
  1960  
  1961      nsmallest = function(array, n, cmp) {
  1962        var elem, i, los, result, _i, _j, _len, _ref, _ref1, _results;
  1963        if (cmp == null) {
  1964          cmp = defaultCmp;
  1965        }
  1966        if (n * 10 <= array.length) {
  1967          result = array.slice(0, n).sort(cmp);
  1968          if (!result.length) {
  1969            return result;
  1970          }
  1971          los = result[result.length - 1];
  1972          _ref = array.slice(n);
  1973          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  1974            elem = _ref[_i];
  1975            if (cmp(elem, los) < 0) {
  1976              insort(result, elem, 0, null, cmp);
  1977              result.pop();
  1978              los = result[result.length - 1];
  1979            }
  1980          }
  1981          return result;
  1982        }
  1983        heapify(array, cmp);
  1984        _results = [];
  1985        for (i = _j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
  1986          _results.push(heappop(array, cmp));
  1987        }
  1988        return _results;
  1989      };
  1990  
  1991      _siftdown = function(array, startpos, pos, cmp) {
  1992        var newitem, parent, parentpos;
  1993        if (cmp == null) {
  1994          cmp = defaultCmp;
  1995        }
  1996        newitem = array[pos];
  1997        while (pos > startpos) {
  1998          parentpos = (pos - 1) >> 1;
  1999          parent = array[parentpos];
  2000          if (cmp(newitem, parent) < 0) {
  2001            array[pos] = parent;
  2002            pos = parentpos;
  2003            continue;
  2004          }
  2005          break;
  2006        }
  2007        return array[pos] = newitem;
  2008      };
  2009  
  2010      _siftup = function(array, pos, cmp) {
  2011        var childpos, endpos, newitem, rightpos, startpos;
  2012        if (cmp == null) {
  2013          cmp = defaultCmp;
  2014        }
  2015        endpos = array.length;
  2016        startpos = pos;
  2017        newitem = array[pos];
  2018        childpos = 2 * pos + 1;
  2019        while (childpos < endpos) {
  2020          rightpos = childpos + 1;
  2021          if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) {
  2022            childpos = rightpos;
  2023          }
  2024          array[pos] = array[childpos];
  2025          pos = childpos;
  2026          childpos = 2 * pos + 1;
  2027        }
  2028        array[pos] = newitem;
  2029        return _siftdown(array, startpos, pos, cmp);
  2030      };
  2031  
  2032      Heap = (function() {
  2033        Heap.push = heappush;
  2034  
  2035        Heap.pop = heappop;
  2036  
  2037        Heap.replace = heapreplace;
  2038  
  2039        Heap.pushpop = heappushpop;
  2040  
  2041        Heap.heapify = heapify;
  2042  
  2043        Heap.updateItem = updateItem;
  2044  
  2045        Heap.nlargest = nlargest;
  2046  
  2047        Heap.nsmallest = nsmallest;
  2048  
  2049        function Heap(cmp) {
  2050          this.cmp = cmp != null ? cmp : defaultCmp;
  2051          this.nodes = [];
  2052        }
  2053  
  2054        Heap.prototype.push = function(x) {
  2055          return heappush(this.nodes, x, this.cmp);
  2056        };
  2057  
  2058        Heap.prototype.pop = function() {
  2059          return heappop(this.nodes, this.cmp);
  2060        };
  2061  
  2062        Heap.prototype.peek = function() {
  2063          return this.nodes[0];
  2064        };
  2065  
  2066        Heap.prototype.contains = function(x) {
  2067          return this.nodes.indexOf(x) !== -1;
  2068        };
  2069  
  2070        Heap.prototype.replace = function(x) {
  2071          return heapreplace(this.nodes, x, this.cmp);
  2072        };
  2073  
  2074        Heap.prototype.pushpop = function(x) {
  2075          return heappushpop(this.nodes, x, this.cmp);
  2076        };
  2077  
  2078        Heap.prototype.heapify = function() {
  2079          return heapify(this.nodes, this.cmp);
  2080        };
  2081  
  2082        Heap.prototype.updateItem = function(x) {
  2083          return updateItem(this.nodes, x, this.cmp);
  2084        };
  2085  
  2086        Heap.prototype.clear = function() {
  2087          return this.nodes = [];
  2088        };
  2089  
  2090        Heap.prototype.empty = function() {
  2091          return this.nodes.length === 0;
  2092        };
  2093  
  2094        Heap.prototype.size = function() {
  2095          return this.nodes.length;
  2096        };
  2097  
  2098        Heap.prototype.clone = function() {
  2099          var heap;
  2100          heap = new Heap();
  2101          heap.nodes = this.nodes.slice(0);
  2102          return heap;
  2103        };
  2104  
  2105        Heap.prototype.toArray = function() {
  2106          return this.nodes.slice(0);
  2107        };
  2108  
  2109        Heap.prototype.insert = Heap.prototype.push;
  2110  
  2111        Heap.prototype.top = Heap.prototype.peek;
  2112  
  2113        Heap.prototype.front = Heap.prototype.peek;
  2114  
  2115        Heap.prototype.has = Heap.prototype.contains;
  2116  
  2117        Heap.prototype.copy = Heap.prototype.clone;
  2118  
  2119        return Heap;
  2120  
  2121      })();
  2122  
  2123      (function(root, factory) {
  2124        {
  2125          return module.exports = factory();
  2126        }
  2127      })(this, function() {
  2128        return Heap;
  2129      });
  2130  
  2131    }).call(commonjsGlobal);
  2132    });
  2133  
  2134    var heap$1 = heap;
  2135  
  2136    var dijkstraDefaults = defaults({
  2137      root: null,
  2138      weight: function weight(edge) {
  2139        return 1;
  2140      },
  2141      directed: false
  2142    });
  2143    var elesfn$1 = {
  2144      dijkstra: function dijkstra(options) {
  2145        if (!plainObject(options)) {
  2146          var args = arguments;
  2147          options = {
  2148            root: args[0],
  2149            weight: args[1],
  2150            directed: args[2]
  2151          };
  2152        }
  2153  
  2154        var _dijkstraDefaults = dijkstraDefaults(options),
  2155            root = _dijkstraDefaults.root,
  2156            weight = _dijkstraDefaults.weight,
  2157            directed = _dijkstraDefaults.directed;
  2158  
  2159        var eles = this;
  2160        var weightFn = weight;
  2161        var source = string(root) ? this.filter(root)[0] : root[0];
  2162        var dist = {};
  2163        var prev = {};
  2164        var knownDist = {};
  2165  
  2166        var _this$byGroup = this.byGroup(),
  2167            nodes = _this$byGroup.nodes,
  2168            edges = _this$byGroup.edges;
  2169  
  2170        edges.unmergeBy(function (ele) {
  2171          return ele.isLoop();
  2172        });
  2173  
  2174        var getDist = function getDist(node) {
  2175          return dist[node.id()];
  2176        };
  2177  
  2178        var setDist = function setDist(node, d) {
  2179          dist[node.id()] = d;
  2180          Q.updateItem(node);
  2181        };
  2182  
  2183        var Q = new heap$1(function (a, b) {
  2184          return getDist(a) - getDist(b);
  2185        });
  2186  
  2187        for (var i = 0; i < nodes.length; i++) {
  2188          var node = nodes[i];
  2189          dist[node.id()] = node.same(source) ? 0 : Infinity;
  2190          Q.push(node);
  2191        }
  2192  
  2193        var distBetween = function distBetween(u, v) {
  2194          var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
  2195          var smallestDistance = Infinity;
  2196          var smallestEdge;
  2197  
  2198          for (var _i = 0; _i < uvs.length; _i++) {
  2199            var edge = uvs[_i];
  2200  
  2201            var _weight = weightFn(edge);
  2202  
  2203            if (_weight < smallestDistance || !smallestEdge) {
  2204              smallestDistance = _weight;
  2205              smallestEdge = edge;
  2206            }
  2207          }
  2208  
  2209          return {
  2210            edge: smallestEdge,
  2211            dist: smallestDistance
  2212          };
  2213        };
  2214  
  2215        while (Q.size() > 0) {
  2216          var u = Q.pop();
  2217          var smalletsDist = getDist(u);
  2218          var uid = u.id();
  2219          knownDist[uid] = smalletsDist;
  2220  
  2221          if (smalletsDist === Infinity) {
  2222            continue;
  2223          }
  2224  
  2225          var neighbors = u.neighborhood().intersect(nodes);
  2226  
  2227          for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
  2228            var v = neighbors[_i2];
  2229            var vid = v.id();
  2230            var vDist = distBetween(u, v);
  2231            var alt = smalletsDist + vDist.dist;
  2232  
  2233            if (alt < getDist(v)) {
  2234              setDist(v, alt);
  2235              prev[vid] = {
  2236                node: u,
  2237                edge: vDist.edge
  2238              };
  2239            }
  2240          } // for
  2241  
  2242        } // while
  2243  
  2244  
  2245        return {
  2246          distanceTo: function distanceTo(node) {
  2247            var target = string(node) ? nodes.filter(node)[0] : node[0];
  2248            return knownDist[target.id()];
  2249          },
  2250          pathTo: function pathTo(node) {
  2251            var target = string(node) ? nodes.filter(node)[0] : node[0];
  2252            var S = [];
  2253            var u = target;
  2254            var uid = u.id();
  2255  
  2256            if (target.length > 0) {
  2257              S.unshift(target);
  2258  
  2259              while (prev[uid]) {
  2260                var p = prev[uid];
  2261                S.unshift(p.edge);
  2262                S.unshift(p.node);
  2263                u = p.node;
  2264                uid = u.id();
  2265              }
  2266            }
  2267  
  2268            return eles.spawn(S);
  2269          }
  2270        };
  2271      }
  2272    };
  2273  
  2274    var elesfn$2 = {
  2275      // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
  2276      // implemented from pseudocode from wikipedia
  2277      kruskal: function kruskal(weightFn) {
  2278        weightFn = weightFn || function (edge) {
  2279          return 1;
  2280        };
  2281  
  2282        var _this$byGroup = this.byGroup(),
  2283            nodes = _this$byGroup.nodes,
  2284            edges = _this$byGroup.edges;
  2285  
  2286        var numNodes = nodes.length;
  2287        var forest = new Array(numNodes);
  2288        var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
  2289  
  2290        var findSetIndex = function findSetIndex(ele) {
  2291          for (var i = 0; i < forest.length; i++) {
  2292            var eles = forest[i];
  2293  
  2294            if (eles.has(ele)) {
  2295              return i;
  2296            }
  2297          }
  2298        }; // start with one forest per node
  2299  
  2300  
  2301        for (var i = 0; i < numNodes; i++) {
  2302          forest[i] = this.spawn(nodes[i]);
  2303        }
  2304  
  2305        var S = edges.sort(function (a, b) {
  2306          return weightFn(a) - weightFn(b);
  2307        });
  2308  
  2309        for (var _i = 0; _i < S.length; _i++) {
  2310          var edge = S[_i];
  2311          var u = edge.source()[0];
  2312          var v = edge.target()[0];
  2313          var setUIndex = findSetIndex(u);
  2314          var setVIndex = findSetIndex(v);
  2315          var setU = forest[setUIndex];
  2316          var setV = forest[setVIndex];
  2317  
  2318          if (setUIndex !== setVIndex) {
  2319            A.merge(edge); // combine forests for u and v
  2320  
  2321            setU.merge(setV);
  2322            forest.splice(setVIndex, 1);
  2323          }
  2324        }
  2325  
  2326        return A;
  2327      }
  2328    };
  2329  
  2330    var aStarDefaults = defaults({
  2331      root: null,
  2332      goal: null,
  2333      weight: function weight(edge) {
  2334        return 1;
  2335      },
  2336      heuristic: function heuristic(edge) {
  2337        return 0;
  2338      },
  2339      directed: false
  2340    });
  2341    var elesfn$3 = {
  2342      // Implemented from pseudocode from wikipedia
  2343      aStar: function aStar(options) {
  2344        var cy = this.cy();
  2345  
  2346        var _aStarDefaults = aStarDefaults(options),
  2347            root = _aStarDefaults.root,
  2348            goal = _aStarDefaults.goal,
  2349            heuristic = _aStarDefaults.heuristic,
  2350            directed = _aStarDefaults.directed,
  2351            weight = _aStarDefaults.weight;
  2352  
  2353        root = cy.collection(root)[0];
  2354        goal = cy.collection(goal)[0];
  2355        var sid = root.id();
  2356        var tid = goal.id();
  2357        var gScore = {};
  2358        var fScore = {};
  2359        var closedSetIds = {};
  2360        var openSet = new heap$1(function (a, b) {
  2361          return fScore[a.id()] - fScore[b.id()];
  2362        });
  2363        var openSetIds = new Set$1();
  2364        var cameFrom = {};
  2365        var cameFromEdge = {};
  2366  
  2367        var addToOpenSet = function addToOpenSet(ele, id) {
  2368          openSet.push(ele);
  2369          openSetIds.add(id);
  2370        };
  2371  
  2372        var cMin, cMinId;
  2373  
  2374        var popFromOpenSet = function popFromOpenSet() {
  2375          cMin = openSet.pop();
  2376          cMinId = cMin.id();
  2377          openSetIds["delete"](cMinId);
  2378        };
  2379  
  2380        var isInOpenSet = function isInOpenSet(id) {
  2381          return openSetIds.has(id);
  2382        };
  2383  
  2384        addToOpenSet(root, sid);
  2385        gScore[sid] = 0;
  2386        fScore[sid] = heuristic(root); // Counter
  2387  
  2388        var steps = 0; // Main loop
  2389  
  2390        while (openSet.size() > 0) {
  2391          popFromOpenSet();
  2392          steps++; // If we've found our goal, then we are done
  2393  
  2394          if (cMinId === tid) {
  2395            var path = [];
  2396            var pathNode = goal;
  2397            var pathNodeId = tid;
  2398            var pathEdge = cameFromEdge[pathNodeId];
  2399  
  2400            for (;;) {
  2401              path.unshift(pathNode);
  2402  
  2403              if (pathEdge != null) {
  2404                path.unshift(pathEdge);
  2405              }
  2406  
  2407              pathNode = cameFrom[pathNodeId];
  2408  
  2409              if (pathNode == null) {
  2410                break;
  2411              }
  2412  
  2413              pathNodeId = pathNode.id();
  2414              pathEdge = cameFromEdge[pathNodeId];
  2415            }
  2416  
  2417            return {
  2418              found: true,
  2419              distance: gScore[cMinId],
  2420              path: this.spawn(path),
  2421              steps: steps
  2422            };
  2423          } // Add cMin to processed nodes
  2424  
  2425  
  2426          closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
  2427          // Take into account if graph is directed or not
  2428  
  2429          var vwEdges = cMin._private.edges;
  2430  
  2431          for (var i = 0; i < vwEdges.length; i++) {
  2432            var e = vwEdges[i]; // edge must be in set of calling eles
  2433  
  2434            if (!this.hasElementWithId(e.id())) {
  2435              continue;
  2436            } // cMin must be the source of edge if directed
  2437  
  2438  
  2439            if (directed && e.data('source') !== cMinId) {
  2440              continue;
  2441            }
  2442  
  2443            var wSrc = e.source();
  2444            var wTgt = e.target();
  2445            var w = wSrc.id() !== cMinId ? wSrc : wTgt;
  2446            var wid = w.id(); // node must be in set of calling eles
  2447  
  2448            if (!this.hasElementWithId(wid)) {
  2449              continue;
  2450            } // if node is in closedSet, ignore it
  2451  
  2452  
  2453            if (closedSetIds[wid]) {
  2454              continue;
  2455            } // New tentative score for node w
  2456  
  2457  
  2458            var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
  2459            //   w not present in openSet
  2460            // OR
  2461            //   tentative gScore is less than previous value
  2462            // w not in openSet
  2463  
  2464            if (!isInOpenSet(wid)) {
  2465              gScore[wid] = tempScore;
  2466              fScore[wid] = tempScore + heuristic(w);
  2467              addToOpenSet(w, wid);
  2468              cameFrom[wid] = cMin;
  2469              cameFromEdge[wid] = e;
  2470              continue;
  2471            } // w already in openSet, but with greater gScore
  2472  
  2473  
  2474            if (tempScore < gScore[wid]) {
  2475              gScore[wid] = tempScore;
  2476              fScore[wid] = tempScore + heuristic(w);
  2477              cameFrom[wid] = cMin;
  2478            }
  2479          } // End of neighbors update
  2480  
  2481        } // End of main loop
  2482        // If we've reached here, then we've not reached our goal
  2483  
  2484  
  2485        return {
  2486          found: false,
  2487          distance: undefined,
  2488          path: undefined,
  2489          steps: steps
  2490        };
  2491      }
  2492    }; // elesfn
  2493  
  2494    var floydWarshallDefaults = defaults({
  2495      weight: function weight(edge) {
  2496        return 1;
  2497      },
  2498      directed: false
  2499    });
  2500    var elesfn$4 = {
  2501      // Implemented from pseudocode from wikipedia
  2502      floydWarshall: function floydWarshall(options) {
  2503        var cy = this.cy();
  2504  
  2505        var _floydWarshallDefault = floydWarshallDefaults(options),
  2506            weight = _floydWarshallDefault.weight,
  2507            directed = _floydWarshallDefault.directed;
  2508  
  2509        var weightFn = weight;
  2510  
  2511        var _this$byGroup = this.byGroup(),
  2512            nodes = _this$byGroup.nodes,
  2513            edges = _this$byGroup.edges;
  2514  
  2515        var N = nodes.length;
  2516        var Nsq = N * N;
  2517  
  2518        var indexOf = function indexOf(node) {
  2519          return nodes.indexOf(node);
  2520        };
  2521  
  2522        var atIndex = function atIndex(i) {
  2523          return nodes[i];
  2524        }; // Initialize distance matrix
  2525  
  2526  
  2527        var dist = new Array(Nsq);
  2528  
  2529        for (var n = 0; n < Nsq; n++) {
  2530          var j = n % N;
  2531          var i = (n - j) / N;
  2532  
  2533          if (i === j) {
  2534            dist[n] = 0;
  2535          } else {
  2536            dist[n] = Infinity;
  2537          }
  2538        } // Initialize matrix used for path reconstruction
  2539        // Initialize distance matrix
  2540  
  2541  
  2542        var next = new Array(Nsq);
  2543        var edgeNext = new Array(Nsq); // Process edges
  2544  
  2545        for (var _i = 0; _i < edges.length; _i++) {
  2546          var edge = edges[_i];
  2547          var src = edge.source()[0];
  2548          var tgt = edge.target()[0];
  2549  
  2550          if (src === tgt) {
  2551            continue;
  2552          } // exclude loops
  2553  
  2554  
  2555          var s = indexOf(src);
  2556          var t = indexOf(tgt);
  2557          var st = s * N + t; // source to target index
  2558  
  2559          var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
  2560  
  2561  
  2562          if (dist[st] > _weight) {
  2563            dist[st] = _weight;
  2564            next[st] = t;
  2565            edgeNext[st] = edge;
  2566          } // If undirected graph, process 'reversed' edge
  2567  
  2568  
  2569          if (!directed) {
  2570            var ts = t * N + s; // target to source index
  2571  
  2572            if (!directed && dist[ts] > _weight) {
  2573              dist[ts] = _weight;
  2574              next[ts] = s;
  2575              edgeNext[ts] = edge;
  2576            }
  2577          }
  2578        } // Main loop
  2579  
  2580  
  2581        for (var k = 0; k < N; k++) {
  2582          for (var _i2 = 0; _i2 < N; _i2++) {
  2583            var ik = _i2 * N + k;
  2584  
  2585            for (var _j = 0; _j < N; _j++) {
  2586              var ij = _i2 * N + _j;
  2587              var kj = k * N + _j;
  2588  
  2589              if (dist[ik] + dist[kj] < dist[ij]) {
  2590                dist[ij] = dist[ik] + dist[kj];
  2591                next[ij] = next[ik];
  2592              }
  2593            }
  2594          }
  2595        }
  2596  
  2597        var getArgEle = function getArgEle(ele) {
  2598          return (string(ele) ? cy.filter(ele) : ele)[0];
  2599        };
  2600  
  2601        var indexOfArgEle = function indexOfArgEle(ele) {
  2602          return indexOf(getArgEle(ele));
  2603        };
  2604  
  2605        var res = {
  2606          distance: function distance(from, to) {
  2607            var i = indexOfArgEle(from);
  2608            var j = indexOfArgEle(to);
  2609            return dist[i * N + j];
  2610          },
  2611          path: function path(from, to) {
  2612            var i = indexOfArgEle(from);
  2613            var j = indexOfArgEle(to);
  2614            var fromNode = atIndex(i);
  2615  
  2616            if (i === j) {
  2617              return fromNode.collection();
  2618            }
  2619  
  2620            if (next[i * N + j] == null) {
  2621              return cy.collection();
  2622            }
  2623  
  2624            var path = cy.collection();
  2625            var prev = i;
  2626            var edge;
  2627            path.merge(fromNode);
  2628  
  2629            while (i !== j) {
  2630              prev = i;
  2631              i = next[i * N + j];
  2632              edge = edgeNext[prev * N + i];
  2633              path.merge(edge);
  2634              path.merge(atIndex(i));
  2635            }
  2636  
  2637            return path;
  2638          }
  2639        };
  2640        return res;
  2641      } // floydWarshall
  2642  
  2643    }; // elesfn
  2644  
  2645    var bellmanFordDefaults = defaults({
  2646      weight: function weight(edge) {
  2647        return 1;
  2648      },
  2649      directed: false,
  2650      root: null
  2651    });
  2652    var elesfn$5 = {
  2653      // Implemented from pseudocode from wikipedia
  2654      bellmanFord: function bellmanFord(options) {
  2655        var _this = this;
  2656  
  2657        var _bellmanFordDefaults = bellmanFordDefaults(options),
  2658            weight = _bellmanFordDefaults.weight,
  2659            directed = _bellmanFordDefaults.directed,
  2660            root = _bellmanFordDefaults.root;
  2661  
  2662        var weightFn = weight;
  2663        var eles = this;
  2664        var cy = this.cy();
  2665  
  2666        var _this$byGroup = this.byGroup(),
  2667            edges = _this$byGroup.edges,
  2668            nodes = _this$byGroup.nodes;
  2669  
  2670        var numNodes = nodes.length;
  2671        var infoMap = new Map$1();
  2672        var hasNegativeWeightCycle = false;
  2673        var negativeWeightCycles = [];
  2674        root = cy.collection(root)[0]; // in case selector passed
  2675  
  2676        edges.unmergeBy(function (edge) {
  2677          return edge.isLoop();
  2678        });
  2679        var numEdges = edges.length;
  2680  
  2681        var getInfo = function getInfo(node) {
  2682          var obj = infoMap.get(node.id());
  2683  
  2684          if (!obj) {
  2685            obj = {};
  2686            infoMap.set(node.id(), obj);
  2687          }
  2688  
  2689          return obj;
  2690        };
  2691  
  2692        var getNodeFromTo = function getNodeFromTo(to) {
  2693          return (string(to) ? cy.$(to) : to)[0];
  2694        };
  2695  
  2696        var distanceTo = function distanceTo(to) {
  2697          return getInfo(getNodeFromTo(to)).dist;
  2698        };
  2699  
  2700        var pathTo = function pathTo(to) {
  2701          var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
  2702          var end = getNodeFromTo(to);
  2703          var path = [];
  2704          var node = end;
  2705  
  2706          for (;;) {
  2707            if (node == null) {
  2708              return _this.spawn();
  2709            }
  2710  
  2711            var _getInfo = getInfo(node),
  2712                edge = _getInfo.edge,
  2713                pred = _getInfo.pred;
  2714  
  2715            path.unshift(node[0]);
  2716  
  2717            if (node.same(thisStart) && path.length > 0) {
  2718              break;
  2719            }
  2720  
  2721            if (edge != null) {
  2722              path.unshift(edge);
  2723            }
  2724  
  2725            node = pred;
  2726          }
  2727  
  2728          return eles.spawn(path);
  2729        }; // Initializations { dist, pred, edge }
  2730  
  2731  
  2732        for (var i = 0; i < numNodes; i++) {
  2733          var node = nodes[i];
  2734          var info = getInfo(node);
  2735  
  2736          if (node.same(root)) {
  2737            info.dist = 0;
  2738          } else {
  2739            info.dist = Infinity;
  2740          }
  2741  
  2742          info.pred = null;
  2743          info.edge = null;
  2744        } // Edges relaxation
  2745  
  2746  
  2747        var replacedEdge = false;
  2748  
  2749        var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
  2750          var dist = info1.dist + weight;
  2751  
  2752          if (dist < info2.dist && !edge.same(info1.edge)) {
  2753            info2.dist = dist;
  2754            info2.pred = node1;
  2755            info2.edge = edge;
  2756            replacedEdge = true;
  2757          }
  2758        };
  2759  
  2760        for (var _i = 1; _i < numNodes; _i++) {
  2761          replacedEdge = false;
  2762  
  2763          for (var e = 0; e < numEdges; e++) {
  2764            var edge = edges[e];
  2765            var src = edge.source();
  2766            var tgt = edge.target();
  2767  
  2768            var _weight = weightFn(edge);
  2769  
  2770            var srcInfo = getInfo(src);
  2771            var tgtInfo = getInfo(tgt);
  2772            checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
  2773  
  2774            if (!directed) {
  2775              checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
  2776            }
  2777          }
  2778  
  2779          if (!replacedEdge) {
  2780            break;
  2781          }
  2782        }
  2783  
  2784        if (replacedEdge) {
  2785          // Check for negative weight cycles
  2786          for (var _e = 0; _e < numEdges; _e++) {
  2787            var _edge = edges[_e];
  2788  
  2789            var _src = _edge.source();
  2790  
  2791            var _tgt = _edge.target();
  2792  
  2793            var _weight2 = weightFn(_edge);
  2794  
  2795            var srcDist = getInfo(_src).dist;
  2796            var tgtDist = getInfo(_tgt).dist;
  2797  
  2798            if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
  2799              warn('Graph contains a negative weight cycle for Bellman-Ford');
  2800              hasNegativeWeightCycle = true;
  2801              break;
  2802            }
  2803          }
  2804        }
  2805  
  2806        return {
  2807          distanceTo: distanceTo,
  2808          pathTo: pathTo,
  2809          hasNegativeWeightCycle: hasNegativeWeightCycle,
  2810          negativeWeightCycles: negativeWeightCycles
  2811        };
  2812      } // bellmanFord
  2813  
  2814    }; // elesfn
  2815  
  2816    var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
  2817    // Updates the remaining edge lists
  2818    // Receives as a paramater the edge which causes the collapse
  2819  
  2820    var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
  2821      if (remainingEdges.length === 0) {
  2822        error("Karger-Stein must be run on a connected (sub)graph");
  2823      }
  2824  
  2825      var edgeInfo = remainingEdges[edgeIndex];
  2826      var sourceIn = edgeInfo[1];
  2827      var targetIn = edgeInfo[2];
  2828      var partition1 = nodeMap[sourceIn];
  2829      var partition2 = nodeMap[targetIn];
  2830      var newEdges = remainingEdges; // re-use array
  2831      // Delete all edges between partition1 and partition2
  2832  
  2833      for (var i = newEdges.length - 1; i >= 0; i--) {
  2834        var edge = newEdges[i];
  2835        var src = edge[1];
  2836        var tgt = edge[2];
  2837  
  2838        if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
  2839          newEdges.splice(i, 1);
  2840        }
  2841      } // All edges pointing to partition2 should now point to partition1
  2842  
  2843  
  2844      for (var _i = 0; _i < newEdges.length; _i++) {
  2845        var _edge = newEdges[_i];
  2846  
  2847        if (_edge[1] === partition2) {
  2848          // Check source
  2849          newEdges[_i] = _edge.slice(); // copy
  2850  
  2851          newEdges[_i][1] = partition1;
  2852        } else if (_edge[2] === partition2) {
  2853          // Check target
  2854          newEdges[_i] = _edge.slice(); // copy
  2855  
  2856          newEdges[_i][2] = partition1;
  2857        }
  2858      } // Move all nodes from partition2 to partition1
  2859  
  2860  
  2861      for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
  2862        if (nodeMap[_i2] === partition2) {
  2863          nodeMap[_i2] = partition1;
  2864        }
  2865      }
  2866  
  2867      return newEdges;
  2868    }; // Contracts a graph until we reach a certain number of meta nodes
  2869  
  2870  
  2871    var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
  2872      while (size > sizeLimit) {
  2873        // Choose an edge randomly
  2874        var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
  2875  
  2876        remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
  2877        size--;
  2878      }
  2879  
  2880      return remainingEdges;
  2881    };
  2882  
  2883    var elesfn$6 = {
  2884      // Computes the minimum cut of an undirected graph
  2885      // Returns the correct answer with high probability
  2886      kargerStein: function kargerStein() {
  2887        var _this = this;
  2888  
  2889        var _this$byGroup = this.byGroup(),
  2890            nodes = _this$byGroup.nodes,
  2891            edges = _this$byGroup.edges;
  2892  
  2893        edges.unmergeBy(function (edge) {
  2894          return edge.isLoop();
  2895        });
  2896        var numNodes = nodes.length;
  2897        var numEdges = edges.length;
  2898        var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
  2899        var stopSize = Math.floor(numNodes / sqrt2);
  2900  
  2901        if (numNodes < 2) {
  2902          error('At least 2 nodes are required for Karger-Stein algorithm');
  2903          return undefined;
  2904        } // Now store edge destination as indexes
  2905        // Format for each edge (edge index, source node index, target node index)
  2906  
  2907  
  2908        var edgeIndexes = [];
  2909  
  2910        for (var i = 0; i < numEdges; i++) {
  2911          var e = edges[i];
  2912          edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
  2913        } // We will store the best cut found here
  2914  
  2915  
  2916        var minCutSize = Infinity;
  2917        var minCutEdgeIndexes = [];
  2918        var minCutNodeMap = new Array(numNodes); // Initial meta node partition
  2919  
  2920        var metaNodeMap = new Array(numNodes);
  2921        var metaNodeMap2 = new Array(numNodes);
  2922  
  2923        var copyNodesMap = function copyNodesMap(from, to) {
  2924          for (var _i3 = 0; _i3 < numNodes; _i3++) {
  2925            to[_i3] = from[_i3];
  2926          }
  2927        }; // Main loop
  2928  
  2929  
  2930        for (var iter = 0; iter <= numIter; iter++) {
  2931          // Reset meta node partition
  2932          for (var _i4 = 0; _i4 < numNodes; _i4++) {
  2933            metaNodeMap[_i4] = _i4;
  2934          } // Contract until stop point (stopSize nodes)
  2935  
  2936  
  2937          var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
  2938          var edgesState2 = edgesState.slice(); // copy
  2939          // Create a copy of the colapsed nodes state
  2940  
  2941          copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
  2942  
  2943          var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
  2944          var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
  2945  
  2946          if (res1.length <= res2.length && res1.length < minCutSize) {
  2947            minCutSize = res1.length;
  2948            minCutEdgeIndexes = res1;
  2949            copyNodesMap(metaNodeMap, minCutNodeMap);
  2950          } else if (res2.length <= res1.length && res2.length < minCutSize) {
  2951            minCutSize = res2.length;
  2952            minCutEdgeIndexes = res2;
  2953            copyNodesMap(metaNodeMap2, minCutNodeMap);
  2954          }
  2955        } // end of main loop
  2956        // Construct result
  2957  
  2958  
  2959        var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
  2960          return edges[e[0]];
  2961        }));
  2962        var partition1 = this.spawn();
  2963        var partition2 = this.spawn(); // traverse metaNodeMap for best cut
  2964  
  2965        var witnessNodePartition = minCutNodeMap[0];
  2966  
  2967        for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
  2968          var partitionId = minCutNodeMap[_i5];
  2969          var node = nodes[_i5];
  2970  
  2971          if (partitionId === witnessNodePartition) {
  2972            partition1.merge(node);
  2973          } else {
  2974            partition2.merge(node);
  2975          }
  2976        } // construct components corresponding to each disjoint subset of nodes
  2977  
  2978  
  2979        var constructComponent = function constructComponent(subset) {
  2980          var component = _this.spawn();
  2981  
  2982          subset.forEach(function (node) {
  2983            component.merge(node);
  2984            node.connectedEdges().forEach(function (edge) {
  2985              // ensure edge is within calling collection and edge is not in cut
  2986              if (_this.contains(edge) && !cut.contains(edge)) {
  2987                component.merge(edge);
  2988              }
  2989            });
  2990          });
  2991          return component;
  2992        };
  2993  
  2994        var components = [constructComponent(partition1), constructComponent(partition2)];
  2995        var ret = {
  2996          cut: cut,
  2997          components: components,
  2998          // n.b. partitions are included to be compatible with the old api spec
  2999          // (could be removed in a future major version)
  3000          partition1: partition1,
  3001          partition2: partition2
  3002        };
  3003        return ret;
  3004      }
  3005    }; // elesfn
  3006  
  3007    var copyPosition = function copyPosition(p) {
  3008      return {
  3009        x: p.x,
  3010        y: p.y
  3011      };
  3012    };
  3013    var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
  3014      return {
  3015        x: p.x * zoom + pan.x,
  3016        y: p.y * zoom + pan.y
  3017      };
  3018    };
  3019    var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
  3020      return {
  3021        x: (p.x - pan.x) / zoom,
  3022        y: (p.y - pan.y) / zoom
  3023      };
  3024    };
  3025    var array2point = function array2point(arr) {
  3026      return {
  3027        x: arr[0],
  3028        y: arr[1]
  3029      };
  3030    };
  3031    var min = function min(arr) {
  3032      var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  3033      var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
  3034      var min = Infinity;
  3035  
  3036      for (var i = begin; i < end; i++) {
  3037        var val = arr[i];
  3038  
  3039        if (isFinite(val)) {
  3040          min = Math.min(val, min);
  3041        }
  3042      }
  3043  
  3044      return min;
  3045    };
  3046    var max = function max(arr) {
  3047      var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  3048      var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
  3049      var max = -Infinity;
  3050  
  3051      for (var i = begin; i < end; i++) {
  3052        var val = arr[i];
  3053  
  3054        if (isFinite(val)) {
  3055          max = Math.max(val, max);
  3056        }
  3057      }
  3058  
  3059      return max;
  3060    };
  3061    var mean = function mean(arr) {
  3062      var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  3063      var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
  3064      var total = 0;
  3065      var n = 0;
  3066  
  3067      for (var i = begin; i < end; i++) {
  3068        var val = arr[i];
  3069  
  3070        if (isFinite(val)) {
  3071          total += val;
  3072          n++;
  3073        }
  3074      }
  3075  
  3076      return total / n;
  3077    };
  3078    var median = function median(arr) {
  3079      var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  3080      var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
  3081      var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
  3082      var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
  3083      var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
  3084  
  3085      if (copy) {
  3086        arr = arr.slice(begin, end);
  3087      } else {
  3088        if (end < arr.length) {
  3089          arr.splice(end, arr.length - end);
  3090        }
  3091  
  3092        if (begin > 0) {
  3093          arr.splice(0, begin);
  3094        }
  3095      } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
  3096  
  3097  
  3098      var off = 0; // offset from non-finite values
  3099  
  3100      for (var i = arr.length - 1; i >= 0; i--) {
  3101        var v = arr[i];
  3102  
  3103        if (includeHoles) {
  3104          if (!isFinite(v)) {
  3105            arr[i] = -Infinity;
  3106            off++;
  3107          }
  3108        } else {
  3109          // just remove it if we don't want to consider holes
  3110          arr.splice(i, 1);
  3111        }
  3112      }
  3113  
  3114      if (sort) {
  3115        arr.sort(function (a, b) {
  3116          return a - b;
  3117        }); // requires copy = true if you don't want to change the orig
  3118      }
  3119  
  3120      var len = arr.length;
  3121      var mid = Math.floor(len / 2);
  3122  
  3123      if (len % 2 !== 0) {
  3124        return arr[mid + 1 + off];
  3125      } else {
  3126        return (arr[mid - 1 + off] + arr[mid + off]) / 2;
  3127      }
  3128    };
  3129    var deg2rad = function deg2rad(deg) {
  3130      return Math.PI * deg / 180;
  3131    };
  3132    var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
  3133      return Math.atan2(dispY, dispX) - Math.PI / 2;
  3134    };
  3135    var log2 = Math.log2 || function (n) {
  3136      return Math.log(n) / Math.log(2);
  3137    };
  3138    var signum = function signum(x) {
  3139      if (x > 0) {
  3140        return 1;
  3141      } else if (x < 0) {
  3142        return -1;
  3143      } else {
  3144        return 0;
  3145      }
  3146    };
  3147    var dist = function dist(p1, p2) {
  3148      return Math.sqrt(sqdist(p1, p2));
  3149    };
  3150    var sqdist = function sqdist(p1, p2) {
  3151      var dx = p2.x - p1.x;
  3152      var dy = p2.y - p1.y;
  3153      return dx * dx + dy * dy;
  3154    };
  3155    var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
  3156      var length = v.length; // First, get sum of all elements
  3157  
  3158      var total = 0;
  3159  
  3160      for (var i = 0; i < length; i++) {
  3161        total += v[i];
  3162      } // Now, divide each by the sum of all elements
  3163  
  3164  
  3165      for (var _i = 0; _i < length; _i++) {
  3166        v[_i] = v[_i] / total;
  3167      }
  3168  
  3169      return v;
  3170    };
  3171  
  3172    var qbezierAt = function qbezierAt(p0, p1, p2, t) {
  3173      return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
  3174    };
  3175    var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
  3176      return {
  3177        x: qbezierAt(p0.x, p1.x, p2.x, t),
  3178        y: qbezierAt(p0.y, p1.y, p2.y, t)
  3179      };
  3180    };
  3181    var lineAt = function lineAt(p0, p1, t, d) {
  3182      var vec = {
  3183        x: p1.x - p0.x,
  3184        y: p1.y - p0.y
  3185      };
  3186      var vecDist = dist(p0, p1);
  3187      var normVec = {
  3188        x: vec.x / vecDist,
  3189        y: vec.y / vecDist
  3190      };
  3191      t = t == null ? 0 : t;
  3192      d = d != null ? d : t * vecDist;
  3193      return {
  3194        x: p0.x + normVec.x * d,
  3195        y: p0.y + normVec.y * d
  3196      };
  3197    };
  3198    var bound = function bound(min, val, max) {
  3199      return Math.max(min, Math.min(max, val));
  3200    }; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
  3201  
  3202    var makeBoundingBox = function makeBoundingBox(bb) {
  3203      if (bb == null) {
  3204        return {
  3205          x1: Infinity,
  3206          y1: Infinity,
  3207          x2: -Infinity,
  3208          y2: -Infinity,
  3209          w: 0,
  3210          h: 0
  3211        };
  3212      } else if (bb.x1 != null && bb.y1 != null) {
  3213        if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
  3214          return {
  3215            x1: bb.x1,
  3216            y1: bb.y1,
  3217            x2: bb.x2,
  3218            y2: bb.y2,
  3219            w: bb.x2 - bb.x1,
  3220            h: bb.y2 - bb.y1
  3221          };
  3222        } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
  3223          return {
  3224            x1: bb.x1,
  3225            y1: bb.y1,
  3226            x2: bb.x1 + bb.w,
  3227            y2: bb.y1 + bb.h,
  3228            w: bb.w,
  3229            h: bb.h
  3230          };
  3231        }
  3232      }
  3233    };
  3234    var copyBoundingBox = function copyBoundingBox(bb) {
  3235      return {
  3236        x1: bb.x1,
  3237        x2: bb.x2,
  3238        w: bb.w,
  3239        y1: bb.y1,
  3240        y2: bb.y2,
  3241        h: bb.h
  3242      };
  3243    };
  3244    var clearBoundingBox = function clearBoundingBox(bb) {
  3245      bb.x1 = Infinity;
  3246      bb.y1 = Infinity;
  3247      bb.x2 = -Infinity;
  3248      bb.y2 = -Infinity;
  3249      bb.w = 0;
  3250      bb.h = 0;
  3251    };
  3252    var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
  3253      // update bb1 with bb2 bounds
  3254      bb1.x1 = Math.min(bb1.x1, bb2.x1);
  3255      bb1.x2 = Math.max(bb1.x2, bb2.x2);
  3256      bb1.w = bb1.x2 - bb1.x1;
  3257      bb1.y1 = Math.min(bb1.y1, bb2.y1);
  3258      bb1.y2 = Math.max(bb1.y2, bb2.y2);
  3259      bb1.h = bb1.y2 - bb1.y1;
  3260    };
  3261    var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
  3262      bb.x1 = Math.min(bb.x1, x);
  3263      bb.x2 = Math.max(bb.x2, x);
  3264      bb.w = bb.x2 - bb.x1;
  3265      bb.y1 = Math.min(bb.y1, y);
  3266      bb.y2 = Math.max(bb.y2, y);
  3267      bb.h = bb.y2 - bb.y1;
  3268    };
  3269    var expandBoundingBox = function expandBoundingBox(bb) {
  3270      var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  3271      bb.x1 -= padding;
  3272      bb.x2 += padding;
  3273      bb.y1 -= padding;
  3274      bb.y2 += padding;
  3275      bb.w = bb.x2 - bb.x1;
  3276      bb.h = bb.y2 - bb.y1;
  3277      return bb;
  3278    };
  3279    var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
  3280      var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
  3281      var top, right, bottom, left;
  3282  
  3283      if (padding.length === 1) {
  3284        top = right = bottom = left = padding[0];
  3285      } else if (padding.length === 2) {
  3286        top = bottom = padding[0];
  3287        left = right = padding[1];
  3288      } else if (padding.length === 4) {
  3289        var _padding = _slicedToArray(padding, 4);
  3290  
  3291        top = _padding[0];
  3292        right = _padding[1];
  3293        bottom = _padding[2];
  3294        left = _padding[3];
  3295      }
  3296  
  3297      bb.x1 -= left;
  3298      bb.x2 += right;
  3299      bb.y1 -= top;
  3300      bb.y2 += bottom;
  3301      bb.w = bb.x2 - bb.x1;
  3302      bb.h = bb.y2 - bb.y1;
  3303      return bb;
  3304    };
  3305  
  3306    var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
  3307      bb1.x1 = bb2.x1;
  3308      bb1.y1 = bb2.y1;
  3309      bb1.x2 = bb2.x2;
  3310      bb1.y2 = bb2.y2;
  3311      bb1.w = bb1.x2 - bb1.x1;
  3312      bb1.h = bb1.y2 - bb1.y1;
  3313    };
  3314    var assignShiftToBoundingBox = function assignShiftToBoundingBox(bb, delta) {
  3315      bb.x1 += delta.x;
  3316      bb.x2 += delta.x;
  3317      bb.y1 += delta.y;
  3318      bb.y2 += delta.y;
  3319    };
  3320    var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
  3321      // case: one bb to right of other
  3322      if (bb1.x1 > bb2.x2) {
  3323        return false;
  3324      }
  3325  
  3326      if (bb2.x1 > bb1.x2) {
  3327        return false;
  3328      } // case: one bb to left of other
  3329  
  3330  
  3331      if (bb1.x2 < bb2.x1) {
  3332        return false;
  3333      }
  3334  
  3335      if (bb2.x2 < bb1.x1) {
  3336        return false;
  3337      } // case: one bb above other
  3338  
  3339  
  3340      if (bb1.y2 < bb2.y1) {
  3341        return false;
  3342      }
  3343  
  3344      if (bb2.y2 < bb1.y1) {
  3345        return false;
  3346      } // case: one bb below other
  3347  
  3348  
  3349      if (bb1.y1 > bb2.y2) {
  3350        return false;
  3351      }
  3352  
  3353      if (bb2.y1 > bb1.y2) {
  3354        return false;
  3355      } // otherwise, must have some overlap
  3356  
  3357  
  3358      return true;
  3359    };
  3360    var inBoundingBox = function inBoundingBox(bb, x, y) {
  3361      return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
  3362    };
  3363    var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
  3364      return inBoundingBox(bb, pt.x, pt.y);
  3365    };
  3366    var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
  3367      return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
  3368    };
  3369    var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
  3370      var cornerRadius = getRoundRectangleRadius(width, height);
  3371      var halfWidth = width / 2;
  3372      var halfHeight = height / 2; // Check intersections with straight line segments
  3373  
  3374      var straightLineIntersections; // Top segment, left to right
  3375  
  3376      {
  3377        var topStartX = nodeX - halfWidth + cornerRadius - padding;
  3378        var topStartY = nodeY - halfHeight - padding;
  3379        var topEndX = nodeX + halfWidth - cornerRadius + padding;
  3380        var topEndY = topStartY;
  3381        straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
  3382  
  3383        if (straightLineIntersections.length > 0) {
  3384          return straightLineIntersections;
  3385        }
  3386      } // Right segment, top to bottom
  3387  
  3388      {
  3389        var rightStartX = nodeX + halfWidth + padding;
  3390        var rightStartY = nodeY - halfHeight + cornerRadius - padding;
  3391        var rightEndX = rightStartX;
  3392        var rightEndY = nodeY + halfHeight - cornerRadius + padding;
  3393        straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
  3394  
  3395        if (straightLineIntersections.length > 0) {
  3396          return straightLineIntersections;
  3397        }
  3398      } // Bottom segment, left to right
  3399  
  3400      {
  3401        var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
  3402        var bottomStartY = nodeY + halfHeight + padding;
  3403        var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
  3404        var bottomEndY = bottomStartY;
  3405        straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
  3406  
  3407        if (straightLineIntersections.length > 0) {
  3408          return straightLineIntersections;
  3409        }
  3410      } // Left segment, top to bottom
  3411  
  3412      {
  3413        var leftStartX = nodeX - halfWidth - padding;
  3414        var leftStartY = nodeY - halfHeight + cornerRadius - padding;
  3415        var leftEndX = leftStartX;
  3416        var leftEndY = nodeY + halfHeight - cornerRadius + padding;
  3417        straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
  3418  
  3419        if (straightLineIntersections.length > 0) {
  3420          return straightLineIntersections;
  3421        }
  3422      } // Check intersections with arc segments
  3423  
  3424      var arcIntersections; // Top Left
  3425  
  3426      {
  3427        var topLeftCenterX = nodeX - halfWidth + cornerRadius;
  3428        var topLeftCenterY = nodeY - halfHeight + cornerRadius;
  3429        arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
  3430  
  3431        if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
  3432          return [arcIntersections[0], arcIntersections[1]];
  3433        }
  3434      } // Top Right
  3435  
  3436      {
  3437        var topRightCenterX = nodeX + halfWidth - cornerRadius;
  3438        var topRightCenterY = nodeY - halfHeight + cornerRadius;
  3439        arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
  3440  
  3441        if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
  3442          return [arcIntersections[0], arcIntersections[1]];
  3443        }
  3444      } // Bottom Right
  3445  
  3446      {
  3447        var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
  3448        var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
  3449        arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
  3450  
  3451        if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
  3452          return [arcIntersections[0], arcIntersections[1]];
  3453        }
  3454      } // Bottom Left
  3455  
  3456      {
  3457        var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
  3458        var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
  3459        arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
  3460  
  3461        if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
  3462          return [arcIntersections[0], arcIntersections[1]];
  3463        }
  3464      }
  3465      return []; // if nothing
  3466    };
  3467    var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
  3468      var t = tolerance;
  3469      var x1 = Math.min(lx1, lx2);
  3470      var x2 = Math.max(lx1, lx2);
  3471      var y1 = Math.min(ly1, ly2);
  3472      var y2 = Math.max(ly1, ly2);
  3473      return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
  3474    };
  3475    var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
  3476      var bb = {
  3477        x1: Math.min(x1, x3, x2) - tolerance,
  3478        x2: Math.max(x1, x3, x2) + tolerance,
  3479        y1: Math.min(y1, y3, y2) - tolerance,
  3480        y2: Math.max(y1, y3, y2) + tolerance
  3481      }; // if outside the rough bounding box for the bezier, then it can't be a hit
  3482  
  3483      if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
  3484        // console.log('bezier out of rough bb')
  3485        return false;
  3486      } else {
  3487        // console.log('do more expensive check');
  3488        return true;
  3489      }
  3490    };
  3491    var solveQuadratic = function solveQuadratic(a, b, c, val) {
  3492      c -= val;
  3493      var r = b * b - 4 * a * c;
  3494  
  3495      if (r < 0) {
  3496        return [];
  3497      }
  3498  
  3499      var sqrtR = Math.sqrt(r);
  3500      var denom = 2 * a;
  3501      var root1 = (-b + sqrtR) / denom;
  3502      var root2 = (-b - sqrtR) / denom;
  3503      return [root1, root2];
  3504    };
  3505    var solveCubic = function solveCubic(a, b, c, d, result) {
  3506      // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
  3507      // r is the real component, i is the imaginary component
  3508      // An implementation of the Cardano method from the year 1545
  3509      // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
  3510      var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
  3511  
  3512      if (a === 0) {
  3513        a = epsilon;
  3514      }
  3515  
  3516      b /= a;
  3517      c /= a;
  3518      d /= a;
  3519      var discriminant, q, r, dum1, s, t, term1, r13;
  3520      q = (3.0 * c - b * b) / 9.0;
  3521      r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
  3522      r /= 54.0;
  3523      discriminant = q * q * q + r * r;
  3524      result[1] = 0;
  3525      term1 = b / 3.0;
  3526  
  3527      if (discriminant > 0) {
  3528        s = r + Math.sqrt(discriminant);
  3529        s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
  3530        t = r - Math.sqrt(discriminant);
  3531        t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
  3532        result[0] = -term1 + s + t;
  3533        term1 += (s + t) / 2.0;
  3534        result[4] = result[2] = -term1;
  3535        term1 = Math.sqrt(3.0) * (-t + s) / 2;
  3536        result[3] = term1;
  3537        result[5] = -term1;
  3538        return;
  3539      }
  3540  
  3541      result[5] = result[3] = 0;
  3542  
  3543      if (discriminant === 0) {
  3544        r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
  3545        result[0] = -term1 + 2.0 * r13;
  3546        result[4] = result[2] = -(r13 + term1);
  3547        return;
  3548      }
  3549  
  3550      q = -q;
  3551      dum1 = q * q * q;
  3552      dum1 = Math.acos(r / Math.sqrt(dum1));
  3553      r13 = 2.0 * Math.sqrt(q);
  3554      result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
  3555      result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
  3556      result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
  3557      return;
  3558    };
  3559    var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
  3560      // Find minimum distance by using the minimum of the distance
  3561      // function between the given point and the curve
  3562      // This gives the coefficients of the resulting cubic equation
  3563      // whose roots tell us where a possible minimum is
  3564      // (Coefficients are divided by 4)
  3565      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;
  3566      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;
  3567      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;
  3568      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);
  3569  
  3570      var roots = []; // Use the cubic solving algorithm
  3571  
  3572      solveCubic(a, b, c, d, roots);
  3573      var zeroThreshold = 0.0000001;
  3574      var params = [];
  3575  
  3576      for (var index = 0; index < 6; index += 2) {
  3577        if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
  3578          params.push(roots[index]);
  3579        }
  3580      }
  3581  
  3582      params.push(1.0);
  3583      params.push(0.0);
  3584      var minDistanceSquared = -1;
  3585      var curX, curY, distSquared;
  3586  
  3587      for (var i = 0; i < params.length; i++) {
  3588        curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
  3589        curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
  3590        distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
  3591  
  3592        if (minDistanceSquared >= 0) {
  3593          if (distSquared < minDistanceSquared) {
  3594            minDistanceSquared = distSquared;
  3595          }
  3596        } else {
  3597          minDistanceSquared = distSquared;
  3598        }
  3599      }
  3600  
  3601      return minDistanceSquared;
  3602    };
  3603    var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
  3604      var offset = [x - x1, y - y1];
  3605      var line = [x2 - x1, y2 - y1];
  3606      var lineSq = line[0] * line[0] + line[1] * line[1];
  3607      var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
  3608      var dotProduct = offset[0] * line[0] + offset[1] * line[1];
  3609      var adjSq = dotProduct * dotProduct / lineSq;
  3610  
  3611      if (dotProduct < 0) {
  3612        return hypSq;
  3613      }
  3614  
  3615      if (adjSq > lineSq) {
  3616        return (x - x2) * (x - x2) + (y - y2) * (y - y2);
  3617      }
  3618  
  3619      return hypSq - adjSq;
  3620    };
  3621    var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
  3622      var x1, y1, x2, y2;
  3623      var y3; // Intersect with vertical line through (x, y)
  3624  
  3625      var up = 0; // let down = 0;
  3626  
  3627      for (var i = 0; i < points.length / 2; i++) {
  3628        x1 = points[i * 2];
  3629        y1 = points[i * 2 + 1];
  3630  
  3631        if (i + 1 < points.length / 2) {
  3632          x2 = points[(i + 1) * 2];
  3633          y2 = points[(i + 1) * 2 + 1];
  3634        } else {
  3635          x2 = points[(i + 1 - points.length / 2) * 2];
  3636          y2 = points[(i + 1 - points.length / 2) * 2 + 1];
  3637        }
  3638  
  3639        if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
  3640          y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
  3641  
  3642          if (y3 > y) {
  3643            up++;
  3644          } // if( y3 < y ){
  3645          // down++;
  3646          // }
  3647  
  3648        } else {
  3649          continue;
  3650        }
  3651      }
  3652  
  3653      if (up % 2 === 0) {
  3654        return false;
  3655      } else {
  3656        return true;
  3657      }
  3658    };
  3659    var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
  3660      var transformedPoints = new Array(basePoints.length); // Gives negative angle
  3661  
  3662      var angle;
  3663  
  3664      if (direction[0] != null) {
  3665        angle = Math.atan(direction[1] / direction[0]);
  3666  
  3667        if (direction[0] < 0) {
  3668          angle = angle + Math.PI / 2;
  3669        } else {
  3670          angle = -angle - Math.PI / 2;
  3671        }
  3672      } else {
  3673        angle = direction;
  3674      }
  3675  
  3676      var cos = Math.cos(-angle);
  3677      var sin = Math.sin(-angle); //    console.log("base: " + basePoints);
  3678  
  3679      for (var i = 0; i < transformedPoints.length / 2; i++) {
  3680        transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
  3681        transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
  3682        transformedPoints[i * 2] += centerX;
  3683        transformedPoints[i * 2 + 1] += centerY;
  3684      }
  3685  
  3686      var points;
  3687  
  3688      if (padding > 0) {
  3689        var expandedLineSet = expandPolygon(transformedPoints, -padding);
  3690        points = joinLines(expandedLineSet);
  3691      } else {
  3692        points = transformedPoints;
  3693      }
  3694  
  3695      return pointInsidePolygonPoints(x, y, points);
  3696    };
  3697    var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
  3698      var cutPolygonPoints = new Array(basePoints.length);
  3699      var halfW = width / 2;
  3700      var halfH = height / 2;
  3701      var cornerRadius = getRoundPolygonRadius(width, height);
  3702      var squaredCornerRadius = cornerRadius * cornerRadius;
  3703  
  3704      for (var i = 0; i < basePoints.length / 4; i++) {
  3705        var sourceUv = void 0,
  3706            destUv = void 0;
  3707  
  3708        if (i === 0) {
  3709          sourceUv = basePoints.length - 2;
  3710        } else {
  3711          sourceUv = i * 4 - 2;
  3712        }
  3713  
  3714        destUv = i * 4 + 2;
  3715        var px = centerX + halfW * basePoints[i * 4];
  3716        var py = centerY + halfH * basePoints[i * 4 + 1];
  3717        var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
  3718        var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
  3719        var cp0x = px - offset * basePoints[sourceUv];
  3720        var cp0y = py - offset * basePoints[sourceUv + 1];
  3721        var cp1x = px + offset * basePoints[destUv];
  3722        var cp1y = py + offset * basePoints[destUv + 1];
  3723        cutPolygonPoints[i * 4] = cp0x;
  3724        cutPolygonPoints[i * 4 + 1] = cp0y;
  3725        cutPolygonPoints[i * 4 + 2] = cp1x;
  3726        cutPolygonPoints[i * 4 + 3] = cp1y;
  3727        var orthx = basePoints[sourceUv + 1];
  3728        var orthy = -basePoints[sourceUv];
  3729        var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
  3730  
  3731        if (cosAlpha < 0) {
  3732          orthx *= -1;
  3733          orthy *= -1;
  3734        }
  3735  
  3736        var cx = cp0x + orthx * cornerRadius;
  3737        var cy = cp0y + orthy * cornerRadius;
  3738        var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
  3739  
  3740        if (squaredDistance <= squaredCornerRadius) {
  3741          return true;
  3742        }
  3743      }
  3744  
  3745      return pointInsidePolygonPoints(x, y, cutPolygonPoints);
  3746    };
  3747    var joinLines = function joinLines(lineSet) {
  3748      var vertices = new Array(lineSet.length / 2);
  3749      var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
  3750      var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
  3751  
  3752      for (var i = 0; i < lineSet.length / 4; i++) {
  3753        currentLineStartX = lineSet[i * 4];
  3754        currentLineStartY = lineSet[i * 4 + 1];
  3755        currentLineEndX = lineSet[i * 4 + 2];
  3756        currentLineEndY = lineSet[i * 4 + 3];
  3757  
  3758        if (i < lineSet.length / 4 - 1) {
  3759          nextLineStartX = lineSet[(i + 1) * 4];
  3760          nextLineStartY = lineSet[(i + 1) * 4 + 1];
  3761          nextLineEndX = lineSet[(i + 1) * 4 + 2];
  3762          nextLineEndY = lineSet[(i + 1) * 4 + 3];
  3763        } else {
  3764          nextLineStartX = lineSet[0];
  3765          nextLineStartY = lineSet[1];
  3766          nextLineEndX = lineSet[2];
  3767          nextLineEndY = lineSet[3];
  3768        }
  3769  
  3770        var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
  3771        vertices[i * 2] = intersection[0];
  3772        vertices[i * 2 + 1] = intersection[1];
  3773      }
  3774  
  3775      return vertices;
  3776    };
  3777    var expandPolygon = function expandPolygon(points, pad) {
  3778      var expandedLineSet = new Array(points.length * 2);
  3779      var currentPointX, currentPointY, nextPointX, nextPointY;
  3780  
  3781      for (var i = 0; i < points.length / 2; i++) {
  3782        currentPointX = points[i * 2];
  3783        currentPointY = points[i * 2 + 1];
  3784  
  3785        if (i < points.length / 2 - 1) {
  3786          nextPointX = points[(i + 1) * 2];
  3787          nextPointY = points[(i + 1) * 2 + 1];
  3788        } else {
  3789          nextPointX = points[0];
  3790          nextPointY = points[1];
  3791        } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
  3792        // Assume CCW polygon winding
  3793  
  3794  
  3795        var offsetX = nextPointY - currentPointY;
  3796        var offsetY = -(nextPointX - currentPointX); // Normalize
  3797  
  3798        var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
  3799        var normalizedOffsetX = offsetX / offsetLength;
  3800        var normalizedOffsetY = offsetY / offsetLength;
  3801        expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
  3802        expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
  3803        expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
  3804        expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
  3805      }
  3806  
  3807      return expandedLineSet;
  3808    };
  3809    var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
  3810      var dispX = centerX - x;
  3811      var dispY = centerY - y;
  3812      dispX /= ellipseWradius;
  3813      dispY /= ellipseHradius;
  3814      var len = Math.sqrt(dispX * dispX + dispY * dispY);
  3815      var newLength = len - 1;
  3816  
  3817      if (newLength < 0) {
  3818        return [];
  3819      }
  3820  
  3821      var lenProportion = newLength / len;
  3822      return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
  3823    };
  3824    var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
  3825      x -= centerX;
  3826      y -= centerY;
  3827      x /= width / 2 + padding;
  3828      y /= height / 2 + padding;
  3829      return x * x + y * y <= 1;
  3830    }; // Returns intersections of increasing distance from line's start point
  3831  
  3832    var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
  3833      // Calculate d, direction vector of line
  3834      var d = [x2 - x1, y2 - y1]; // Direction vector of line
  3835  
  3836      var f = [x1 - centerX, y1 - centerY];
  3837      var a = d[0] * d[0] + d[1] * d[1];
  3838      var b = 2 * (f[0] * d[0] + f[1] * d[1]);
  3839      var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
  3840      var discriminant = b * b - 4 * a * c;
  3841  
  3842      if (discriminant < 0) {
  3843        return [];
  3844      }
  3845  
  3846      var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
  3847      var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
  3848      var tMin = Math.min(t1, t2);
  3849      var tMax = Math.max(t1, t2);
  3850      var inRangeParams = [];
  3851  
  3852      if (tMin >= 0 && tMin <= 1) {
  3853        inRangeParams.push(tMin);
  3854      }
  3855  
  3856      if (tMax >= 0 && tMax <= 1) {
  3857        inRangeParams.push(tMax);
  3858      }
  3859  
  3860      if (inRangeParams.length === 0) {
  3861        return [];
  3862      }
  3863  
  3864      var nearIntersectionX = inRangeParams[0] * d[0] + x1;
  3865      var nearIntersectionY = inRangeParams[0] * d[1] + y1;
  3866  
  3867      if (inRangeParams.length > 1) {
  3868        if (inRangeParams[0] == inRangeParams[1]) {
  3869          return [nearIntersectionX, nearIntersectionY];
  3870        } else {
  3871          var farIntersectionX = inRangeParams[1] * d[0] + x1;
  3872          var farIntersectionY = inRangeParams[1] * d[1] + y1;
  3873          return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
  3874        }
  3875      } else {
  3876        return [nearIntersectionX, nearIntersectionY];
  3877      }
  3878    };
  3879    var midOfThree = function midOfThree(a, b, c) {
  3880      if (b <= a && a <= c || c <= a && a <= b) {
  3881        return a;
  3882      } else if (a <= b && b <= c || c <= b && b <= a) {
  3883        return b;
  3884      } else {
  3885        return c;
  3886      }
  3887    }; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
  3888  
  3889    var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
  3890      var dx13 = x1 - x3;
  3891      var dx21 = x2 - x1;
  3892      var dx43 = x4 - x3;
  3893      var dy13 = y1 - y3;
  3894      var dy21 = y2 - y1;
  3895      var dy43 = y4 - y3;
  3896      var ua_t = dx43 * dy13 - dy43 * dx13;
  3897      var ub_t = dx21 * dy13 - dy21 * dx13;
  3898      var u_b = dy43 * dx21 - dx43 * dy21;
  3899  
  3900      if (u_b !== 0) {
  3901        var ua = ua_t / u_b;
  3902        var ub = ub_t / u_b;
  3903        var flptThreshold = 0.001;
  3904  
  3905        var _min = 0 - flptThreshold;
  3906  
  3907        var _max = 1 + flptThreshold;
  3908  
  3909        if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
  3910          return [x1 + ua * dx21, y1 + ua * dy21];
  3911        } else {
  3912          if (!infiniteLines) {
  3913            return [];
  3914          } else {
  3915            return [x1 + ua * dx21, y1 + ua * dy21];
  3916          }
  3917        }
  3918      } else {
  3919        if (ua_t === 0 || ub_t === 0) {
  3920          // Parallel, coincident lines. Check if overlap
  3921          // Check endpoint of second line
  3922          if (midOfThree(x1, x2, x4) === x4) {
  3923            return [x4, y4];
  3924          } // Check start point of second line
  3925  
  3926  
  3927          if (midOfThree(x1, x2, x3) === x3) {
  3928            return [x3, y3];
  3929          } // Endpoint of first line
  3930  
  3931  
  3932          if (midOfThree(x3, x4, x2) === x2) {
  3933            return [x2, y2];
  3934          }
  3935  
  3936          return [];
  3937        } else {
  3938          // Parallel, non-coincident
  3939          return [];
  3940        }
  3941      }
  3942    }; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
  3943    // intersect a node polygon (pts transformed)
  3944    //
  3945    // math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
  3946    // intersect the points (no transform)
  3947  
  3948    var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
  3949      var intersections = [];
  3950      var intersection;
  3951      var transformedPoints = new Array(basePoints.length);
  3952      var doTransform = true;
  3953  
  3954      if (width == null) {
  3955        doTransform = false;
  3956      }
  3957  
  3958      var points;
  3959  
  3960      if (doTransform) {
  3961        for (var i = 0; i < transformedPoints.length / 2; i++) {
  3962          transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
  3963          transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
  3964        }
  3965  
  3966        if (padding > 0) {
  3967          var expandedLineSet = expandPolygon(transformedPoints, -padding);
  3968          points = joinLines(expandedLineSet);
  3969        } else {
  3970          points = transformedPoints;
  3971        }
  3972      } else {
  3973        points = basePoints;
  3974      }
  3975  
  3976      var currentX, currentY, nextX, nextY;
  3977  
  3978      for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
  3979        currentX = points[_i2 * 2];
  3980        currentY = points[_i2 * 2 + 1];
  3981  
  3982        if (_i2 < points.length / 2 - 1) {
  3983          nextX = points[(_i2 + 1) * 2];
  3984          nextY = points[(_i2 + 1) * 2 + 1];
  3985        } else {
  3986          nextX = points[0];
  3987          nextY = points[1];
  3988        }
  3989  
  3990        intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
  3991  
  3992        if (intersection.length !== 0) {
  3993          intersections.push(intersection[0], intersection[1]);
  3994        }
  3995      }
  3996  
  3997      return intersections;
  3998    };
  3999    var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
  4000      var intersections = [];
  4001      var intersection;
  4002      var lines = new Array(basePoints.length);
  4003      var halfW = width / 2;
  4004      var halfH = height / 2;
  4005      var cornerRadius = getRoundPolygonRadius(width, height);
  4006  
  4007      for (var i = 0; i < basePoints.length / 4; i++) {
  4008        var sourceUv = void 0,
  4009            destUv = void 0;
  4010  
  4011        if (i === 0) {
  4012          sourceUv = basePoints.length - 2;
  4013        } else {
  4014          sourceUv = i * 4 - 2;
  4015        }
  4016  
  4017        destUv = i * 4 + 2;
  4018        var px = centerX + halfW * basePoints[i * 4];
  4019        var py = centerY + halfH * basePoints[i * 4 + 1];
  4020        var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
  4021        var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
  4022        var cp0x = px - offset * basePoints[sourceUv];
  4023        var cp0y = py - offset * basePoints[sourceUv + 1];
  4024        var cp1x = px + offset * basePoints[destUv];
  4025        var cp1y = py + offset * basePoints[destUv + 1];
  4026  
  4027        if (i === 0) {
  4028          lines[basePoints.length - 2] = cp0x;
  4029          lines[basePoints.length - 1] = cp0y;
  4030        } else {
  4031          lines[i * 4 - 2] = cp0x;
  4032          lines[i * 4 - 1] = cp0y;
  4033        }
  4034  
  4035        lines[i * 4] = cp1x;
  4036        lines[i * 4 + 1] = cp1y;
  4037        var orthx = basePoints[sourceUv + 1];
  4038        var orthy = -basePoints[sourceUv];
  4039        var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
  4040  
  4041        if (cosAlpha < 0) {
  4042          orthx *= -1;
  4043          orthy *= -1;
  4044        }
  4045  
  4046        var cx = cp0x + orthx * cornerRadius;
  4047        var cy = cp0y + orthy * cornerRadius;
  4048        intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
  4049  
  4050        if (intersection.length !== 0) {
  4051          intersections.push(intersection[0], intersection[1]);
  4052        }
  4053      }
  4054  
  4055      for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
  4056        intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
  4057  
  4058        if (intersection.length !== 0) {
  4059          intersections.push(intersection[0], intersection[1]);
  4060        }
  4061      }
  4062  
  4063      if (intersections.length > 2) {
  4064        var lowestIntersection = [intersections[0], intersections[1]];
  4065        var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
  4066  
  4067        for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
  4068          var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
  4069  
  4070          if (squaredDistance <= lowestSquaredDistance) {
  4071            lowestIntersection[0] = intersections[_i4 * 2];
  4072            lowestIntersection[1] = intersections[_i4 * 2 + 1];
  4073            lowestSquaredDistance = squaredDistance;
  4074          }
  4075        }
  4076  
  4077        return lowestIntersection;
  4078      }
  4079  
  4080      return intersections;
  4081    };
  4082    var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
  4083      var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
  4084      var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
  4085      var lenRatio = (length - amount) / length;
  4086  
  4087      if (lenRatio < 0) {
  4088        lenRatio = 0.00001;
  4089      }
  4090  
  4091      return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
  4092    };
  4093    var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
  4094      var points = generateUnitNgonPoints(sides, rotationRadians);
  4095      points = fitPolygonToSquare(points);
  4096      return points;
  4097    };
  4098    var fitPolygonToSquare = function fitPolygonToSquare(points) {
  4099      var x, y;
  4100      var sides = points.length / 2;
  4101      var minX = Infinity,
  4102          minY = Infinity,
  4103          maxX = -Infinity,
  4104          maxY = -Infinity;
  4105  
  4106      for (var i = 0; i < sides; i++) {
  4107        x = points[2 * i];
  4108        y = points[2 * i + 1];
  4109        minX = Math.min(minX, x);
  4110        maxX = Math.max(maxX, x);
  4111        minY = Math.min(minY, y);
  4112        maxY = Math.max(maxY, y);
  4113      } // stretch factors
  4114  
  4115  
  4116      var sx = 2 / (maxX - minX);
  4117      var sy = 2 / (maxY - minY);
  4118  
  4119      for (var _i5 = 0; _i5 < sides; _i5++) {
  4120        x = points[2 * _i5] = points[2 * _i5] * sx;
  4121        y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
  4122        minX = Math.min(minX, x);
  4123        maxX = Math.max(maxX, x);
  4124        minY = Math.min(minY, y);
  4125        maxY = Math.max(maxY, y);
  4126      }
  4127  
  4128      if (minY < -1) {
  4129        for (var _i6 = 0; _i6 < sides; _i6++) {
  4130          y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
  4131        }
  4132      }
  4133  
  4134      return points;
  4135    };
  4136    var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
  4137      var increment = 1.0 / sides * 2 * Math.PI;
  4138      var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
  4139      startAngle += rotationRadians;
  4140      var points = new Array(sides * 2);
  4141      var currentAngle;
  4142  
  4143      for (var i = 0; i < sides; i++) {
  4144        currentAngle = i * increment + startAngle;
  4145        points[2 * i] = Math.cos(currentAngle); // x
  4146  
  4147        points[2 * i + 1] = Math.sin(-currentAngle); // y
  4148      }
  4149  
  4150      return points;
  4151    }; // Set the default radius, unless half of width or height is smaller than default
  4152  
  4153    var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
  4154      return Math.min(width / 4, height / 4, 8);
  4155    }; // Set the default radius
  4156  
  4157    var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
  4158      return Math.min(width / 10, height / 10, 8);
  4159    };
  4160    var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
  4161      return 8;
  4162    };
  4163    var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
  4164      return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
  4165    }; // get curve width, height, and control point position offsets as a percentage of node height / width
  4166  
  4167    var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
  4168      return {
  4169        heightOffset: Math.min(15, 0.05 * height),
  4170        widthOffset: Math.min(100, 0.25 * width),
  4171        ctrlPtOffsetPct: 0.05
  4172      };
  4173    };
  4174  
  4175    var pageRankDefaults = defaults({
  4176      dampingFactor: 0.8,
  4177      precision: 0.000001,
  4178      iterations: 200,
  4179      weight: function weight(edge) {
  4180        return 1;
  4181      }
  4182    });
  4183    var elesfn$7 = {
  4184      pageRank: function pageRank(options) {
  4185        var _pageRankDefaults = pageRankDefaults(options),
  4186            dampingFactor = _pageRankDefaults.dampingFactor,
  4187            precision = _pageRankDefaults.precision,
  4188            iterations = _pageRankDefaults.iterations,
  4189            weight = _pageRankDefaults.weight;
  4190  
  4191        var cy = this._private.cy;
  4192  
  4193        var _this$byGroup = this.byGroup(),
  4194            nodes = _this$byGroup.nodes,
  4195            edges = _this$byGroup.edges;
  4196  
  4197        var numNodes = nodes.length;
  4198        var numNodesSqd = numNodes * numNodes;
  4199        var numEdges = edges.length; // Construct transposed adjacency matrix
  4200        // First lets have a zeroed matrix of the right size
  4201        // We'll also keep track of the sum of each column
  4202  
  4203        var matrix = new Array(numNodesSqd);
  4204        var columnSum = new Array(numNodes);
  4205        var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
  4206  
  4207        for (var i = 0; i < numNodes; i++) {
  4208          for (var j = 0; j < numNodes; j++) {
  4209            var n = i * numNodes + j;
  4210            matrix[n] = 0;
  4211          }
  4212  
  4213          columnSum[i] = 0;
  4214        } // Now, process edges
  4215  
  4216  
  4217        for (var _i = 0; _i < numEdges; _i++) {
  4218          var edge = edges[_i];
  4219          var srcId = edge.data('source');
  4220          var tgtId = edge.data('target'); // Don't include loops in the matrix
  4221  
  4222          if (srcId === tgtId) {
  4223            continue;
  4224          }
  4225  
  4226          var s = nodes.indexOfId(srcId);
  4227          var t = nodes.indexOfId(tgtId);
  4228          var w = weight(edge);
  4229  
  4230          var _n = t * numNodes + s; // Update matrix
  4231  
  4232  
  4233          matrix[_n] += w; // Update column sum
  4234  
  4235          columnSum[s] += w;
  4236        } // Add additional probability based on damping factor
  4237        // Also, take into account columns that have sum = 0
  4238  
  4239  
  4240        var p = 1.0 / numNodes + additionalProb; // Shorthand
  4241        // Traverse matrix, column by column
  4242  
  4243        for (var _j = 0; _j < numNodes; _j++) {
  4244          if (columnSum[_j] === 0) {
  4245            // No 'links' out from node jth, assume equal probability for each possible node
  4246            for (var _i2 = 0; _i2 < numNodes; _i2++) {
  4247              var _n2 = _i2 * numNodes + _j;
  4248  
  4249              matrix[_n2] = p;
  4250            }
  4251          } else {
  4252            // Node jth has outgoing link, compute normalized probabilities
  4253            for (var _i3 = 0; _i3 < numNodes; _i3++) {
  4254              var _n3 = _i3 * numNodes + _j;
  4255  
  4256              matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
  4257            }
  4258          }
  4259        } // Compute dominant eigenvector using power method
  4260  
  4261  
  4262        var eigenvector = new Array(numNodes);
  4263        var temp = new Array(numNodes);
  4264        var previous; // Start with a vector of all 1's
  4265        // Also, initialize a null vector which will be used as shorthand
  4266  
  4267        for (var _i4 = 0; _i4 < numNodes; _i4++) {
  4268          eigenvector[_i4] = 1;
  4269        }
  4270  
  4271        for (var iter = 0; iter < iterations; iter++) {
  4272          // Temp array with all 0's
  4273          for (var _i5 = 0; _i5 < numNodes; _i5++) {
  4274            temp[_i5] = 0;
  4275          } // Multiply matrix with previous result
  4276  
  4277  
  4278          for (var _i6 = 0; _i6 < numNodes; _i6++) {
  4279            for (var _j2 = 0; _j2 < numNodes; _j2++) {
  4280              var _n4 = _i6 * numNodes + _j2;
  4281  
  4282              temp[_i6] += matrix[_n4] * eigenvector[_j2];
  4283            }
  4284          }
  4285  
  4286          inPlaceSumNormalize(temp);
  4287          previous = eigenvector;
  4288          eigenvector = temp;
  4289          temp = previous;
  4290          var diff = 0; // Compute difference (squared module) of both vectors
  4291  
  4292          for (var _i7 = 0; _i7 < numNodes; _i7++) {
  4293            var delta = previous[_i7] - eigenvector[_i7];
  4294            diff += delta * delta;
  4295          } // If difference is less than the desired threshold, stop iterating
  4296  
  4297  
  4298          if (diff < precision) {
  4299            break;
  4300          }
  4301        } // Construct result
  4302  
  4303  
  4304        var res = {
  4305          rank: function rank(node) {
  4306            node = cy.collection(node)[0];
  4307            return eigenvector[nodes.indexOf(node)];
  4308          }
  4309        };
  4310        return res;
  4311      } // pageRank
  4312  
  4313    }; // elesfn
  4314  
  4315    var defaults$1 = defaults({
  4316      root: null,
  4317      weight: function weight(edge) {
  4318        return 1;
  4319      },
  4320      directed: false,
  4321      alpha: 0
  4322    });
  4323    var elesfn$8 = {
  4324      degreeCentralityNormalized: function degreeCentralityNormalized(options) {
  4325        options = defaults$1(options);
  4326        var cy = this.cy();
  4327        var nodes = this.nodes();
  4328        var numNodes = nodes.length;
  4329  
  4330        if (!options.directed) {
  4331          var degrees = {};
  4332          var maxDegree = 0;
  4333  
  4334          for (var i = 0; i < numNodes; i++) {
  4335            var node = nodes[i]; // add current node to the current options object and call degreeCentrality
  4336  
  4337            options.root = node;
  4338            var currDegree = this.degreeCentrality(options);
  4339  
  4340            if (maxDegree < currDegree.degree) {
  4341              maxDegree = currDegree.degree;
  4342            }
  4343  
  4344            degrees[node.id()] = currDegree.degree;
  4345          }
  4346  
  4347          return {
  4348            degree: function degree(node) {
  4349              if (maxDegree === 0) {
  4350                return 0;
  4351              }
  4352  
  4353              if (string(node)) {
  4354                // from is a selector string
  4355                node = cy.filter(node);
  4356              }
  4357  
  4358              return degrees[node.id()] / maxDegree;
  4359            }
  4360          };
  4361        } else {
  4362          var indegrees = {};
  4363          var outdegrees = {};
  4364          var maxIndegree = 0;
  4365          var maxOutdegree = 0;
  4366  
  4367          for (var _i = 0; _i < numNodes; _i++) {
  4368            var _node = nodes[_i];
  4369  
  4370            var id = _node.id(); // add current node to the current options object and call degreeCentrality
  4371  
  4372  
  4373            options.root = _node;
  4374  
  4375            var _currDegree = this.degreeCentrality(options);
  4376  
  4377            if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
  4378            if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
  4379            indegrees[id] = _currDegree.indegree;
  4380            outdegrees[id] = _currDegree.outdegree;
  4381          }
  4382  
  4383          return {
  4384            indegree: function indegree(node) {
  4385              if (maxIndegree == 0) {
  4386                return 0;
  4387              }
  4388  
  4389              if (string(node)) {
  4390                // from is a selector string
  4391                node = cy.filter(node);
  4392              }
  4393  
  4394              return indegrees[node.id()] / maxIndegree;
  4395            },
  4396            outdegree: function outdegree(node) {
  4397              if (maxOutdegree === 0) {
  4398                return 0;
  4399              }
  4400  
  4401              if (string(node)) {
  4402                // from is a selector string
  4403                node = cy.filter(node);
  4404              }
  4405  
  4406              return outdegrees[node.id()] / maxOutdegree;
  4407            }
  4408          };
  4409        }
  4410      },
  4411      // degreeCentralityNormalized
  4412      // Implemented from the algorithm in Opsahl's paper
  4413      // "Node centrality in weighted networks: Generalizing degree and shortest paths"
  4414      // check the heading 2 "Degree"
  4415      degreeCentrality: function degreeCentrality(options) {
  4416        options = defaults$1(options);
  4417        var cy = this.cy();
  4418        var callingEles = this;
  4419        var _options = options,
  4420            root = _options.root,
  4421            weight = _options.weight,
  4422            directed = _options.directed,
  4423            alpha = _options.alpha;
  4424        root = cy.collection(root)[0];
  4425  
  4426        if (!directed) {
  4427          var connEdges = root.connectedEdges().intersection(callingEles);
  4428          var k = connEdges.length;
  4429          var s = 0; // Now, sum edge weights
  4430  
  4431          for (var i = 0; i < connEdges.length; i++) {
  4432            s += weight(connEdges[i]);
  4433          }
  4434  
  4435          return {
  4436            degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
  4437          };
  4438        } else {
  4439          var edges = root.connectedEdges();
  4440          var incoming = edges.filter(function (edge) {
  4441            return edge.target().same(root) && callingEles.has(edge);
  4442          });
  4443          var outgoing = edges.filter(function (edge) {
  4444            return edge.source().same(root) && callingEles.has(edge);
  4445          });
  4446          var k_in = incoming.length;
  4447          var k_out = outgoing.length;
  4448          var s_in = 0;
  4449          var s_out = 0; // Now, sum incoming edge weights
  4450  
  4451          for (var _i2 = 0; _i2 < incoming.length; _i2++) {
  4452            s_in += weight(incoming[_i2]);
  4453          } // Now, sum outgoing edge weights
  4454  
  4455  
  4456          for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
  4457            s_out += weight(outgoing[_i3]);
  4458          }
  4459  
  4460          return {
  4461            indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
  4462            outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
  4463          };
  4464        }
  4465      } // degreeCentrality
  4466  
  4467    }; // elesfn
  4468    // nice, short mathemathical alias
  4469  
  4470    elesfn$8.dc = elesfn$8.degreeCentrality;
  4471    elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
  4472  
  4473    var defaults$2 = defaults({
  4474      harmonic: true,
  4475      weight: function weight() {
  4476        return 1;
  4477      },
  4478      directed: false,
  4479      root: null
  4480    });
  4481    var elesfn$9 = {
  4482      closenessCentralityNormalized: function closenessCentralityNormalized(options) {
  4483        var _defaults = defaults$2(options),
  4484            harmonic = _defaults.harmonic,
  4485            weight = _defaults.weight,
  4486            directed = _defaults.directed;
  4487  
  4488        var cy = this.cy();
  4489        var closenesses = {};
  4490        var maxCloseness = 0;
  4491        var nodes = this.nodes();
  4492        var fw = this.floydWarshall({
  4493          weight: weight,
  4494          directed: directed
  4495        }); // Compute closeness for every node and find the maximum closeness
  4496  
  4497        for (var i = 0; i < nodes.length; i++) {
  4498          var currCloseness = 0;
  4499          var node_i = nodes[i];
  4500  
  4501          for (var j = 0; j < nodes.length; j++) {
  4502            if (i !== j) {
  4503              var d = fw.distance(node_i, nodes[j]);
  4504  
  4505              if (harmonic) {
  4506                currCloseness += 1 / d;
  4507              } else {
  4508                currCloseness += d;
  4509              }
  4510            }
  4511          }
  4512  
  4513          if (!harmonic) {
  4514            currCloseness = 1 / currCloseness;
  4515          }
  4516  
  4517          if (maxCloseness < currCloseness) {
  4518            maxCloseness = currCloseness;
  4519          }
  4520  
  4521          closenesses[node_i.id()] = currCloseness;
  4522        }
  4523  
  4524        return {
  4525          closeness: function closeness(node) {
  4526            if (maxCloseness == 0) {
  4527              return 0;
  4528            }
  4529  
  4530            if (string(node)) {
  4531              // from is a selector string
  4532              node = cy.filter(node)[0].id();
  4533            } else {
  4534              // from is a node
  4535              node = node.id();
  4536            }
  4537  
  4538            return closenesses[node] / maxCloseness;
  4539          }
  4540        };
  4541      },
  4542      // Implemented from pseudocode from wikipedia
  4543      closenessCentrality: function closenessCentrality(options) {
  4544        var _defaults2 = defaults$2(options),
  4545            root = _defaults2.root,
  4546            weight = _defaults2.weight,
  4547            directed = _defaults2.directed,
  4548            harmonic = _defaults2.harmonic;
  4549  
  4550        root = this.filter(root)[0]; // we need distance from this node to every other node
  4551  
  4552        var dijkstra = this.dijkstra({
  4553          root: root,
  4554          weight: weight,
  4555          directed: directed
  4556        });
  4557        var totalDistance = 0;
  4558        var nodes = this.nodes();
  4559  
  4560        for (var i = 0; i < nodes.length; i++) {
  4561          var n = nodes[i];
  4562  
  4563          if (!n.same(root)) {
  4564            var d = dijkstra.distanceTo(n);
  4565  
  4566            if (harmonic) {
  4567              totalDistance += 1 / d;
  4568            } else {
  4569              totalDistance += d;
  4570            }
  4571          }
  4572        }
  4573  
  4574        return harmonic ? totalDistance : 1 / totalDistance;
  4575      } // closenessCentrality
  4576  
  4577    }; // elesfn
  4578    // nice, short mathemathical alias
  4579  
  4580    elesfn$9.cc = elesfn$9.closenessCentrality;
  4581    elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
  4582  
  4583    var defaults$3 = defaults({
  4584      weight: null,
  4585      directed: false
  4586    });
  4587    var elesfn$a = {
  4588      // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
  4589      betweennessCentrality: function betweennessCentrality(options) {
  4590        var _defaults = defaults$3(options),
  4591            directed = _defaults.directed,
  4592            weight = _defaults.weight;
  4593  
  4594        var weighted = weight != null;
  4595        var cy = this.cy(); // starting
  4596  
  4597        var V = this.nodes();
  4598        var A = {};
  4599        var _C = {};
  4600        var max = 0;
  4601        var C = {
  4602          set: function set(key, val) {
  4603            _C[key] = val;
  4604  
  4605            if (val > max) {
  4606              max = val;
  4607            }
  4608          },
  4609          get: function get(key) {
  4610            return _C[key];
  4611          }
  4612        }; // A contains the neighborhoods of every node
  4613  
  4614        for (var i = 0; i < V.length; i++) {
  4615          var v = V[i];
  4616          var vid = v.id();
  4617  
  4618          if (directed) {
  4619            A[vid] = v.outgoers().nodes(); // get outgoers of every node
  4620          } else {
  4621            A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
  4622          }
  4623  
  4624          C.set(vid, 0);
  4625        }
  4626  
  4627        var _loop = function _loop(s) {
  4628          var sid = V[s].id();
  4629          var S = []; // stack
  4630  
  4631          var P = {};
  4632          var g = {};
  4633          var d = {};
  4634          var Q = new heap$1(function (a, b) {
  4635            return d[a] - d[b];
  4636          }); // queue
  4637          // init dictionaries
  4638  
  4639          for (var _i = 0; _i < V.length; _i++) {
  4640            var _vid = V[_i].id();
  4641  
  4642            P[_vid] = [];
  4643            g[_vid] = 0;
  4644            d[_vid] = Infinity;
  4645          }
  4646  
  4647          g[sid] = 1; // sigma
  4648  
  4649          d[sid] = 0; // distance to s
  4650  
  4651          Q.push(sid);
  4652  
  4653          while (!Q.empty()) {
  4654            var _v = Q.pop();
  4655  
  4656            S.push(_v);
  4657  
  4658            if (weighted) {
  4659              for (var j = 0; j < A[_v].length; j++) {
  4660                var w = A[_v][j];
  4661                var vEle = cy.getElementById(_v);
  4662                var edge = void 0;
  4663  
  4664                if (vEle.edgesTo(w).length > 0) {
  4665                  edge = vEle.edgesTo(w)[0];
  4666                } else {
  4667                  edge = w.edgesTo(vEle)[0];
  4668                }
  4669  
  4670                var edgeWeight = weight(edge);
  4671                w = w.id();
  4672  
  4673                if (d[w] > d[_v] + edgeWeight) {
  4674                  d[w] = d[_v] + edgeWeight;
  4675  
  4676                  if (Q.nodes.indexOf(w) < 0) {
  4677                    //if w is not in Q
  4678                    Q.push(w);
  4679                  } else {
  4680                    // update position if w is in Q
  4681                    Q.updateItem(w);
  4682                  }
  4683  
  4684                  g[w] = 0;
  4685                  P[w] = [];
  4686                }
  4687  
  4688                if (d[w] == d[_v] + edgeWeight) {
  4689                  g[w] = g[w] + g[_v];
  4690                  P[w].push(_v);
  4691                }
  4692              }
  4693            } else {
  4694              for (var _j = 0; _j < A[_v].length; _j++) {
  4695                var _w = A[_v][_j].id();
  4696  
  4697                if (d[_w] == Infinity) {
  4698                  Q.push(_w);
  4699                  d[_w] = d[_v] + 1;
  4700                }
  4701  
  4702                if (d[_w] == d[_v] + 1) {
  4703                  g[_w] = g[_w] + g[_v];
  4704  
  4705                  P[_w].push(_v);
  4706                }
  4707              }
  4708            }
  4709          }
  4710  
  4711          var e = {};
  4712  
  4713          for (var _i2 = 0; _i2 < V.length; _i2++) {
  4714            e[V[_i2].id()] = 0;
  4715          }
  4716  
  4717          while (S.length > 0) {
  4718            var _w2 = S.pop();
  4719  
  4720            for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
  4721              var _v2 = P[_w2][_j2];
  4722              e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
  4723  
  4724              if (_w2 != V[s].id()) {
  4725                C.set(_w2, C.get(_w2) + e[_w2]);
  4726              }
  4727            }
  4728          }
  4729        };
  4730  
  4731        for (var s = 0; s < V.length; s++) {
  4732          _loop(s);
  4733        }
  4734  
  4735        var ret = {
  4736          betweenness: function betweenness(node) {
  4737            var id = cy.collection(node).id();
  4738            return C.get(id);
  4739          },
  4740          betweennessNormalized: function betweennessNormalized(node) {
  4741            if (max == 0) {
  4742              return 0;
  4743            }
  4744  
  4745            var id = cy.collection(node).id();
  4746            return C.get(id) / max;
  4747          }
  4748        }; // alias
  4749  
  4750        ret.betweennessNormalised = ret.betweennessNormalized;
  4751        return ret;
  4752      } // betweennessCentrality
  4753  
  4754    }; // elesfn
  4755    // nice, short mathemathical alias
  4756  
  4757    elesfn$a.bc = elesfn$a.betweennessCentrality;
  4758  
  4759    // Implemented by Zoe Xi @zoexi for GSOC 2016
  4760    /* eslint-disable no-unused-vars */
  4761  
  4762    var defaults$4 = defaults({
  4763      expandFactor: 2,
  4764      // affects time of computation and cluster granularity to some extent: M * M
  4765      inflateFactor: 2,
  4766      // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
  4767      multFactor: 1,
  4768      // optional self loops for each node. Use a neutral value to improve cluster computations.
  4769      maxIterations: 20,
  4770      // maximum number of iterations of the MCL algorithm in a single run
  4771      attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
  4772      function (edge) {
  4773        return 1;
  4774      }]
  4775    });
  4776    /* eslint-enable */
  4777  
  4778    var setOptions = function setOptions(options) {
  4779      return defaults$4(options);
  4780    };
  4781    /* eslint-enable */
  4782  
  4783  
  4784    var getSimilarity = function getSimilarity(edge, attributes) {
  4785      var total = 0;
  4786  
  4787      for (var i = 0; i < attributes.length; i++) {
  4788        total += attributes[i](edge);
  4789      }
  4790  
  4791      return total;
  4792    };
  4793  
  4794    var addLoops = function addLoops(M, n, val) {
  4795      for (var i = 0; i < n; i++) {
  4796        M[i * n + i] = val;
  4797      }
  4798    };
  4799  
  4800    var normalize = function normalize(M, n) {
  4801      var sum;
  4802  
  4803      for (var col = 0; col < n; col++) {
  4804        sum = 0;
  4805  
  4806        for (var row = 0; row < n; row++) {
  4807          sum += M[row * n + col];
  4808        }
  4809  
  4810        for (var _row = 0; _row < n; _row++) {
  4811          M[_row * n + col] = M[_row * n + col] / sum;
  4812        }
  4813      }
  4814    }; // TODO: blocked matrix multiplication?
  4815  
  4816  
  4817    var mmult = function mmult(A, B, n) {
  4818      var C = new Array(n * n);
  4819  
  4820      for (var i = 0; i < n; i++) {
  4821        for (var j = 0; j < n; j++) {
  4822          C[i * n + j] = 0;
  4823        }
  4824  
  4825        for (var k = 0; k < n; k++) {
  4826          for (var _j = 0; _j < n; _j++) {
  4827            C[i * n + _j] += A[i * n + k] * B[k * n + _j];
  4828          }
  4829        }
  4830      }
  4831  
  4832      return C;
  4833    };
  4834  
  4835    var expand = function expand(M, n, expandFactor
  4836    /** power **/
  4837    ) {
  4838      var _M = M.slice(0);
  4839  
  4840      for (var p = 1; p < expandFactor; p++) {
  4841        M = mmult(M, _M, n);
  4842      }
  4843  
  4844      return M;
  4845    };
  4846  
  4847    var inflate = function inflate(M, n, inflateFactor
  4848    /** r **/
  4849    ) {
  4850      var _M = new Array(n * n); // M(i,j) ^ inflatePower
  4851  
  4852  
  4853      for (var i = 0; i < n * n; i++) {
  4854        _M[i] = Math.pow(M[i], inflateFactor);
  4855      }
  4856  
  4857      normalize(_M, n);
  4858      return _M;
  4859    };
  4860  
  4861    var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
  4862      // Check that both matrices have the same elements (i,j)
  4863      for (var i = 0; i < n2; i++) {
  4864        var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
  4865  
  4866        var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
  4867  
  4868        if (v1 !== v2) {
  4869          return false;
  4870        }
  4871      }
  4872  
  4873      return true;
  4874    };
  4875  
  4876    var assign = function assign(M, n, nodes, cy) {
  4877      var clusters = [];
  4878  
  4879      for (var i = 0; i < n; i++) {
  4880        var cluster = [];
  4881  
  4882        for (var j = 0; j < n; j++) {
  4883          // Row-wise attractors and elements that they attract belong in same cluster
  4884          if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
  4885            cluster.push(nodes[j]);
  4886          }
  4887        }
  4888  
  4889        if (cluster.length !== 0) {
  4890          clusters.push(cy.collection(cluster));
  4891        }
  4892      }
  4893  
  4894      return clusters;
  4895    };
  4896  
  4897    var isDuplicate = function isDuplicate(c1, c2) {
  4898      for (var i = 0; i < c1.length; i++) {
  4899        if (!c2[i] || c1[i].id() !== c2[i].id()) {
  4900          return false;
  4901        }
  4902      }
  4903  
  4904      return true;
  4905    };
  4906  
  4907    var removeDuplicates = function removeDuplicates(clusters) {
  4908      for (var i = 0; i < clusters.length; i++) {
  4909        for (var j = 0; j < clusters.length; j++) {
  4910          if (i != j && isDuplicate(clusters[i], clusters[j])) {
  4911            clusters.splice(j, 1);
  4912          }
  4913        }
  4914      }
  4915  
  4916      return clusters;
  4917    };
  4918  
  4919    var markovClustering = function markovClustering(options) {
  4920      var nodes = this.nodes();
  4921      var edges = this.edges();
  4922      var cy = this.cy(); // Set parameters of algorithm:
  4923  
  4924      var opts = setOptions(options); // Map each node to its position in node array
  4925  
  4926      var id2position = {};
  4927  
  4928      for (var i = 0; i < nodes.length; i++) {
  4929        id2position[nodes[i].id()] = i;
  4930      } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
  4931  
  4932  
  4933      var n = nodes.length,
  4934          n2 = n * n;
  4935  
  4936      var M = new Array(n2),
  4937          _M;
  4938  
  4939      for (var _i = 0; _i < n2; _i++) {
  4940        M[_i] = 0;
  4941      }
  4942  
  4943      for (var e = 0; e < edges.length; e++) {
  4944        var edge = edges[e];
  4945        var _i2 = id2position[edge.source().id()];
  4946        var j = id2position[edge.target().id()];
  4947        var sim = getSimilarity(edge, opts.attributes);
  4948        M[_i2 * n + j] += sim; // G should be symmetric and undirected
  4949  
  4950        M[j * n + _i2] += sim;
  4951      } // Begin Markov cluster algorithm
  4952      // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
  4953  
  4954  
  4955      addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
  4956  
  4957      normalize(M, n);
  4958      var isStillMoving = true;
  4959      var iterations = 0;
  4960  
  4961      while (isStillMoving && iterations < opts.maxIterations) {
  4962        isStillMoving = false; // Step 3:
  4963  
  4964        _M = expand(M, n, opts.expandFactor); // Step 4:
  4965  
  4966        M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
  4967  
  4968        if (!hasConverged(M, _M, n2, 4)) {
  4969          isStillMoving = true;
  4970        }
  4971  
  4972        iterations++;
  4973      } // Build clusters from matrix
  4974  
  4975  
  4976      var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
  4977  
  4978      clusters = removeDuplicates(clusters);
  4979      return clusters;
  4980    };
  4981  
  4982    var markovClustering$1 = {
  4983      markovClustering: markovClustering,
  4984      mcl: markovClustering
  4985    };
  4986  
  4987    // Common distance metrics for clustering algorithms
  4988  
  4989    var identity = function identity(x) {
  4990      return x;
  4991    };
  4992  
  4993    var absDiff = function absDiff(p, q) {
  4994      return Math.abs(q - p);
  4995    };
  4996  
  4997    var addAbsDiff = function addAbsDiff(total, p, q) {
  4998      return total + absDiff(p, q);
  4999    };
  5000  
  5001    var addSquaredDiff = function addSquaredDiff(total, p, q) {
  5002      return total + Math.pow(q - p, 2);
  5003    };
  5004  
  5005    var sqrt = function sqrt(x) {
  5006      return Math.sqrt(x);
  5007    };
  5008  
  5009    var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
  5010      return Math.max(currentMax, absDiff(p, q));
  5011    };
  5012  
  5013    var getDistance = function getDistance(length, getP, getQ, init, visit) {
  5014      var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
  5015      var ret = init;
  5016      var p, q;
  5017  
  5018      for (var dim = 0; dim < length; dim++) {
  5019        p = getP(dim);
  5020        q = getQ(dim);
  5021        ret = visit(ret, p, q);
  5022      }
  5023  
  5024      return post(ret);
  5025    };
  5026  
  5027    var distances = {
  5028      euclidean: function euclidean(length, getP, getQ) {
  5029        if (length >= 2) {
  5030          return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
  5031        } else {
  5032          // for single attr case, more efficient to avoid sqrt
  5033          return getDistance(length, getP, getQ, 0, addAbsDiff);
  5034        }
  5035      },
  5036      squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
  5037        return getDistance(length, getP, getQ, 0, addSquaredDiff);
  5038      },
  5039      manhattan: function manhattan(length, getP, getQ) {
  5040        return getDistance(length, getP, getQ, 0, addAbsDiff);
  5041      },
  5042      max: function max(length, getP, getQ) {
  5043        return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
  5044      }
  5045    }; // in case the user accidentally doesn't use camel case
  5046  
  5047    distances['squared-euclidean'] = distances['squaredEuclidean'];
  5048    distances['squaredeuclidean'] = distances['squaredEuclidean'];
  5049    function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
  5050      var impl;
  5051  
  5052      if (fn(method)) {
  5053        impl = method;
  5054      } else {
  5055        impl = distances[method] || distances.euclidean;
  5056      }
  5057  
  5058      if (length === 0 && fn(method)) {
  5059        return impl(nodeP, nodeQ);
  5060      } else {
  5061        return impl(length, getP, getQ, nodeP, nodeQ);
  5062      }
  5063    }
  5064  
  5065    var defaults$5 = defaults({
  5066      k: 2,
  5067      m: 2,
  5068      sensitivityThreshold: 0.0001,
  5069      distance: 'euclidean',
  5070      maxIterations: 10,
  5071      attributes: [],
  5072      testMode: false,
  5073      testCentroids: null
  5074    });
  5075  
  5076    var setOptions$1 = function setOptions(options) {
  5077      return defaults$5(options);
  5078    };
  5079    /* eslint-enable */
  5080  
  5081  
  5082    var getDist = function getDist(type, node, centroid, attributes, mode) {
  5083      var noNodeP = mode !== 'kMedoids';
  5084      var getP = noNodeP ? function (i) {
  5085        return centroid[i];
  5086      } : function (i) {
  5087        return attributes[i](centroid);
  5088      };
  5089  
  5090      var getQ = function getQ(i) {
  5091        return attributes[i](node);
  5092      };
  5093  
  5094      var nodeP = centroid;
  5095      var nodeQ = node;
  5096      return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
  5097    };
  5098  
  5099    var randomCentroids = function randomCentroids(nodes, k, attributes) {
  5100      var ndim = attributes.length;
  5101      var min = new Array(ndim);
  5102      var max = new Array(ndim);
  5103      var centroids = new Array(k);
  5104      var centroid = null; // Find min, max values for each attribute dimension
  5105  
  5106      for (var i = 0; i < ndim; i++) {
  5107        min[i] = nodes.min(attributes[i]).value;
  5108        max[i] = nodes.max(attributes[i]).value;
  5109      } // Build k centroids, each represented as an n-dim feature vector
  5110  
  5111  
  5112      for (var c = 0; c < k; c++) {
  5113        centroid = [];
  5114  
  5115        for (var _i = 0; _i < ndim; _i++) {
  5116          centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
  5117        }
  5118  
  5119        centroids[c] = centroid;
  5120      }
  5121  
  5122      return centroids;
  5123    };
  5124  
  5125    var classify = function classify(node, centroids, distance, attributes, type) {
  5126      var min = Infinity;
  5127      var index = 0;
  5128  
  5129      for (var i = 0; i < centroids.length; i++) {
  5130        var dist = getDist(distance, node, centroids[i], attributes, type);
  5131  
  5132        if (dist < min) {
  5133          min = dist;
  5134          index = i;
  5135        }
  5136      }
  5137  
  5138      return index;
  5139    };
  5140  
  5141    var buildCluster = function buildCluster(centroid, nodes, assignment) {
  5142      var cluster = [];
  5143      var node = null;
  5144  
  5145      for (var n = 0; n < nodes.length; n++) {
  5146        node = nodes[n];
  5147  
  5148        if (assignment[node.id()] === centroid) {
  5149          //console.log("Node " + node.id() + " is associated with medoid #: " + m);
  5150          cluster.push(node);
  5151        }
  5152      }
  5153  
  5154      return cluster;
  5155    };
  5156  
  5157    var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
  5158      return Math.abs(v2 - v1) <= sensitivityThreshold;
  5159    };
  5160  
  5161    var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
  5162      for (var i = 0; i < v1.length; i++) {
  5163        for (var j = 0; j < v1[i].length; j++) {
  5164          var diff = Math.abs(v1[i][j] - v2[i][j]);
  5165  
  5166          if (diff > sensitivityThreshold) {
  5167            return false;
  5168          }
  5169        }
  5170      }
  5171  
  5172      return true;
  5173    };
  5174  
  5175    var seenBefore = function seenBefore(node, medoids, n) {
  5176      for (var i = 0; i < n; i++) {
  5177        if (node === medoids[i]) return true;
  5178      }
  5179  
  5180      return false;
  5181    };
  5182  
  5183    var randomMedoids = function randomMedoids(nodes, k) {
  5184      var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
  5185      // so we need to check to see if we've already seen or chose this node before.
  5186  
  5187      if (nodes.length < 50) {
  5188        // Randomly select k medoids from the n nodes
  5189        for (var i = 0; i < k; i++) {
  5190          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).
  5191          // Instead choose a different random node.
  5192  
  5193          while (seenBefore(node, medoids, i)) {
  5194            node = nodes[Math.floor(Math.random() * nodes.length)];
  5195          }
  5196  
  5197          medoids[i] = node;
  5198        }
  5199      } else {
  5200        // Relatively large data set, so pretty safe to not check and just select random nodes
  5201        for (var _i2 = 0; _i2 < k; _i2++) {
  5202          medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
  5203        }
  5204      }
  5205  
  5206      return medoids;
  5207    };
  5208  
  5209    var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
  5210      var cost = 0;
  5211  
  5212      for (var n = 0; n < cluster.length; n++) {
  5213        cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
  5214      }
  5215  
  5216      return cost;
  5217    };
  5218  
  5219    var kMeans = function kMeans(options) {
  5220      var cy = this.cy();
  5221      var nodes = this.nodes();
  5222      var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
  5223  
  5224      var opts = setOptions$1(options); // Begin k-means algorithm
  5225  
  5226      var clusters = new Array(opts.k);
  5227      var assignment = {};
  5228      var centroids; // Step 1: Initialize centroid positions
  5229  
  5230      if (opts.testMode) {
  5231        if (typeof opts.testCentroids === 'number') {
  5232          centroids = randomCentroids(nodes, opts.k, opts.attributes);
  5233        } else if (_typeof(opts.testCentroids) === 'object') {
  5234          centroids = opts.testCentroids;
  5235        } else {
  5236          centroids = randomCentroids(nodes, opts.k, opts.attributes);
  5237        }
  5238      } else {
  5239        centroids = randomCentroids(nodes, opts.k, opts.attributes);
  5240      }
  5241  
  5242      var isStillMoving = true;
  5243      var iterations = 0;
  5244  
  5245      while (isStillMoving && iterations < opts.maxIterations) {
  5246        // Step 2: Assign nodes to the nearest centroid
  5247        for (var n = 0; n < nodes.length; n++) {
  5248          node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
  5249  
  5250          assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
  5251        } // Step 3: For each of the k clusters, update its centroid
  5252  
  5253  
  5254        isStillMoving = false;
  5255  
  5256        for (var c = 0; c < opts.k; c++) {
  5257          // Get all nodes that belong to this cluster
  5258          var cluster = buildCluster(c, nodes, assignment);
  5259  
  5260          if (cluster.length === 0) {
  5261            // If cluster is empty, break out early & move to next cluster
  5262            continue;
  5263          } // Update centroids by calculating avg of all nodes within the cluster.
  5264  
  5265  
  5266          var ndim = opts.attributes.length;
  5267          var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
  5268  
  5269          var newCentroid = new Array(ndim);
  5270          var sum = new Array(ndim);
  5271  
  5272          for (var d = 0; d < ndim; d++) {
  5273            sum[d] = 0.0;
  5274  
  5275            for (var i = 0; i < cluster.length; i++) {
  5276              node = cluster[i];
  5277              sum[d] += opts.attributes[d](node);
  5278            }
  5279  
  5280            newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
  5281  
  5282            if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
  5283              isStillMoving = true;
  5284            }
  5285          }
  5286  
  5287          centroids[c] = newCentroid;
  5288          clusters[c] = cy.collection(cluster);
  5289        }
  5290  
  5291        iterations++;
  5292      }
  5293  
  5294      return clusters;
  5295    };
  5296  
  5297    var kMedoids = function kMedoids(options) {
  5298      var cy = this.cy();
  5299      var nodes = this.nodes();
  5300      var node = null;
  5301      var opts = setOptions$1(options); // Begin k-medoids algorithm
  5302  
  5303      var clusters = new Array(opts.k);
  5304      var medoids;
  5305      var assignment = {};
  5306      var curCost;
  5307      var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
  5308      // Step 1: Initialize k medoids
  5309  
  5310      if (opts.testMode) {
  5311        if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
  5312          medoids = opts.testCentroids;
  5313        } else {
  5314          medoids = randomMedoids(nodes, opts.k);
  5315        }
  5316      } else {
  5317        medoids = randomMedoids(nodes, opts.k);
  5318      }
  5319  
  5320      var isStillMoving = true;
  5321      var iterations = 0;
  5322  
  5323      while (isStillMoving && iterations < opts.maxIterations) {
  5324        // Step 2: Assign nodes to the nearest medoid
  5325        for (var n = 0; n < nodes.length; n++) {
  5326          node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
  5327  
  5328          assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
  5329        }
  5330  
  5331        isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
  5332        // select the node with the lowest configuration cost as new medoid.
  5333  
  5334        for (var m = 0; m < medoids.length; m++) {
  5335          // Get all nodes that belong to this medoid
  5336          var cluster = buildCluster(m, nodes, assignment);
  5337  
  5338          if (cluster.length === 0) {
  5339            // If cluster is empty, break out early & move to next cluster
  5340            continue;
  5341          }
  5342  
  5343          minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
  5344          // Select different medoid if its configuration has the lowest cost
  5345  
  5346          for (var _n = 0; _n < cluster.length; _n++) {
  5347            curCost = findCost(cluster[_n], cluster, opts.attributes);
  5348  
  5349            if (curCost < minCosts[m]) {
  5350              minCosts[m] = curCost;
  5351              medoids[m] = cluster[_n];
  5352              isStillMoving = true;
  5353            }
  5354          }
  5355  
  5356          clusters[m] = cy.collection(cluster);
  5357        }
  5358  
  5359        iterations++;
  5360      }
  5361  
  5362      return clusters;
  5363    };
  5364  
  5365    var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
  5366      var numerator, denominator;
  5367  
  5368      for (var n = 0; n < nodes.length; n++) {
  5369        for (var c = 0; c < centroids.length; c++) {
  5370          weight[n][c] = Math.pow(U[n][c], opts.m);
  5371        }
  5372      }
  5373  
  5374      for (var _c = 0; _c < centroids.length; _c++) {
  5375        for (var dim = 0; dim < opts.attributes.length; dim++) {
  5376          numerator = 0;
  5377          denominator = 0;
  5378  
  5379          for (var _n2 = 0; _n2 < nodes.length; _n2++) {
  5380            numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
  5381            denominator += weight[_n2][_c];
  5382          }
  5383  
  5384          centroids[_c][dim] = numerator / denominator;
  5385        }
  5386      }
  5387    };
  5388  
  5389    var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
  5390      // Save previous step
  5391      for (var i = 0; i < U.length; i++) {
  5392        _U[i] = U[i].slice();
  5393      }
  5394  
  5395      var sum, numerator, denominator;
  5396      var pow = 2 / (opts.m - 1);
  5397  
  5398      for (var c = 0; c < centroids.length; c++) {
  5399        for (var n = 0; n < nodes.length; n++) {
  5400          sum = 0;
  5401  
  5402          for (var k = 0; k < centroids.length; k++) {
  5403            // against all other centroids
  5404            numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
  5405            denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
  5406            sum += Math.pow(numerator / denominator, pow);
  5407          }
  5408  
  5409          U[n][c] = 1 / sum;
  5410        }
  5411      }
  5412    };
  5413  
  5414    var assign$1 = function assign(nodes, U, opts, cy) {
  5415      var clusters = new Array(opts.k);
  5416  
  5417      for (var c = 0; c < clusters.length; c++) {
  5418        clusters[c] = [];
  5419      }
  5420  
  5421      var max;
  5422      var index;
  5423  
  5424      for (var n = 0; n < U.length; n++) {
  5425        // for each node (U is N x C matrix)
  5426        max = -Infinity;
  5427        index = -1; // Determine which cluster the node is most likely to belong in
  5428  
  5429        for (var _c2 = 0; _c2 < U[0].length; _c2++) {
  5430          if (U[n][_c2] > max) {
  5431            max = U[n][_c2];
  5432            index = _c2;
  5433          }
  5434        }
  5435  
  5436        clusters[index].push(nodes[n]);
  5437      } // Turn every array into a collection of nodes
  5438  
  5439  
  5440      for (var _c3 = 0; _c3 < clusters.length; _c3++) {
  5441        clusters[_c3] = cy.collection(clusters[_c3]);
  5442      }
  5443  
  5444      return clusters;
  5445    };
  5446  
  5447    var fuzzyCMeans = function fuzzyCMeans(options) {
  5448      var cy = this.cy();
  5449      var nodes = this.nodes();
  5450      var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
  5451  
  5452      var clusters;
  5453      var centroids;
  5454      var U;
  5455  
  5456      var _U;
  5457  
  5458      var weight; // Step 1: Initialize letiables.
  5459  
  5460      _U = new Array(nodes.length);
  5461  
  5462      for (var i = 0; i < nodes.length; i++) {
  5463        // N x C matrix
  5464        _U[i] = new Array(opts.k);
  5465      }
  5466  
  5467      U = new Array(nodes.length);
  5468  
  5469      for (var _i3 = 0; _i3 < nodes.length; _i3++) {
  5470        // N x C matrix
  5471        U[_i3] = new Array(opts.k);
  5472      }
  5473  
  5474      for (var _i4 = 0; _i4 < nodes.length; _i4++) {
  5475        var total = 0;
  5476  
  5477        for (var j = 0; j < opts.k; j++) {
  5478          U[_i4][j] = Math.random();
  5479          total += U[_i4][j];
  5480        }
  5481  
  5482        for (var _j = 0; _j < opts.k; _j++) {
  5483          U[_i4][_j] = U[_i4][_j] / total;
  5484        }
  5485      }
  5486  
  5487      centroids = new Array(opts.k);
  5488  
  5489      for (var _i5 = 0; _i5 < opts.k; _i5++) {
  5490        centroids[_i5] = new Array(opts.attributes.length);
  5491      }
  5492  
  5493      weight = new Array(nodes.length);
  5494  
  5495      for (var _i6 = 0; _i6 < nodes.length; _i6++) {
  5496        // N x C matrix
  5497        weight[_i6] = new Array(opts.k);
  5498      } // end init FCM
  5499  
  5500  
  5501      var isStillMoving = true;
  5502      var iterations = 0;
  5503  
  5504      while (isStillMoving && iterations < opts.maxIterations) {
  5505        isStillMoving = false; // Step 2: Calculate the centroids for each step.
  5506  
  5507        updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
  5508  
  5509        updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
  5510  
  5511        if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
  5512          isStillMoving = true;
  5513        }
  5514  
  5515        iterations++;
  5516      } // Assign nodes to clusters with highest probability.
  5517  
  5518  
  5519      clusters = assign$1(nodes, U, opts, cy);
  5520      return {
  5521        clusters: clusters,
  5522        degreeOfMembership: U
  5523      };
  5524    };
  5525  
  5526    var kClustering = {
  5527      kMeans: kMeans,
  5528      kMedoids: kMedoids,
  5529      fuzzyCMeans: fuzzyCMeans,
  5530      fcm: fuzzyCMeans
  5531    };
  5532  
  5533    // Implemented by Zoe Xi @zoexi for GSOC 2016
  5534    var defaults$6 = defaults({
  5535      distance: 'euclidean',
  5536      // distance metric to compare nodes
  5537      linkage: 'min',
  5538      // linkage criterion : how to determine the distance between clusters of nodes
  5539      mode: 'threshold',
  5540      // mode:'threshold' => clusters must be threshold distance apart
  5541      threshold: Infinity,
  5542      // the distance threshold
  5543      // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
  5544      addDendrogram: false,
  5545      // whether to add the dendrogram to the graph for viz
  5546      dendrogramDepth: 0,
  5547      // depth at which dendrogram branches are merged into the returned clusters
  5548      attributes: [] // array of attr functions
  5549  
  5550    });
  5551    var linkageAliases = {
  5552      'single': 'min',
  5553      'complete': 'max'
  5554    };
  5555  
  5556    var setOptions$2 = function setOptions(options) {
  5557      var opts = defaults$6(options);
  5558      var preferredAlias = linkageAliases[opts.linkage];
  5559  
  5560      if (preferredAlias != null) {
  5561        opts.linkage = preferredAlias;
  5562      }
  5563  
  5564      return opts;
  5565    };
  5566  
  5567    var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
  5568      // Find two closest clusters from cached mins
  5569      var minKey = 0;
  5570      var min = Infinity;
  5571      var dist;
  5572      var attrs = opts.attributes;
  5573  
  5574      var getDist = function getDist(n1, n2) {
  5575        return clusteringDistance(opts.distance, attrs.length, function (i) {
  5576          return attrs[i](n1);
  5577        }, function (i) {
  5578          return attrs[i](n2);
  5579        }, n1, n2);
  5580      };
  5581  
  5582      for (var i = 0; i < clusters.length; i++) {
  5583        var key = clusters[i].key;
  5584        var _dist = dists[key][mins[key]];
  5585  
  5586        if (_dist < min) {
  5587          minKey = key;
  5588          min = _dist;
  5589        }
  5590      }
  5591  
  5592      if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
  5593        return false;
  5594      }
  5595  
  5596      var c1 = index[minKey];
  5597      var c2 = index[mins[minKey]];
  5598      var merged; // Merge two closest clusters
  5599  
  5600      if (opts.mode === 'dendrogram') {
  5601        merged = {
  5602          left: c1,
  5603          right: c2,
  5604          key: c1.key
  5605        };
  5606      } else {
  5607        merged = {
  5608          value: c1.value.concat(c2.value),
  5609          key: c1.key
  5610        };
  5611      }
  5612  
  5613      clusters[c1.index] = merged;
  5614      clusters.splice(c2.index, 1);
  5615      index[c1.key] = merged; // Update distances with new merged cluster
  5616  
  5617      for (var _i = 0; _i < clusters.length; _i++) {
  5618        var cur = clusters[_i];
  5619  
  5620        if (c1.key === cur.key) {
  5621          dist = Infinity;
  5622        } else if (opts.linkage === 'min') {
  5623          dist = dists[c1.key][cur.key];
  5624  
  5625          if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
  5626            dist = dists[c2.key][cur.key];
  5627          }
  5628        } else if (opts.linkage === 'max') {
  5629          dist = dists[c1.key][cur.key];
  5630  
  5631          if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
  5632            dist = dists[c2.key][cur.key];
  5633          }
  5634        } else if (opts.linkage === 'mean') {
  5635          dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
  5636        } else {
  5637          if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
  5638        }
  5639  
  5640        dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
  5641      } // Update cached mins
  5642  
  5643  
  5644      for (var _i2 = 0; _i2 < clusters.length; _i2++) {
  5645        var key1 = clusters[_i2].key;
  5646  
  5647        if (mins[key1] === c1.key || mins[key1] === c2.key) {
  5648          var _min = key1;
  5649  
  5650          for (var j = 0; j < clusters.length; j++) {
  5651            var key2 = clusters[j].key;
  5652  
  5653            if (dists[key1][key2] < dists[key1][_min]) {
  5654              _min = key2;
  5655            }
  5656          }
  5657  
  5658          mins[key1] = _min;
  5659        }
  5660  
  5661        clusters[_i2].index = _i2;
  5662      } // Clean up meta data used for clustering
  5663  
  5664  
  5665      c1.key = c2.key = c1.index = c2.index = null;
  5666      return true;
  5667    };
  5668  
  5669    var getAllChildren = function getAllChildren(root, arr, cy) {
  5670      if (!root) return;
  5671  
  5672      if (root.value) {
  5673        arr.push(root.value);
  5674      } else {
  5675        if (root.left) getAllChildren(root.left, arr);
  5676        if (root.right) getAllChildren(root.right, arr);
  5677      }
  5678    };
  5679  
  5680    var buildDendrogram = function buildDendrogram(root, cy) {
  5681      if (!root) return '';
  5682  
  5683      if (root.left && root.right) {
  5684        var leftStr = buildDendrogram(root.left, cy);
  5685        var rightStr = buildDendrogram(root.right, cy);
  5686        var node = cy.add({
  5687          group: 'nodes',
  5688          data: {
  5689            id: leftStr + ',' + rightStr
  5690          }
  5691        });
  5692        cy.add({
  5693          group: 'edges',
  5694          data: {
  5695            source: leftStr,
  5696            target: node.id()
  5697          }
  5698        });
  5699        cy.add({
  5700          group: 'edges',
  5701          data: {
  5702            source: rightStr,
  5703            target: node.id()
  5704          }
  5705        });
  5706        return node.id();
  5707      } else if (root.value) {
  5708        return root.value.id();
  5709      }
  5710    };
  5711  
  5712    var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
  5713      if (!root) return [];
  5714      var left = [],
  5715          right = [],
  5716          leaves = [];
  5717  
  5718      if (k === 0) {
  5719        // don't cut tree, simply return all nodes as 1 single cluster
  5720        if (root.left) getAllChildren(root.left, left);
  5721        if (root.right) getAllChildren(root.right, right);
  5722        leaves = left.concat(right);
  5723        return [cy.collection(leaves)];
  5724      } else if (k === 1) {
  5725        // cut at root
  5726        if (root.value) {
  5727          // leaf node
  5728          return [cy.collection(root.value)];
  5729        } else {
  5730          if (root.left) getAllChildren(root.left, left);
  5731          if (root.right) getAllChildren(root.right, right);
  5732          return [cy.collection(left), cy.collection(right)];
  5733        }
  5734      } else {
  5735        if (root.value) {
  5736          return [cy.collection(root.value)];
  5737        } else {
  5738          if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
  5739          if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
  5740          return left.concat(right);
  5741        }
  5742      }
  5743    };
  5744    /* eslint-enable */
  5745  
  5746  
  5747    var hierarchicalClustering = function hierarchicalClustering(options) {
  5748      var cy = this.cy();
  5749      var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
  5750  
  5751      var opts = setOptions$2(options);
  5752      var attrs = opts.attributes;
  5753  
  5754      var getDist = function getDist(n1, n2) {
  5755        return clusteringDistance(opts.distance, attrs.length, function (i) {
  5756          return attrs[i](n1);
  5757        }, function (i) {
  5758          return attrs[i](n2);
  5759        }, n1, n2);
  5760      }; // Begin hierarchical algorithm
  5761  
  5762  
  5763      var clusters = [];
  5764      var dists = []; // distances between each pair of clusters
  5765  
  5766      var mins = []; // closest cluster for each cluster
  5767  
  5768      var index = []; // hash of all clusters by key
  5769      // In agglomerative (bottom-up) clustering, each node starts as its own cluster
  5770  
  5771      for (var n = 0; n < nodes.length; n++) {
  5772        var cluster = {
  5773          value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
  5774          key: n,
  5775          index: n
  5776        };
  5777        clusters[n] = cluster;
  5778        index[n] = cluster;
  5779        dists[n] = [];
  5780        mins[n] = 0;
  5781      } // Calculate the distance between each pair of clusters
  5782  
  5783  
  5784      for (var i = 0; i < clusters.length; i++) {
  5785        for (var j = 0; j <= i; j++) {
  5786          var dist = void 0;
  5787  
  5788          if (opts.mode === 'dendrogram') {
  5789            // modes store cluster values differently
  5790            dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
  5791          } else {
  5792            dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
  5793          }
  5794  
  5795          dists[i][j] = dist;
  5796          dists[j][i] = dist;
  5797  
  5798          if (dist < dists[i][mins[i]]) {
  5799            mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
  5800          }
  5801        }
  5802      } // Find the closest pair of clusters and merge them into a single cluster.
  5803      // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
  5804  
  5805  
  5806      var merged = mergeClosest(clusters, index, dists, mins, opts);
  5807  
  5808      while (merged) {
  5809        merged = mergeClosest(clusters, index, dists, mins, opts);
  5810      }
  5811  
  5812      var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
  5813      // in addition to returning the clusters.
  5814  
  5815      if (opts.mode === 'dendrogram') {
  5816        retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
  5817        if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
  5818      } else {
  5819        // Regular mode simply returns the clusters
  5820        retClusters = new Array(clusters.length);
  5821        clusters.forEach(function (cluster, i) {
  5822          // Clean up meta data used for clustering
  5823          cluster.key = cluster.index = null;
  5824          retClusters[i] = cy.collection(cluster.value);
  5825        });
  5826      }
  5827  
  5828      return retClusters;
  5829    };
  5830  
  5831    var hierarchicalClustering$1 = {
  5832      hierarchicalClustering: hierarchicalClustering,
  5833      hca: hierarchicalClustering
  5834    };
  5835  
  5836    // Implemented by Zoe Xi @zoexi for GSOC 2016
  5837    var defaults$7 = defaults({
  5838      distance: 'euclidean',
  5839      // distance metric to compare attributes between two nodes
  5840      preference: 'median',
  5841      // suitability of a data point to serve as an exemplar
  5842      damping: 0.8,
  5843      // damping factor between [0.5, 1)
  5844      maxIterations: 1000,
  5845      // max number of iterations to run
  5846      minIterations: 100,
  5847      // min number of iterations to run in order for clustering to stop
  5848      attributes: [// functions to quantify the similarity between any two points
  5849        // e.g. node => node.data('weight')
  5850      ]
  5851    });
  5852  
  5853    var setOptions$3 = function setOptions(options) {
  5854      var dmp = options.damping;
  5855      var pref = options.preference;
  5856  
  5857      if (!(0.5 <= dmp && dmp < 1)) {
  5858        error("Damping must range on [0.5, 1).  Got: ".concat(dmp));
  5859      }
  5860  
  5861      var validPrefs = ['median', 'mean', 'min', 'max'];
  5862  
  5863      if (!(validPrefs.some(function (v) {
  5864        return v === pref;
  5865      }) || number(pref))) {
  5866        error("Preference must be one of [".concat(validPrefs.map(function (p) {
  5867          return "'".concat(p, "'");
  5868        }).join(', '), "] or a number.  Got: ").concat(pref));
  5869      }
  5870  
  5871      return defaults$7(options);
  5872    };
  5873    /* eslint-enable */
  5874  
  5875  
  5876    var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
  5877      var attr = function attr(n, i) {
  5878        return attributes[i](n);
  5879      }; // nb negative because similarity should have an inverse relationship to distance
  5880  
  5881  
  5882      return -clusteringDistance(type, attributes.length, function (i) {
  5883        return attr(n1, i);
  5884      }, function (i) {
  5885        return attr(n2, i);
  5886      }, n1, n2);
  5887    };
  5888  
  5889    var getPreference = function getPreference(S, preference) {
  5890      // larger preference = greater # of clusters
  5891      var p = null;
  5892  
  5893      if (preference === 'median') {
  5894        p = median(S);
  5895      } else if (preference === 'mean') {
  5896        p = mean(S);
  5897      } else if (preference === 'min') {
  5898        p = min(S);
  5899      } else if (preference === 'max') {
  5900        p = max(S);
  5901      } else {
  5902        // Custom preference number, as set by user
  5903        p = preference;
  5904      }
  5905  
  5906      return p;
  5907    };
  5908  
  5909    var findExemplars = function findExemplars(n, R, A) {
  5910      var indices = [];
  5911  
  5912      for (var i = 0; i < n; i++) {
  5913        if (R[i * n + i] + A[i * n + i] > 0) {
  5914          indices.push(i);
  5915        }
  5916      }
  5917  
  5918      return indices;
  5919    };
  5920  
  5921    var assignClusters = function assignClusters(n, S, exemplars) {
  5922      var clusters = [];
  5923  
  5924      for (var i = 0; i < n; i++) {
  5925        var index = -1;
  5926        var max = -Infinity;
  5927  
  5928        for (var ei = 0; ei < exemplars.length; ei++) {
  5929          var e = exemplars[ei];
  5930  
  5931          if (S[i * n + e] > max) {
  5932            index = e;
  5933            max = S[i * n + e];
  5934          }
  5935        }
  5936  
  5937        if (index > 0) {
  5938          clusters.push(index);
  5939        }
  5940      }
  5941  
  5942      for (var _ei = 0; _ei < exemplars.length; _ei++) {
  5943        clusters[exemplars[_ei]] = exemplars[_ei];
  5944      }
  5945  
  5946      return clusters;
  5947    };
  5948  
  5949    var assign$2 = function assign(n, S, exemplars) {
  5950      var clusters = assignClusters(n, S, exemplars);
  5951  
  5952      for (var ei = 0; ei < exemplars.length; ei++) {
  5953        var ii = [];
  5954  
  5955        for (var c = 0; c < clusters.length; c++) {
  5956          if (clusters[c] === exemplars[ei]) {
  5957            ii.push(c);
  5958          }
  5959        }
  5960  
  5961        var maxI = -1;
  5962        var maxSum = -Infinity;
  5963  
  5964        for (var i = 0; i < ii.length; i++) {
  5965          var sum = 0;
  5966  
  5967          for (var j = 0; j < ii.length; j++) {
  5968            sum += S[ii[j] * n + ii[i]];
  5969          }
  5970  
  5971          if (sum > maxSum) {
  5972            maxI = i;
  5973            maxSum = sum;
  5974          }
  5975        }
  5976  
  5977        exemplars[ei] = ii[maxI];
  5978      }
  5979  
  5980      clusters = assignClusters(n, S, exemplars);
  5981      return clusters;
  5982    };
  5983  
  5984    var affinityPropagation = function affinityPropagation(options) {
  5985      var cy = this.cy();
  5986      var nodes = this.nodes();
  5987      var opts = setOptions$3(options); // Map each node to its position in node array
  5988  
  5989      var id2position = {};
  5990  
  5991      for (var i = 0; i < nodes.length; i++) {
  5992        id2position[nodes[i].id()] = i;
  5993      } // Begin affinity propagation algorithm
  5994  
  5995  
  5996      var n; // number of data points
  5997  
  5998      var n2; // size of matrices
  5999  
  6000      var S; // similarity matrix (1D array)
  6001  
  6002      var p; // preference/suitability of a data point to serve as an exemplar
  6003  
  6004      var R; // responsibility matrix (1D array)
  6005  
  6006      var A; // availability matrix (1D array)
  6007  
  6008      n = nodes.length;
  6009      n2 = n * n; // Initialize and build S similarity matrix
  6010  
  6011      S = new Array(n2);
  6012  
  6013      for (var _i = 0; _i < n2; _i++) {
  6014        S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
  6015      }
  6016  
  6017      for (var _i2 = 0; _i2 < n; _i2++) {
  6018        for (var j = 0; j < n; j++) {
  6019          if (_i2 !== j) {
  6020            S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
  6021          }
  6022        }
  6023      } // Place preferences on the diagonal of S
  6024  
  6025  
  6026      p = getPreference(S, opts.preference);
  6027  
  6028      for (var _i3 = 0; _i3 < n; _i3++) {
  6029        S[_i3 * n + _i3] = p;
  6030      } // Initialize R responsibility matrix
  6031  
  6032  
  6033      R = new Array(n2);
  6034  
  6035      for (var _i4 = 0; _i4 < n2; _i4++) {
  6036        R[_i4] = 0.0;
  6037      } // Initialize A availability matrix
  6038  
  6039  
  6040      A = new Array(n2);
  6041  
  6042      for (var _i5 = 0; _i5 < n2; _i5++) {
  6043        A[_i5] = 0.0;
  6044      }
  6045  
  6046      var old = new Array(n);
  6047      var Rp = new Array(n);
  6048      var se = new Array(n);
  6049  
  6050      for (var _i6 = 0; _i6 < n; _i6++) {
  6051        old[_i6] = 0.0;
  6052        Rp[_i6] = 0.0;
  6053        se[_i6] = 0;
  6054      }
  6055  
  6056      var e = new Array(n * opts.minIterations);
  6057  
  6058      for (var _i7 = 0; _i7 < e.length; _i7++) {
  6059        e[_i7] = 0;
  6060      }
  6061  
  6062      var iter;
  6063  
  6064      for (iter = 0; iter < opts.maxIterations; iter++) {
  6065        // main algorithmic loop
  6066        // Update R responsibility matrix
  6067        for (var _i8 = 0; _i8 < n; _i8++) {
  6068          var max = -Infinity,
  6069              max2 = -Infinity,
  6070              maxI = -1,
  6071              AS = 0.0;
  6072  
  6073          for (var _j = 0; _j < n; _j++) {
  6074            old[_j] = R[_i8 * n + _j];
  6075            AS = A[_i8 * n + _j] + S[_i8 * n + _j];
  6076  
  6077            if (AS >= max) {
  6078              max2 = max;
  6079              max = AS;
  6080              maxI = _j;
  6081            } else if (AS > max2) {
  6082              max2 = AS;
  6083            }
  6084          }
  6085  
  6086          for (var _j2 = 0; _j2 < n; _j2++) {
  6087            R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
  6088          }
  6089  
  6090          R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
  6091        } // Update A availability matrix
  6092  
  6093  
  6094        for (var _i9 = 0; _i9 < n; _i9++) {
  6095          var sum = 0;
  6096  
  6097          for (var _j3 = 0; _j3 < n; _j3++) {
  6098            old[_j3] = A[_j3 * n + _i9];
  6099            Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
  6100            sum += Rp[_j3];
  6101          }
  6102  
  6103          sum -= Rp[_i9];
  6104          Rp[_i9] = R[_i9 * n + _i9];
  6105          sum += Rp[_i9];
  6106  
  6107          for (var _j4 = 0; _j4 < n; _j4++) {
  6108            A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
  6109          }
  6110  
  6111          A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
  6112        } // Check for convergence
  6113  
  6114  
  6115        var K = 0;
  6116  
  6117        for (var _i10 = 0; _i10 < n; _i10++) {
  6118          var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
  6119          e[iter % opts.minIterations * n + _i10] = E;
  6120          K += E;
  6121        }
  6122  
  6123        if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
  6124          var _sum = 0;
  6125  
  6126          for (var _i11 = 0; _i11 < n; _i11++) {
  6127            se[_i11] = 0;
  6128  
  6129            for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
  6130              se[_i11] += e[_j5 * n + _i11];
  6131            }
  6132  
  6133            if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
  6134              _sum++;
  6135            }
  6136          }
  6137  
  6138          if (_sum === n) {
  6139            // then we have convergence
  6140            break;
  6141          }
  6142        }
  6143      } // Identify exemplars (cluster centers)
  6144  
  6145  
  6146      var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
  6147  
  6148      var clusterIndices = assign$2(n, S, exemplarsIndices);
  6149      var clusters = {};
  6150  
  6151      for (var c = 0; c < exemplarsIndices.length; c++) {
  6152        clusters[exemplarsIndices[c]] = [];
  6153      }
  6154  
  6155      for (var _i12 = 0; _i12 < nodes.length; _i12++) {
  6156        var pos = id2position[nodes[_i12].id()];
  6157  
  6158        var clusterIndex = clusterIndices[pos];
  6159  
  6160        if (clusterIndex != null) {
  6161          // the node may have not been assigned a cluster if no valid attributes were specified
  6162          clusters[clusterIndex].push(nodes[_i12]);
  6163        }
  6164      }
  6165  
  6166      var retClusters = new Array(exemplarsIndices.length);
  6167  
  6168      for (var _c = 0; _c < exemplarsIndices.length; _c++) {
  6169        retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
  6170      }
  6171  
  6172      return retClusters;
  6173    };
  6174  
  6175    var affinityPropagation$1 = {
  6176      affinityPropagation: affinityPropagation,
  6177      ap: affinityPropagation
  6178    };
  6179  
  6180    var hierholzerDefaults = defaults({
  6181      root: undefined,
  6182      directed: false
  6183    });
  6184    var elesfn$b = {
  6185      hierholzer: function hierholzer(options) {
  6186        if (!plainObject(options)) {
  6187          var args = arguments;
  6188          options = {
  6189            root: args[0],
  6190            directed: args[1]
  6191          };
  6192        }
  6193  
  6194        var _hierholzerDefaults = hierholzerDefaults(options),
  6195            root = _hierholzerDefaults.root,
  6196            directed = _hierholzerDefaults.directed;
  6197  
  6198        var eles = this;
  6199        var dflag = false;
  6200        var oddIn;
  6201        var oddOut;
  6202        var startVertex;
  6203        if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
  6204        var nodes = {};
  6205        var edges = {};
  6206  
  6207        if (directed) {
  6208          eles.forEach(function (ele) {
  6209            var id = ele.id();
  6210  
  6211            if (ele.isNode()) {
  6212              var ind = ele.indegree(true);
  6213              var outd = ele.outdegree(true);
  6214              var d1 = ind - outd;
  6215              var d2 = outd - ind;
  6216  
  6217              if (d1 == 1) {
  6218                if (oddIn) dflag = true;else oddIn = id;
  6219              } else if (d2 == 1) {
  6220                if (oddOut) dflag = true;else oddOut = id;
  6221              } else if (d2 > 1 || d1 > 1) {
  6222                dflag = true;
  6223              }
  6224  
  6225              nodes[id] = [];
  6226              ele.outgoers().forEach(function (e) {
  6227                if (e.isEdge()) nodes[id].push(e.id());
  6228              });
  6229            } else {
  6230              edges[id] = [undefined, ele.target().id()];
  6231            }
  6232          });
  6233        } else {
  6234          eles.forEach(function (ele) {
  6235            var id = ele.id();
  6236  
  6237            if (ele.isNode()) {
  6238              var d = ele.degree(true);
  6239  
  6240              if (d % 2) {
  6241                if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
  6242              }
  6243  
  6244              nodes[id] = [];
  6245              ele.connectedEdges().forEach(function (e) {
  6246                return nodes[id].push(e.id());
  6247              });
  6248            } else {
  6249              edges[id] = [ele.source().id(), ele.target().id()];
  6250            }
  6251          });
  6252        }
  6253  
  6254        var result = {
  6255          found: false,
  6256          trail: undefined
  6257        };
  6258        if (dflag) return result;else if (oddOut && oddIn) {
  6259          if (directed) {
  6260            if (startVertex && oddOut != startVertex) {
  6261              return result;
  6262            }
  6263  
  6264            startVertex = oddOut;
  6265          } else {
  6266            if (startVertex && oddOut != startVertex && oddIn != startVertex) {
  6267              return result;
  6268            } else if (!startVertex) {
  6269              startVertex = oddOut;
  6270            }
  6271          }
  6272        } else {
  6273          if (!startVertex) startVertex = eles[0].id();
  6274        }
  6275  
  6276        var walk = function walk(v) {
  6277          var currentNode = v;
  6278          var subtour = [v];
  6279          var adj, adjTail, adjHead;
  6280  
  6281          while (nodes[currentNode].length) {
  6282            adj = nodes[currentNode].shift();
  6283            adjTail = edges[adj][0];
  6284            adjHead = edges[adj][1];
  6285  
  6286            if (currentNode != adjHead) {
  6287              nodes[adjHead] = nodes[adjHead].filter(function (e) {
  6288                return e != adj;
  6289              });
  6290              currentNode = adjHead;
  6291            } else if (!directed && currentNode != adjTail) {
  6292              nodes[adjTail] = nodes[adjTail].filter(function (e) {
  6293                return e != adj;
  6294              });
  6295              currentNode = adjTail;
  6296            }
  6297  
  6298            subtour.unshift(adj);
  6299            subtour.unshift(currentNode);
  6300          }
  6301  
  6302          return subtour;
  6303        };
  6304  
  6305        var trail = [];
  6306        var subtour = [];
  6307        subtour = walk(startVertex);
  6308  
  6309        while (subtour.length != 1) {
  6310          if (nodes[subtour[0]].length == 0) {
  6311            trail.unshift(eles.getElementById(subtour.shift()));
  6312            trail.unshift(eles.getElementById(subtour.shift()));
  6313          } else {
  6314            subtour = walk(subtour.shift()).concat(subtour);
  6315          }
  6316        }
  6317  
  6318        trail.unshift(eles.getElementById(subtour.shift())); // final node
  6319  
  6320        for (var d in nodes) {
  6321          if (nodes[d].length) {
  6322            return result;
  6323          }
  6324        }
  6325  
  6326        result.found = true;
  6327        result.trail = this.spawn(trail);
  6328        return result;
  6329      }
  6330    };
  6331  
  6332    var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
  6333      var eles = this;
  6334      var nodes = {};
  6335      var id = 0;
  6336      var edgeCount = 0;
  6337      var components = [];
  6338      var stack = [];
  6339      var visitedEdges = {};
  6340  
  6341      var buildComponent = function buildComponent(x, y) {
  6342        var i = stack.length - 1;
  6343        var cutset = [];
  6344        var component = eles.spawn();
  6345  
  6346        while (stack[i].x != x || stack[i].y != y) {
  6347          cutset.push(stack.pop().edge);
  6348          i--;
  6349        }
  6350  
  6351        cutset.push(stack.pop().edge);
  6352        cutset.forEach(function (edge) {
  6353          var connectedNodes = edge.connectedNodes().intersection(eles);
  6354          component.merge(edge);
  6355          connectedNodes.forEach(function (node) {
  6356            var nodeId = node.id();
  6357            var connectedEdges = node.connectedEdges().intersection(eles);
  6358            component.merge(node);
  6359  
  6360            if (!nodes[nodeId].cutVertex) {
  6361              component.merge(connectedEdges);
  6362            } else {
  6363              component.merge(connectedEdges.filter(function (edge) {
  6364                return edge.isLoop();
  6365              }));
  6366            }
  6367          });
  6368        });
  6369        components.push(component);
  6370      };
  6371  
  6372      var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
  6373        if (root === parent) edgeCount += 1;
  6374        nodes[currentNode] = {
  6375          id: id,
  6376          low: id++,
  6377          cutVertex: false
  6378        };
  6379        var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
  6380  
  6381        if (edges.size() === 0) {
  6382          components.push(eles.spawn(eles.getElementById(currentNode)));
  6383        } else {
  6384          var sourceId, targetId, otherNodeId, edgeId;
  6385          edges.forEach(function (edge) {
  6386            sourceId = edge.source().id();
  6387            targetId = edge.target().id();
  6388            otherNodeId = sourceId === currentNode ? targetId : sourceId;
  6389  
  6390            if (otherNodeId !== parent) {
  6391              edgeId = edge.id();
  6392  
  6393              if (!visitedEdges[edgeId]) {
  6394                visitedEdges[edgeId] = true;
  6395                stack.push({
  6396                  x: currentNode,
  6397                  y: otherNodeId,
  6398                  edge: edge
  6399                });
  6400              }
  6401  
  6402              if (!(otherNodeId in nodes)) {
  6403                biconnectedSearch(root, otherNodeId, currentNode);
  6404                nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
  6405  
  6406                if (nodes[currentNode].id <= nodes[otherNodeId].low) {
  6407                  nodes[currentNode].cutVertex = true;
  6408                  buildComponent(currentNode, otherNodeId);
  6409                }
  6410              } else {
  6411                nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
  6412              }
  6413            }
  6414          });
  6415        }
  6416      };
  6417  
  6418      eles.forEach(function (ele) {
  6419        if (ele.isNode()) {
  6420          var nodeId = ele.id();
  6421  
  6422          if (!(nodeId in nodes)) {
  6423            edgeCount = 0;
  6424            biconnectedSearch(nodeId, nodeId);
  6425            nodes[nodeId].cutVertex = edgeCount > 1;
  6426          }
  6427        }
  6428      });
  6429      var cutVertices = Object.keys(nodes).filter(function (id) {
  6430        return nodes[id].cutVertex;
  6431      }).map(function (id) {
  6432        return eles.getElementById(id);
  6433      });
  6434      return {
  6435        cut: eles.spawn(cutVertices),
  6436        components: components
  6437      };
  6438    };
  6439  
  6440    var hopcroftTarjanBiconnected$1 = {
  6441      hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
  6442      htbc: hopcroftTarjanBiconnected,
  6443      htb: hopcroftTarjanBiconnected,
  6444      hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
  6445    };
  6446  
  6447    var elesfn$c = {};
  6448    [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) {
  6449      extend(elesfn$c, props);
  6450    });
  6451  
  6452    /*!
  6453    Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
  6454    Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
  6455    Licensed under The MIT License (http://opensource.org/licenses/MIT)
  6456    */
  6457  
  6458    /*  promise states [Promises/A+ 2.1]  */
  6459    var STATE_PENDING = 0;
  6460    /*  [Promises/A+ 2.1.1]  */
  6461  
  6462    var STATE_FULFILLED = 1;
  6463    /*  [Promises/A+ 2.1.2]  */
  6464  
  6465    var STATE_REJECTED = 2;
  6466    /*  [Promises/A+ 2.1.3]  */
  6467  
  6468    /*  promise object constructor  */
  6469  
  6470    var api = function api(executor) {
  6471      /*  optionally support non-constructor/plain-function call  */
  6472      if (!(this instanceof api)) return new api(executor);
  6473      /*  initialize object  */
  6474  
  6475      this.id = 'Thenable/1.0.7';
  6476      this.state = STATE_PENDING;
  6477      /*  initial state  */
  6478  
  6479      this.fulfillValue = undefined;
  6480      /*  initial value  */
  6481  
  6482      /*  [Promises/A+ 1.3, 2.1.2.2]  */
  6483  
  6484      this.rejectReason = undefined;
  6485      /*  initial reason */
  6486  
  6487      /*  [Promises/A+ 1.5, 2.1.3.2]  */
  6488  
  6489      this.onFulfilled = [];
  6490      /*  initial handlers  */
  6491  
  6492      this.onRejected = [];
  6493      /*  initial handlers  */
  6494  
  6495      /*  provide optional information-hiding proxy  */
  6496  
  6497      this.proxy = {
  6498        then: this.then.bind(this)
  6499      };
  6500      /*  support optional executor function  */
  6501  
  6502      if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
  6503    };
  6504    /*  promise API methods  */
  6505  
  6506  
  6507    api.prototype = {
  6508      /*  promise resolving methods  */
  6509      fulfill: function fulfill(value) {
  6510        return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
  6511      },
  6512      reject: function reject(value) {
  6513        return deliver(this, STATE_REJECTED, 'rejectReason', value);
  6514      },
  6515  
  6516      /*  "The then Method" [Promises/A+ 1.1, 1.2, 2.2]  */
  6517      then: function then(onFulfilled, onRejected) {
  6518        var curr = this;
  6519        var next = new api();
  6520        /*  [Promises/A+ 2.2.7]  */
  6521  
  6522        curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
  6523        /*  [Promises/A+ 2.2.2/2.2.6]  */
  6524  
  6525        curr.onRejected.push(resolver(onRejected, next, 'reject'));
  6526        /*  [Promises/A+ 2.2.3/2.2.6]  */
  6527  
  6528        execute(curr);
  6529        return next.proxy;
  6530        /*  [Promises/A+ 2.2.7, 3.3]  */
  6531      }
  6532    };
  6533    /*  deliver an action  */
  6534  
  6535    var deliver = function deliver(curr, state, name, value) {
  6536      if (curr.state === STATE_PENDING) {
  6537        curr.state = state;
  6538        /*  [Promises/A+ 2.1.2.1, 2.1.3.1]  */
  6539  
  6540        curr[name] = value;
  6541        /*  [Promises/A+ 2.1.2.2, 2.1.3.2]  */
  6542  
  6543        execute(curr);
  6544      }
  6545  
  6546      return curr;
  6547    };
  6548    /*  execute all handlers  */
  6549  
  6550  
  6551    var execute = function execute(curr) {
  6552      if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
  6553    };
  6554    /*  execute particular set of handlers  */
  6555  
  6556  
  6557    var execute_handlers = function execute_handlers(curr, name, value) {
  6558      /* global setImmediate: true */
  6559  
  6560      /* global setTimeout: true */
  6561  
  6562      /*  short-circuit processing  */
  6563      if (curr[name].length === 0) return;
  6564      /*  iterate over all handlers, exactly once  */
  6565  
  6566      var handlers = curr[name];
  6567      curr[name] = [];
  6568      /*  [Promises/A+ 2.2.2.3, 2.2.3.3]  */
  6569  
  6570      var func = function func() {
  6571        for (var i = 0; i < handlers.length; i++) {
  6572          handlers[i](value);
  6573        }
  6574        /*  [Promises/A+ 2.2.5]  */
  6575  
  6576      };
  6577      /*  execute procedure asynchronously  */
  6578  
  6579      /*  [Promises/A+ 2.2.4, 3.1]  */
  6580  
  6581  
  6582      if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
  6583    };
  6584    /*  generate a resolver function  */
  6585  
  6586  
  6587    var resolver = function resolver(cb, next, method) {
  6588      return function (value) {
  6589        if (typeof cb !== 'function')
  6590          /*  [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4]  */
  6591          next[method].call(next, value);
  6592          /*  [Promises/A+ 2.2.7.3, 2.2.7.4]  */
  6593        else {
  6594            var result;
  6595  
  6596            try {
  6597              result = cb(value);
  6598            }
  6599            /*  [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2]  */
  6600            catch (e) {
  6601              next.reject(e);
  6602              /*  [Promises/A+ 2.2.7.2]  */
  6603  
  6604              return;
  6605            }
  6606  
  6607            resolve(next, result);
  6608            /*  [Promises/A+ 2.2.7.1]  */
  6609          }
  6610      };
  6611    };
  6612    /*  "Promise Resolution Procedure"  */
  6613  
  6614    /*  [Promises/A+ 2.3]  */
  6615  
  6616  
  6617    var resolve = function resolve(promise, x) {
  6618      /*  sanity check arguments  */
  6619  
  6620      /*  [Promises/A+ 2.3.1]  */
  6621      if (promise === x || promise.proxy === x) {
  6622        promise.reject(new TypeError('cannot resolve promise with itself'));
  6623        return;
  6624      }
  6625      /*  surgically check for a "then" method
  6626        (mainly to just call the "getter" of "then" only once)  */
  6627  
  6628  
  6629      var then;
  6630  
  6631      if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
  6632        try {
  6633          then = x.then;
  6634        }
  6635        /*  [Promises/A+ 2.3.3.1, 3.5]  */
  6636        catch (e) {
  6637          promise.reject(e);
  6638          /*  [Promises/A+ 2.3.3.2]  */
  6639  
  6640          return;
  6641        }
  6642      }
  6643      /*  handle own Thenables    [Promises/A+ 2.3.2]
  6644        and similar "thenables" [Promises/A+ 2.3.3]  */
  6645  
  6646  
  6647      if (typeof then === 'function') {
  6648        var resolved = false;
  6649  
  6650        try {
  6651          /*  call retrieved "then" method */
  6652  
  6653          /*  [Promises/A+ 2.3.3.3]  */
  6654          then.call(x,
  6655          /*  resolvePromise  */
  6656  
  6657          /*  [Promises/A+ 2.3.3.3.1]  */
  6658          function (y) {
  6659            if (resolved) return;
  6660            resolved = true;
  6661            /*  [Promises/A+ 2.3.3.3.3]  */
  6662  
  6663            if (y === x)
  6664              /*  [Promises/A+ 3.6]  */
  6665              promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
  6666          },
  6667          /*  rejectPromise  */
  6668  
  6669          /*  [Promises/A+ 2.3.3.3.2]  */
  6670          function (r) {
  6671            if (resolved) return;
  6672            resolved = true;
  6673            /*  [Promises/A+ 2.3.3.3.3]  */
  6674  
  6675            promise.reject(r);
  6676          });
  6677        } catch (e) {
  6678          if (!resolved)
  6679            /*  [Promises/A+ 2.3.3.3.3]  */
  6680            promise.reject(e);
  6681          /*  [Promises/A+ 2.3.3.3.4]  */
  6682        }
  6683  
  6684        return;
  6685      }
  6686      /*  handle other values  */
  6687  
  6688  
  6689      promise.fulfill(x);
  6690      /*  [Promises/A+ 2.3.4, 2.3.3.4]  */
  6691    }; // so we always have Promise.all()
  6692  
  6693  
  6694    api.all = function (ps) {
  6695      return new api(function (resolveAll, rejectAll) {
  6696        var vals = new Array(ps.length);
  6697        var doneCount = 0;
  6698  
  6699        var fulfill = function fulfill(i, val) {
  6700          vals[i] = val;
  6701          doneCount++;
  6702  
  6703          if (doneCount === ps.length) {
  6704            resolveAll(vals);
  6705          }
  6706        };
  6707  
  6708        for (var i = 0; i < ps.length; i++) {
  6709          (function (i) {
  6710            var p = ps[i];
  6711            var isPromise = p != null && p.then != null;
  6712  
  6713            if (isPromise) {
  6714              p.then(function (val) {
  6715                fulfill(i, val);
  6716              }, function (err) {
  6717                rejectAll(err);
  6718              });
  6719            } else {
  6720              var val = p;
  6721              fulfill(i, val);
  6722            }
  6723          })(i);
  6724        }
  6725      });
  6726    };
  6727  
  6728    api.resolve = function (val) {
  6729      return new api(function (resolve, reject) {
  6730        resolve(val);
  6731      });
  6732    };
  6733  
  6734    api.reject = function (val) {
  6735      return new api(function (resolve, reject) {
  6736        reject(val);
  6737      });
  6738    };
  6739  
  6740    var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
  6741  
  6742    var Animation = function Animation(target, opts, opts2) {
  6743      var isCore = core(target);
  6744      var isEle = !isCore;
  6745  
  6746      var _p = this._private = extend({
  6747        duration: 1000
  6748      }, opts, opts2);
  6749  
  6750      _p.target = target;
  6751      _p.style = _p.style || _p.css;
  6752      _p.started = false;
  6753      _p.playing = false;
  6754      _p.hooked = false;
  6755      _p.applying = false;
  6756      _p.progress = 0;
  6757      _p.completes = [];
  6758      _p.frames = [];
  6759  
  6760      if (_p.complete && fn(_p.complete)) {
  6761        _p.completes.push(_p.complete);
  6762      }
  6763  
  6764      if (isEle) {
  6765        var pos = target.position();
  6766        _p.startPosition = _p.startPosition || {
  6767          x: pos.x,
  6768          y: pos.y
  6769        };
  6770        _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
  6771      }
  6772  
  6773      if (isCore) {
  6774        var pan = target.pan();
  6775        _p.startPan = {
  6776          x: pan.x,
  6777          y: pan.y
  6778        };
  6779        _p.startZoom = target.zoom();
  6780      } // for future timeline/animations impl
  6781  
  6782  
  6783      this.length = 1;
  6784      this[0] = this;
  6785    };
  6786  
  6787    var anifn = Animation.prototype;
  6788    extend(anifn, {
  6789      instanceString: function instanceString() {
  6790        return 'animation';
  6791      },
  6792      hook: function hook() {
  6793        var _p = this._private;
  6794  
  6795        if (!_p.hooked) {
  6796          // add to target's animation queue
  6797          var q;
  6798          var tAni = _p.target._private.animation;
  6799  
  6800          if (_p.queue) {
  6801            q = tAni.queue;
  6802          } else {
  6803            q = tAni.current;
  6804          }
  6805  
  6806          q.push(this); // add to the animation loop pool
  6807  
  6808          if (elementOrCollection(_p.target)) {
  6809            _p.target.cy().addToAnimationPool(_p.target);
  6810          }
  6811  
  6812          _p.hooked = true;
  6813        }
  6814  
  6815        return this;
  6816      },
  6817      play: function play() {
  6818        var _p = this._private; // autorewind
  6819  
  6820        if (_p.progress === 1) {
  6821          _p.progress = 0;
  6822        }
  6823  
  6824        _p.playing = true;
  6825        _p.started = false; // needs to be started by animation loop
  6826  
  6827        _p.stopped = false;
  6828        this.hook(); // the animation loop will start the animation...
  6829  
  6830        return this;
  6831      },
  6832      playing: function playing() {
  6833        return this._private.playing;
  6834      },
  6835      apply: function apply() {
  6836        var _p = this._private;
  6837        _p.applying = true;
  6838        _p.started = false; // needs to be started by animation loop
  6839  
  6840        _p.stopped = false;
  6841        this.hook(); // the animation loop will apply the animation at this progress
  6842  
  6843        return this;
  6844      },
  6845      applying: function applying() {
  6846        return this._private.applying;
  6847      },
  6848      pause: function pause() {
  6849        var _p = this._private;
  6850        _p.playing = false;
  6851        _p.started = false;
  6852        return this;
  6853      },
  6854      stop: function stop() {
  6855        var _p = this._private;
  6856        _p.playing = false;
  6857        _p.started = false;
  6858        _p.stopped = true; // to be removed from animation queues
  6859  
  6860        return this;
  6861      },
  6862      rewind: function rewind() {
  6863        return this.progress(0);
  6864      },
  6865      fastforward: function fastforward() {
  6866        return this.progress(1);
  6867      },
  6868      time: function time(t) {
  6869        var _p = this._private;
  6870  
  6871        if (t === undefined) {
  6872          return _p.progress * _p.duration;
  6873        } else {
  6874          return this.progress(t / _p.duration);
  6875        }
  6876      },
  6877      progress: function progress(p) {
  6878        var _p = this._private;
  6879        var wasPlaying = _p.playing;
  6880  
  6881        if (p === undefined) {
  6882          return _p.progress;
  6883        } else {
  6884          if (wasPlaying) {
  6885            this.pause();
  6886          }
  6887  
  6888          _p.progress = p;
  6889          _p.started = false;
  6890  
  6891          if (wasPlaying) {
  6892            this.play();
  6893          }
  6894        }
  6895  
  6896        return this;
  6897      },
  6898      completed: function completed() {
  6899        return this._private.progress === 1;
  6900      },
  6901      reverse: function reverse() {
  6902        var _p = this._private;
  6903        var wasPlaying = _p.playing;
  6904  
  6905        if (wasPlaying) {
  6906          this.pause();
  6907        }
  6908  
  6909        _p.progress = 1 - _p.progress;
  6910        _p.started = false;
  6911  
  6912        var swap = function swap(a, b) {
  6913          var _pa = _p[a];
  6914  
  6915          if (_pa == null) {
  6916            return;
  6917          }
  6918  
  6919          _p[a] = _p[b];
  6920          _p[b] = _pa;
  6921        };
  6922  
  6923        swap('zoom', 'startZoom');
  6924        swap('pan', 'startPan');
  6925        swap('position', 'startPosition'); // swap styles
  6926  
  6927        if (_p.style) {
  6928          for (var i = 0; i < _p.style.length; i++) {
  6929            var prop = _p.style[i];
  6930            var name = prop.name;
  6931            var startStyleProp = _p.startStyle[name];
  6932            _p.startStyle[name] = prop;
  6933            _p.style[i] = startStyleProp;
  6934          }
  6935        }
  6936  
  6937        if (wasPlaying) {
  6938          this.play();
  6939        }
  6940  
  6941        return this;
  6942      },
  6943      promise: function promise(type) {
  6944        var _p = this._private;
  6945        var arr;
  6946  
  6947        switch (type) {
  6948          case 'frame':
  6949            arr = _p.frames;
  6950            break;
  6951  
  6952          default:
  6953          case 'complete':
  6954          case 'completed':
  6955            arr = _p.completes;
  6956        }
  6957  
  6958        return new Promise$1(function (resolve, reject) {
  6959          arr.push(function () {
  6960            resolve();
  6961          });
  6962        });
  6963      }
  6964    });
  6965    anifn.complete = anifn.completed;
  6966    anifn.run = anifn.play;
  6967    anifn.running = anifn.playing;
  6968  
  6969    var define = {
  6970      animated: function animated() {
  6971        return function animatedImpl() {
  6972          var self = this;
  6973          var selfIsArrayLike = self.length !== undefined;
  6974          var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6975  
  6976          var cy = this._private.cy || this;
  6977  
  6978          if (!cy.styleEnabled()) {
  6979            return false;
  6980          }
  6981  
  6982          var ele = all[0];
  6983  
  6984          if (ele) {
  6985            return ele._private.animation.current.length > 0;
  6986          }
  6987        };
  6988      },
  6989      // animated
  6990      clearQueue: function clearQueue() {
  6991        return function clearQueueImpl() {
  6992          var self = this;
  6993          var selfIsArrayLike = self.length !== undefined;
  6994          var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  6995  
  6996          var cy = this._private.cy || this;
  6997  
  6998          if (!cy.styleEnabled()) {
  6999            return this;
  7000          }
  7001  
  7002          for (var i = 0; i < all.length; i++) {
  7003            var ele = all[i];
  7004            ele._private.animation.queue = [];
  7005          }
  7006  
  7007          return this;
  7008        };
  7009      },
  7010      // clearQueue
  7011      delay: function delay() {
  7012        return function delayImpl(time, complete) {
  7013          var cy = this._private.cy || this;
  7014  
  7015          if (!cy.styleEnabled()) {
  7016            return this;
  7017          }
  7018  
  7019          return this.animate({
  7020            delay: time,
  7021            duration: time,
  7022            complete: complete
  7023          });
  7024        };
  7025      },
  7026      // delay
  7027      delayAnimation: function delayAnimation() {
  7028        return function delayAnimationImpl(time, complete) {
  7029          var cy = this._private.cy || this;
  7030  
  7031          if (!cy.styleEnabled()) {
  7032            return this;
  7033          }
  7034  
  7035          return this.animation({
  7036            delay: time,
  7037            duration: time,
  7038            complete: complete
  7039          });
  7040        };
  7041      },
  7042      // delay
  7043      animation: function animation() {
  7044        return function animationImpl(properties, params) {
  7045          var self = this;
  7046          var selfIsArrayLike = self.length !== undefined;
  7047          var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  7048  
  7049          var cy = this._private.cy || this;
  7050          var isCore = !selfIsArrayLike;
  7051          var isEles = !isCore;
  7052  
  7053          if (!cy.styleEnabled()) {
  7054            return this;
  7055          }
  7056  
  7057          var style = cy.style();
  7058          properties = extend({}, properties, params);
  7059          var propertiesEmpty = Object.keys(properties).length === 0;
  7060  
  7061          if (propertiesEmpty) {
  7062            return new Animation(all[0], properties); // nothing to animate
  7063          }
  7064  
  7065          if (properties.duration === undefined) {
  7066            properties.duration = 400;
  7067          }
  7068  
  7069          switch (properties.duration) {
  7070            case 'slow':
  7071              properties.duration = 600;
  7072              break;
  7073  
  7074            case 'fast':
  7075              properties.duration = 200;
  7076              break;
  7077          }
  7078  
  7079          if (isEles) {
  7080            properties.style = style.getPropsList(properties.style || properties.css);
  7081            properties.css = undefined;
  7082          }
  7083  
  7084          if (isEles && properties.renderedPosition != null) {
  7085            var rpos = properties.renderedPosition;
  7086            var pan = cy.pan();
  7087            var zoom = cy.zoom();
  7088            properties.position = renderedToModelPosition(rpos, zoom, pan);
  7089          } // override pan w/ panBy if set
  7090  
  7091  
  7092          if (isCore && properties.panBy != null) {
  7093            var panBy = properties.panBy;
  7094            var cyPan = cy.pan();
  7095            properties.pan = {
  7096              x: cyPan.x + panBy.x,
  7097              y: cyPan.y + panBy.y
  7098            };
  7099          } // override pan w/ center if set
  7100  
  7101  
  7102          var center = properties.center || properties.centre;
  7103  
  7104          if (isCore && center != null) {
  7105            var centerPan = cy.getCenterPan(center.eles, properties.zoom);
  7106  
  7107            if (centerPan != null) {
  7108              properties.pan = centerPan;
  7109            }
  7110          } // override pan & zoom w/ fit if set
  7111  
  7112  
  7113          if (isCore && properties.fit != null) {
  7114            var fit = properties.fit;
  7115            var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
  7116  
  7117            if (fitVp != null) {
  7118              properties.pan = fitVp.pan;
  7119              properties.zoom = fitVp.zoom;
  7120            }
  7121          } // override zoom (& potentially pan) w/ zoom obj if set
  7122  
  7123  
  7124          if (isCore && plainObject(properties.zoom)) {
  7125            var vp = cy.getZoomedViewport(properties.zoom);
  7126  
  7127            if (vp != null) {
  7128              if (vp.zoomed) {
  7129                properties.zoom = vp.zoom;
  7130              }
  7131  
  7132              if (vp.panned) {
  7133                properties.pan = vp.pan;
  7134              }
  7135            } else {
  7136              properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
  7137            }
  7138          }
  7139  
  7140          return new Animation(all[0], properties);
  7141        };
  7142      },
  7143      // animate
  7144      animate: function animate() {
  7145        return function animateImpl(properties, params) {
  7146          var self = this;
  7147          var selfIsArrayLike = self.length !== undefined;
  7148          var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  7149  
  7150          var cy = this._private.cy || this;
  7151  
  7152          if (!cy.styleEnabled()) {
  7153            return this;
  7154          }
  7155  
  7156          if (params) {
  7157            properties = extend({}, properties, params);
  7158          } // manually hook and run the animation
  7159  
  7160  
  7161          for (var i = 0; i < all.length; i++) {
  7162            var ele = all[i];
  7163            var queue = ele.animated() && (properties.queue === undefined || properties.queue);
  7164            var ani = ele.animation(properties, queue ? {
  7165              queue: true
  7166            } : undefined);
  7167            ani.play();
  7168          }
  7169  
  7170          return this; // chaining
  7171        };
  7172      },
  7173      // animate
  7174      stop: function stop() {
  7175        return function stopImpl(clearQueue, jumpToEnd) {
  7176          var self = this;
  7177          var selfIsArrayLike = self.length !== undefined;
  7178          var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  7179  
  7180          var cy = this._private.cy || this;
  7181  
  7182          if (!cy.styleEnabled()) {
  7183            return this;
  7184          }
  7185  
  7186          for (var i = 0; i < all.length; i++) {
  7187            var ele = all[i];
  7188            var _p = ele._private;
  7189            var anis = _p.animation.current;
  7190  
  7191            for (var j = 0; j < anis.length; j++) {
  7192              var ani = anis[j];
  7193              var ani_p = ani._private;
  7194  
  7195              if (jumpToEnd) {
  7196                // next iteration of the animation loop, the animation
  7197                // will go straight to the end and be removed
  7198                ani_p.duration = 0;
  7199              }
  7200            } // clear the queue of future animations
  7201  
  7202  
  7203            if (clearQueue) {
  7204              _p.animation.queue = [];
  7205            }
  7206  
  7207            if (!jumpToEnd) {
  7208              _p.animation.current = [];
  7209            }
  7210          } // we have to notify (the animation loop doesn't do it for us on `stop`)
  7211  
  7212  
  7213          cy.notify('draw');
  7214          return this;
  7215        };
  7216      } // stop
  7217  
  7218    }; // define
  7219  
  7220    var define$1 = {
  7221      // access data field
  7222      data: function data(params) {
  7223        var defaults = {
  7224          field: 'data',
  7225          bindingEvent: 'data',
  7226          allowBinding: false,
  7227          allowSetting: false,
  7228          allowGetting: false,
  7229          settingEvent: 'data',
  7230          settingTriggersEvent: false,
  7231          triggerFnName: 'trigger',
  7232          immutableKeys: {},
  7233          // key => true if immutable
  7234          updateStyle: false,
  7235          beforeGet: function beforeGet(self) {},
  7236          beforeSet: function beforeSet(self, obj) {},
  7237          onSet: function onSet(self) {},
  7238          canSet: function canSet(self) {
  7239            return true;
  7240          }
  7241        };
  7242        params = extend({}, defaults, params);
  7243        return function dataImpl(name, value) {
  7244          var p = params;
  7245          var self = this;
  7246          var selfIsArrayLike = self.length !== undefined;
  7247          var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  7248  
  7249          var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
  7250  
  7251          if (string(name)) {
  7252            // set or get property
  7253            // .data('foo')
  7254            if (p.allowGetting && value === undefined) {
  7255              // get
  7256              var ret;
  7257  
  7258              if (single) {
  7259                p.beforeGet(single);
  7260                ret = single._private[p.field][name];
  7261              }
  7262  
  7263              return ret; // .data('foo', 'bar')
  7264            } else if (p.allowSetting && value !== undefined) {
  7265              // set
  7266              var valid = !p.immutableKeys[name];
  7267  
  7268              if (valid) {
  7269                var change = _defineProperty({}, name, value);
  7270  
  7271                p.beforeSet(self, change);
  7272  
  7273                for (var i = 0, l = all.length; i < l; i++) {
  7274                  var ele = all[i];
  7275  
  7276                  if (p.canSet(ele)) {
  7277                    ele._private[p.field][name] = value;
  7278                  }
  7279                } // update mappers if asked
  7280  
  7281  
  7282                if (p.updateStyle) {
  7283                  self.updateStyle();
  7284                } // call onSet callback
  7285  
  7286  
  7287                p.onSet(self);
  7288  
  7289                if (p.settingTriggersEvent) {
  7290                  self[p.triggerFnName](p.settingEvent);
  7291                }
  7292              }
  7293            } // .data({ 'foo': 'bar' })
  7294  
  7295          } else if (p.allowSetting && plainObject(name)) {
  7296            // extend
  7297            var obj = name;
  7298            var k, v;
  7299            var keys = Object.keys(obj);
  7300            p.beforeSet(self, obj);
  7301  
  7302            for (var _i = 0; _i < keys.length; _i++) {
  7303              k = keys[_i];
  7304              v = obj[k];
  7305  
  7306              var _valid = !p.immutableKeys[k];
  7307  
  7308              if (_valid) {
  7309                for (var j = 0; j < all.length; j++) {
  7310                  var _ele = all[j];
  7311  
  7312                  if (p.canSet(_ele)) {
  7313                    _ele._private[p.field][k] = v;
  7314                  }
  7315                }
  7316              }
  7317            } // update mappers if asked
  7318  
  7319  
  7320            if (p.updateStyle) {
  7321              self.updateStyle();
  7322            } // call onSet callback
  7323  
  7324  
  7325            p.onSet(self);
  7326  
  7327            if (p.settingTriggersEvent) {
  7328              self[p.triggerFnName](p.settingEvent);
  7329            } // .data(function(){ ... })
  7330  
  7331          } else if (p.allowBinding && fn(name)) {
  7332            // bind to event
  7333            var fn$1 = name;
  7334            self.on(p.bindingEvent, fn$1); // .data()
  7335          } else if (p.allowGetting && name === undefined) {
  7336            // get whole object
  7337            var _ret;
  7338  
  7339            if (single) {
  7340              p.beforeGet(single);
  7341              _ret = single._private[p.field];
  7342            }
  7343  
  7344            return _ret;
  7345          }
  7346  
  7347          return self; // maintain chainability
  7348        }; // function
  7349      },
  7350      // data
  7351      // remove data field
  7352      removeData: function removeData(params) {
  7353        var defaults = {
  7354          field: 'data',
  7355          event: 'data',
  7356          triggerFnName: 'trigger',
  7357          triggerEvent: false,
  7358          immutableKeys: {} // key => true if immutable
  7359  
  7360        };
  7361        params = extend({}, defaults, params);
  7362        return function removeDataImpl(names) {
  7363          var p = params;
  7364          var self = this;
  7365          var selfIsArrayLike = self.length !== undefined;
  7366          var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
  7367          // .removeData('foo bar')
  7368  
  7369          if (string(names)) {
  7370            // then get the list of keys, and delete them
  7371            var keys = names.split(/\s+/);
  7372            var l = keys.length;
  7373  
  7374            for (var i = 0; i < l; i++) {
  7375              // delete each non-empty key
  7376              var key = keys[i];
  7377  
  7378              if (emptyString(key)) {
  7379                continue;
  7380              }
  7381  
  7382              var valid = !p.immutableKeys[key]; // not valid if immutable
  7383  
  7384              if (valid) {
  7385                for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
  7386                  all[i_a]._private[p.field][key] = undefined;
  7387                }
  7388              }
  7389            }
  7390  
  7391            if (p.triggerEvent) {
  7392              self[p.triggerFnName](p.event);
  7393            } // .removeData()
  7394  
  7395          } else if (names === undefined) {
  7396            // then delete all keys
  7397            for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
  7398              var _privateFields = all[_i_a]._private[p.field];
  7399  
  7400              var _keys = Object.keys(_privateFields);
  7401  
  7402              for (var _i2 = 0; _i2 < _keys.length; _i2++) {
  7403                var _key = _keys[_i2];
  7404                var validKeyToDelete = !p.immutableKeys[_key];
  7405  
  7406                if (validKeyToDelete) {
  7407                  _privateFields[_key] = undefined;
  7408                }
  7409              }
  7410            }
  7411  
  7412            if (p.triggerEvent) {
  7413              self[p.triggerFnName](p.event);
  7414            }
  7415          }
  7416  
  7417          return self; // maintain chaining
  7418        }; // function
  7419      } // removeData
  7420  
  7421    }; // define
  7422  
  7423    var define$2 = {
  7424      eventAliasesOn: function eventAliasesOn(proto) {
  7425        var p = proto;
  7426        p.addListener = p.listen = p.bind = p.on;
  7427        p.unlisten = p.unbind = p.off = p.removeListener;
  7428        p.trigger = p.emit; // this is just a wrapper alias of .on()
  7429  
  7430        p.pon = p.promiseOn = function (events, selector) {
  7431          var self = this;
  7432          var args = Array.prototype.slice.call(arguments, 0);
  7433          return new Promise$1(function (resolve, reject) {
  7434            var callback = function callback(e) {
  7435              self.off.apply(self, offArgs);
  7436              resolve(e);
  7437            };
  7438  
  7439            var onArgs = args.concat([callback]);
  7440            var offArgs = onArgs.concat([]);
  7441            self.on.apply(self, onArgs);
  7442          });
  7443        };
  7444      }
  7445    }; // define
  7446  
  7447    // use this module to cherry pick functions into your prototype
  7448    var define$3 = {};
  7449    [define, define$1, define$2].forEach(function (m) {
  7450      extend(define$3, m);
  7451    });
  7452  
  7453    var elesfn$d = {
  7454      animate: define$3.animate(),
  7455      animation: define$3.animation(),
  7456      animated: define$3.animated(),
  7457      clearQueue: define$3.clearQueue(),
  7458      delay: define$3.delay(),
  7459      delayAnimation: define$3.delayAnimation(),
  7460      stop: define$3.stop()
  7461    };
  7462  
  7463    var elesfn$e = {
  7464      classes: function classes(_classes) {
  7465        var self = this;
  7466  
  7467        if (_classes === undefined) {
  7468          var ret = [];
  7469  
  7470          self[0]._private.classes.forEach(function (cls) {
  7471            return ret.push(cls);
  7472          });
  7473  
  7474          return ret;
  7475        } else if (!array(_classes)) {
  7476          // extract classes from string
  7477          _classes = (_classes || '').match(/\S+/g) || [];
  7478        }
  7479  
  7480        var changed = [];
  7481        var classesSet = new Set$1(_classes); // check and update each ele
  7482  
  7483        for (var j = 0; j < self.length; j++) {
  7484          var ele = self[j];
  7485          var _p = ele._private;
  7486          var eleClasses = _p.classes;
  7487          var changedEle = false; // check if ele has all of the passed classes
  7488  
  7489          for (var i = 0; i < _classes.length; i++) {
  7490            var cls = _classes[i];
  7491            var eleHasClass = eleClasses.has(cls);
  7492  
  7493            if (!eleHasClass) {
  7494              changedEle = true;
  7495              break;
  7496            }
  7497          } // check if ele has classes outside of those passed
  7498  
  7499  
  7500          if (!changedEle) {
  7501            changedEle = eleClasses.size !== _classes.length;
  7502          }
  7503  
  7504          if (changedEle) {
  7505            _p.classes = classesSet;
  7506            changed.push(ele);
  7507          }
  7508        } // trigger update style on those eles that had class changes
  7509  
  7510  
  7511        if (changed.length > 0) {
  7512          this.spawn(changed).updateStyle().emit('class');
  7513        }
  7514  
  7515        return self;
  7516      },
  7517      addClass: function addClass(classes) {
  7518        return this.toggleClass(classes, true);
  7519      },
  7520      hasClass: function hasClass(className) {
  7521        var ele = this[0];
  7522        return ele != null && ele._private.classes.has(className);
  7523      },
  7524      toggleClass: function toggleClass(classes, toggle) {
  7525        if (!array(classes)) {
  7526          // extract classes from string
  7527          classes = classes.match(/\S+/g) || [];
  7528        }
  7529  
  7530        var self = this;
  7531        var toggleUndefd = toggle === undefined;
  7532        var changed = []; // eles who had classes changed
  7533  
  7534        for (var i = 0, il = self.length; i < il; i++) {
  7535          var ele = self[i];
  7536          var eleClasses = ele._private.classes;
  7537          var changedEle = false;
  7538  
  7539          for (var j = 0; j < classes.length; j++) {
  7540            var cls = classes[j];
  7541            var hasClass = eleClasses.has(cls);
  7542            var changedNow = false;
  7543  
  7544            if (toggle || toggleUndefd && !hasClass) {
  7545              eleClasses.add(cls);
  7546              changedNow = true;
  7547            } else if (!toggle || toggleUndefd && hasClass) {
  7548              eleClasses["delete"](cls);
  7549              changedNow = true;
  7550            }
  7551  
  7552            if (!changedEle && changedNow) {
  7553              changed.push(ele);
  7554              changedEle = true;
  7555            }
  7556          } // for j classes
  7557  
  7558        } // for i eles
  7559        // trigger update style on those eles that had class changes
  7560  
  7561  
  7562        if (changed.length > 0) {
  7563          this.spawn(changed).updateStyle().emit('class');
  7564        }
  7565  
  7566        return self;
  7567      },
  7568      removeClass: function removeClass(classes) {
  7569        return this.toggleClass(classes, false);
  7570      },
  7571      flashClass: function flashClass(classes, duration) {
  7572        var self = this;
  7573  
  7574        if (duration == null) {
  7575          duration = 250;
  7576        } else if (duration === 0) {
  7577          return self; // nothing to do really
  7578        }
  7579  
  7580        self.addClass(classes);
  7581        setTimeout(function () {
  7582          self.removeClass(classes);
  7583        }, duration);
  7584        return self;
  7585      }
  7586    };
  7587    elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
  7588  
  7589    var tokens = {
  7590      metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
  7591      // chars we need to escape in let names, etc
  7592      comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
  7593      // binary comparison op (used in data selectors)
  7594      boolOp: '\\?|\\!|\\^',
  7595      // boolean (unary) operators (used in data selectors)
  7596      string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
  7597      // string literals (used in data selectors) -- doublequotes | singlequotes
  7598      number: number$1,
  7599      // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
  7600      meta: 'degree|indegree|outdegree',
  7601      // allowed metadata fields (i.e. allowed functions to use from Collection)
  7602      separator: '\\s*,\\s*',
  7603      // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
  7604      descendant: '\\s+',
  7605      child: '\\s+>\\s+',
  7606      subject: '\\$',
  7607      group: 'node|edge|\\*',
  7608      directedEdge: '\\s+->\\s+',
  7609      undirectedEdge: '\\s+<->\\s+'
  7610    };
  7611    tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name
  7612  
  7613    tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
  7614  
  7615    tokens.className = tokens.variable; // a class name (follows variable conventions)
  7616  
  7617    tokens.id = tokens.variable; // an element id (follows variable conventions)
  7618  
  7619    (function () {
  7620      var ops, op, i; // add @ variants to comparatorOp
  7621  
  7622      ops = tokens.comparatorOp.split('|');
  7623  
  7624      for (i = 0; i < ops.length; i++) {
  7625        op = ops[i];
  7626        tokens.comparatorOp += '|@' + op;
  7627      } // add ! variants to comparatorOp
  7628  
  7629  
  7630      ops = tokens.comparatorOp.split('|');
  7631  
  7632      for (i = 0; i < ops.length; i++) {
  7633        op = ops[i];
  7634  
  7635        if (op.indexOf('!') >= 0) {
  7636          continue;
  7637        } // skip ops that explicitly contain !
  7638  
  7639  
  7640        if (op === '=') {
  7641          continue;
  7642        } // skip = b/c != is explicitly defined
  7643  
  7644  
  7645        tokens.comparatorOp += '|\\!' + op;
  7646      }
  7647    })();
  7648  
  7649    /**
  7650     * Make a new query object
  7651     *
  7652     * @prop type {Type} The type enum (int) of the query
  7653     * @prop checks List of checks to make against an ele to test for a match
  7654     */
  7655    var newQuery = function newQuery() {
  7656      return {
  7657        checks: []
  7658      };
  7659    };
  7660  
  7661    /**
  7662     * A check type enum-like object.  Uses integer values for fast match() lookup.
  7663     * The ordering does not matter as long as the ints are unique.
  7664     */
  7665    var Type = {
  7666      /** E.g. node */
  7667      GROUP: 0,
  7668  
  7669      /** A collection of elements */
  7670      COLLECTION: 1,
  7671  
  7672      /** A filter(ele) function */
  7673      FILTER: 2,
  7674  
  7675      /** E.g. [foo > 1] */
  7676      DATA_COMPARE: 3,
  7677  
  7678      /** E.g. [foo] */
  7679      DATA_EXIST: 4,
  7680  
  7681      /** E.g. [?foo] */
  7682      DATA_BOOL: 5,
  7683  
  7684      /** E.g. [[degree > 2]] */
  7685      META_COMPARE: 6,
  7686  
  7687      /** E.g. :selected */
  7688      STATE: 7,
  7689  
  7690      /** E.g. #foo */
  7691      ID: 8,
  7692  
  7693      /** E.g. .foo */
  7694      CLASS: 9,
  7695  
  7696      /** E.g. #foo <-> #bar */
  7697      UNDIRECTED_EDGE: 10,
  7698  
  7699      /** E.g. #foo -> #bar */
  7700      DIRECTED_EDGE: 11,
  7701  
  7702      /** E.g. $#foo -> #bar */
  7703      NODE_SOURCE: 12,
  7704  
  7705      /** E.g. #foo -> $#bar */
  7706      NODE_TARGET: 13,
  7707  
  7708      /** E.g. $#foo <-> #bar */
  7709      NODE_NEIGHBOR: 14,
  7710  
  7711      /** E.g. #foo > #bar */
  7712      CHILD: 15,
  7713  
  7714      /** E.g. #foo #bar */
  7715      DESCENDANT: 16,
  7716  
  7717      /** E.g. $#foo > #bar */
  7718      PARENT: 17,
  7719  
  7720      /** E.g. $#foo #bar */
  7721      ANCESTOR: 18,
  7722  
  7723      /** E.g. #foo > $bar > #baz */
  7724      COMPOUND_SPLIT: 19,
  7725  
  7726      /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
  7727      TRUE: 20
  7728    };
  7729  
  7730    var stateSelectors = [{
  7731      selector: ':selected',
  7732      matches: function matches(ele) {
  7733        return ele.selected();
  7734      }
  7735    }, {
  7736      selector: ':unselected',
  7737      matches: function matches(ele) {
  7738        return !ele.selected();
  7739      }
  7740    }, {
  7741      selector: ':selectable',
  7742      matches: function matches(ele) {
  7743        return ele.selectable();
  7744      }
  7745    }, {
  7746      selector: ':unselectable',
  7747      matches: function matches(ele) {
  7748        return !ele.selectable();
  7749      }
  7750    }, {
  7751      selector: ':locked',
  7752      matches: function matches(ele) {
  7753        return ele.locked();
  7754      }
  7755    }, {
  7756      selector: ':unlocked',
  7757      matches: function matches(ele) {
  7758        return !ele.locked();
  7759      }
  7760    }, {
  7761      selector: ':visible',
  7762      matches: function matches(ele) {
  7763        return ele.visible();
  7764      }
  7765    }, {
  7766      selector: ':hidden',
  7767      matches: function matches(ele) {
  7768        return !ele.visible();
  7769      }
  7770    }, {
  7771      selector: ':transparent',
  7772      matches: function matches(ele) {
  7773        return ele.transparent();
  7774      }
  7775    }, {
  7776      selector: ':grabbed',
  7777      matches: function matches(ele) {
  7778        return ele.grabbed();
  7779      }
  7780    }, {
  7781      selector: ':free',
  7782      matches: function matches(ele) {
  7783        return !ele.grabbed();
  7784      }
  7785    }, {
  7786      selector: ':removed',
  7787      matches: function matches(ele) {
  7788        return ele.removed();
  7789      }
  7790    }, {
  7791      selector: ':inside',
  7792      matches: function matches(ele) {
  7793        return !ele.removed();
  7794      }
  7795    }, {
  7796      selector: ':grabbable',
  7797      matches: function matches(ele) {
  7798        return ele.grabbable();
  7799      }
  7800    }, {
  7801      selector: ':ungrabbable',
  7802      matches: function matches(ele) {
  7803        return !ele.grabbable();
  7804      }
  7805    }, {
  7806      selector: ':animated',
  7807      matches: function matches(ele) {
  7808        return ele.animated();
  7809      }
  7810    }, {
  7811      selector: ':unanimated',
  7812      matches: function matches(ele) {
  7813        return !ele.animated();
  7814      }
  7815    }, {
  7816      selector: ':parent',
  7817      matches: function matches(ele) {
  7818        return ele.isParent();
  7819      }
  7820    }, {
  7821      selector: ':childless',
  7822      matches: function matches(ele) {
  7823        return ele.isChildless();
  7824      }
  7825    }, {
  7826      selector: ':child',
  7827      matches: function matches(ele) {
  7828        return ele.isChild();
  7829      }
  7830    }, {
  7831      selector: ':orphan',
  7832      matches: function matches(ele) {
  7833        return ele.isOrphan();
  7834      }
  7835    }, {
  7836      selector: ':nonorphan',
  7837      matches: function matches(ele) {
  7838        return ele.isChild();
  7839      }
  7840    }, {
  7841      selector: ':compound',
  7842      matches: function matches(ele) {
  7843        if (ele.isNode()) {
  7844          return ele.isParent();
  7845        } else {
  7846          return ele.source().isParent() || ele.target().isParent();
  7847        }
  7848      }
  7849    }, {
  7850      selector: ':loop',
  7851      matches: function matches(ele) {
  7852        return ele.isLoop();
  7853      }
  7854    }, {
  7855      selector: ':simple',
  7856      matches: function matches(ele) {
  7857        return ele.isSimple();
  7858      }
  7859    }, {
  7860      selector: ':active',
  7861      matches: function matches(ele) {
  7862        return ele.active();
  7863      }
  7864    }, {
  7865      selector: ':inactive',
  7866      matches: function matches(ele) {
  7867        return !ele.active();
  7868      }
  7869    }, {
  7870      selector: ':backgrounding',
  7871      matches: function matches(ele) {
  7872        return ele.backgrounding();
  7873      }
  7874    }, {
  7875      selector: ':nonbackgrounding',
  7876      matches: function matches(ele) {
  7877        return !ele.backgrounding();
  7878      }
  7879    }].sort(function (a, b) {
  7880      // n.b. selectors that are starting substrings of others must have the longer ones first
  7881      return descending(a.selector, b.selector);
  7882    });
  7883  
  7884    var lookup = function () {
  7885      var selToFn = {};
  7886      var s;
  7887  
  7888      for (var i = 0; i < stateSelectors.length; i++) {
  7889        s = stateSelectors[i];
  7890        selToFn[s.selector] = s.matches;
  7891      }
  7892  
  7893      return selToFn;
  7894    }();
  7895  
  7896    var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
  7897      return lookup[sel](ele);
  7898    };
  7899    var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
  7900      return s.selector;
  7901    }).join('|') + ')';
  7902  
  7903    // so that values get compared properly in Selector.filter()
  7904  
  7905    var cleanMetaChars = function cleanMetaChars(str) {
  7906      return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
  7907        return $1;
  7908      });
  7909    };
  7910  
  7911    var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
  7912      selector[selector.length - 1] = replacementQuery;
  7913    }; // NOTE: add new expression syntax here to have it recognised by the parser;
  7914    // - a query contains all adjacent (i.e. no separator in between) expressions;
  7915    // - the current query is stored in selector[i]
  7916    // - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
  7917  
  7918  
  7919    var exprs = [{
  7920      name: 'group',
  7921      // just used for identifying when debugging
  7922      query: true,
  7923      regex: '(' + tokens.group + ')',
  7924      populate: function populate(selector, query, _ref) {
  7925        var _ref2 = _slicedToArray(_ref, 1),
  7926            group = _ref2[0];
  7927  
  7928        query.checks.push({
  7929          type: Type.GROUP,
  7930          value: group === '*' ? group : group + 's'
  7931        });
  7932      }
  7933    }, {
  7934      name: 'state',
  7935      query: true,
  7936      regex: stateSelectorRegex,
  7937      populate: function populate(selector, query, _ref3) {
  7938        var _ref4 = _slicedToArray(_ref3, 1),
  7939            state = _ref4[0];
  7940  
  7941        query.checks.push({
  7942          type: Type.STATE,
  7943          value: state
  7944        });
  7945      }
  7946    }, {
  7947      name: 'id',
  7948      query: true,
  7949      regex: '\\#(' + tokens.id + ')',
  7950      populate: function populate(selector, query, _ref5) {
  7951        var _ref6 = _slicedToArray(_ref5, 1),
  7952            id = _ref6[0];
  7953  
  7954        query.checks.push({
  7955          type: Type.ID,
  7956          value: cleanMetaChars(id)
  7957        });
  7958      }
  7959    }, {
  7960      name: 'className',
  7961      query: true,
  7962      regex: '\\.(' + tokens.className + ')',
  7963      populate: function populate(selector, query, _ref7) {
  7964        var _ref8 = _slicedToArray(_ref7, 1),
  7965            className = _ref8[0];
  7966  
  7967        query.checks.push({
  7968          type: Type.CLASS,
  7969          value: cleanMetaChars(className)
  7970        });
  7971      }
  7972    }, {
  7973      name: 'dataExists',
  7974      query: true,
  7975      regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
  7976      populate: function populate(selector, query, _ref9) {
  7977        var _ref10 = _slicedToArray(_ref9, 1),
  7978            variable = _ref10[0];
  7979  
  7980        query.checks.push({
  7981          type: Type.DATA_EXIST,
  7982          field: cleanMetaChars(variable)
  7983        });
  7984      }
  7985    }, {
  7986      name: 'dataCompare',
  7987      query: true,
  7988      regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
  7989      populate: function populate(selector, query, _ref11) {
  7990        var _ref12 = _slicedToArray(_ref11, 3),
  7991            variable = _ref12[0],
  7992            comparatorOp = _ref12[1],
  7993            value = _ref12[2];
  7994  
  7995        var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
  7996  
  7997        if (valueIsString) {
  7998          value = value.substring(1, value.length - 1);
  7999        } else {
  8000          value = parseFloat(value);
  8001        }
  8002  
  8003        query.checks.push({
  8004          type: Type.DATA_COMPARE,
  8005          field: cleanMetaChars(variable),
  8006          operator: comparatorOp,
  8007          value: value
  8008        });
  8009      }
  8010    }, {
  8011      name: 'dataBool',
  8012      query: true,
  8013      regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
  8014      populate: function populate(selector, query, _ref13) {
  8015        var _ref14 = _slicedToArray(_ref13, 2),
  8016            boolOp = _ref14[0],
  8017            variable = _ref14[1];
  8018  
  8019        query.checks.push({
  8020          type: Type.DATA_BOOL,
  8021          field: cleanMetaChars(variable),
  8022          operator: boolOp
  8023        });
  8024      }
  8025    }, {
  8026      name: 'metaCompare',
  8027      query: true,
  8028      regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
  8029      populate: function populate(selector, query, _ref15) {
  8030        var _ref16 = _slicedToArray(_ref15, 3),
  8031            meta = _ref16[0],
  8032            comparatorOp = _ref16[1],
  8033            number = _ref16[2];
  8034  
  8035        query.checks.push({
  8036          type: Type.META_COMPARE,
  8037          field: cleanMetaChars(meta),
  8038          operator: comparatorOp,
  8039          value: parseFloat(number)
  8040        });
  8041      }
  8042    }, {
  8043      name: 'nextQuery',
  8044      separator: true,
  8045      regex: tokens.separator,
  8046      populate: function populate(selector, query) {
  8047        var currentSubject = selector.currentSubject;
  8048        var edgeCount = selector.edgeCount;
  8049        var compoundCount = selector.compoundCount;
  8050        var lastQ = selector[selector.length - 1];
  8051  
  8052        if (currentSubject != null) {
  8053          lastQ.subject = currentSubject;
  8054          selector.currentSubject = null;
  8055        }
  8056  
  8057        lastQ.edgeCount = edgeCount;
  8058        lastQ.compoundCount = compoundCount;
  8059        selector.edgeCount = 0;
  8060        selector.compoundCount = 0; // go on to next query
  8061  
  8062        var nextQuery = selector[selector.length++] = newQuery();
  8063        return nextQuery; // this is the new query to be filled by the following exprs
  8064      }
  8065    }, {
  8066      name: 'directedEdge',
  8067      separator: true,
  8068      regex: tokens.directedEdge,
  8069      populate: function populate(selector, query) {
  8070        if (selector.currentSubject == null) {
  8071          // undirected edge
  8072          var edgeQuery = newQuery();
  8073          var source = query;
  8074          var target = newQuery();
  8075          edgeQuery.checks.push({
  8076            type: Type.DIRECTED_EDGE,
  8077            source: source,
  8078            target: target
  8079          }); // the query in the selector should be the edge rather than the source
  8080  
  8081          replaceLastQuery(selector, query, edgeQuery);
  8082          selector.edgeCount++; // we're now populating the target query with expressions that follow
  8083  
  8084          return target;
  8085        } else {
  8086          // source/target
  8087          var srcTgtQ = newQuery();
  8088          var _source = query;
  8089  
  8090          var _target = newQuery();
  8091  
  8092          srcTgtQ.checks.push({
  8093            type: Type.NODE_SOURCE,
  8094            source: _source,
  8095            target: _target
  8096          }); // the query in the selector should be the neighbourhood rather than the node
  8097  
  8098          replaceLastQuery(selector, query, srcTgtQ);
  8099          selector.edgeCount++;
  8100          return _target; // now populating the target with the following expressions
  8101        }
  8102      }
  8103    }, {
  8104      name: 'undirectedEdge',
  8105      separator: true,
  8106      regex: tokens.undirectedEdge,
  8107      populate: function populate(selector, query) {
  8108        if (selector.currentSubject == null) {
  8109          // undirected edge
  8110          var edgeQuery = newQuery();
  8111          var source = query;
  8112          var target = newQuery();
  8113          edgeQuery.checks.push({
  8114            type: Type.UNDIRECTED_EDGE,
  8115            nodes: [source, target]
  8116          }); // the query in the selector should be the edge rather than the source
  8117  
  8118          replaceLastQuery(selector, query, edgeQuery);
  8119          selector.edgeCount++; // we're now populating the target query with expressions that follow
  8120  
  8121          return target;
  8122        } else {
  8123          // neighbourhood
  8124          var nhoodQ = newQuery();
  8125          var node = query;
  8126          var neighbor = newQuery();
  8127          nhoodQ.checks.push({
  8128            type: Type.NODE_NEIGHBOR,
  8129            node: node,
  8130            neighbor: neighbor
  8131          }); // the query in the selector should be the neighbourhood rather than the node
  8132  
  8133          replaceLastQuery(selector, query, nhoodQ);
  8134          return neighbor; // now populating the neighbor with following expressions
  8135        }
  8136      }
  8137    }, {
  8138      name: 'child',
  8139      separator: true,
  8140      regex: tokens.child,
  8141      populate: function populate(selector, query) {
  8142        if (selector.currentSubject == null) {
  8143          // default: child query
  8144          var parentChildQuery = newQuery();
  8145          var child = newQuery();
  8146          var parent = selector[selector.length - 1];
  8147          parentChildQuery.checks.push({
  8148            type: Type.CHILD,
  8149            parent: parent,
  8150            child: child
  8151          }); // the query in the selector should be the '>' itself
  8152  
  8153          replaceLastQuery(selector, query, parentChildQuery);
  8154          selector.compoundCount++; // we're now populating the child query with expressions that follow
  8155  
  8156          return child;
  8157        } else if (selector.currentSubject === query) {
  8158          // compound split query
  8159          var compound = newQuery();
  8160          var left = selector[selector.length - 1];
  8161          var right = newQuery();
  8162          var subject = newQuery();
  8163  
  8164          var _child = newQuery();
  8165  
  8166          var _parent = newQuery(); // set up the root compound q
  8167  
  8168  
  8169          compound.checks.push({
  8170            type: Type.COMPOUND_SPLIT,
  8171            left: left,
  8172            right: right,
  8173            subject: subject
  8174          }); // populate the subject and replace the q at the old spot (within left) with TRUE
  8175  
  8176          subject.checks = query.checks; // take the checks from the left
  8177  
  8178          query.checks = [{
  8179            type: Type.TRUE
  8180          }]; // checks under left refs the subject implicitly
  8181          // set up the right q
  8182  
  8183          _parent.checks.push({
  8184            type: Type.TRUE
  8185          }); // parent implicitly refs the subject
  8186  
  8187  
  8188          right.checks.push({
  8189            type: Type.PARENT,
  8190            // type is swapped on right side queries
  8191            parent: _parent,
  8192            child: _child // empty for now
  8193  
  8194          });
  8195          replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
  8196  
  8197          selector.currentSubject = subject;
  8198          selector.compoundCount++;
  8199          return _child; // now populating the right side's child
  8200        } else {
  8201          // parent query
  8202          // info for parent query
  8203          var _parent2 = newQuery();
  8204  
  8205          var _child2 = newQuery();
  8206  
  8207          var pcQChecks = [{
  8208            type: Type.PARENT,
  8209            parent: _parent2,
  8210            child: _child2
  8211          }]; // the parent-child query takes the place of the query previously being populated
  8212  
  8213          _parent2.checks = query.checks; // the previous query contains the checks for the parent
  8214  
  8215          query.checks = pcQChecks; // pc query takes over
  8216  
  8217          selector.compoundCount++;
  8218          return _child2; // we're now populating the child
  8219        }
  8220      }
  8221    }, {
  8222      name: 'descendant',
  8223      separator: true,
  8224      regex: tokens.descendant,
  8225      populate: function populate(selector, query) {
  8226        if (selector.currentSubject == null) {
  8227          // default: descendant query
  8228          var ancChQuery = newQuery();
  8229          var descendant = newQuery();
  8230          var ancestor = selector[selector.length - 1];
  8231          ancChQuery.checks.push({
  8232            type: Type.DESCENDANT,
  8233            ancestor: ancestor,
  8234            descendant: descendant
  8235          }); // the query in the selector should be the '>' itself
  8236  
  8237          replaceLastQuery(selector, query, ancChQuery);
  8238          selector.compoundCount++; // we're now populating the descendant query with expressions that follow
  8239  
  8240          return descendant;
  8241        } else if (selector.currentSubject === query) {
  8242          // compound split query
  8243          var compound = newQuery();
  8244          var left = selector[selector.length - 1];
  8245          var right = newQuery();
  8246          var subject = newQuery();
  8247  
  8248          var _descendant = newQuery();
  8249  
  8250          var _ancestor = newQuery(); // set up the root compound q
  8251  
  8252  
  8253          compound.checks.push({
  8254            type: Type.COMPOUND_SPLIT,
  8255            left: left,
  8256            right: right,
  8257            subject: subject
  8258          }); // populate the subject and replace the q at the old spot (within left) with TRUE
  8259  
  8260          subject.checks = query.checks; // take the checks from the left
  8261  
  8262          query.checks = [{
  8263            type: Type.TRUE
  8264          }]; // checks under left refs the subject implicitly
  8265          // set up the right q
  8266  
  8267          _ancestor.checks.push({
  8268            type: Type.TRUE
  8269          }); // ancestor implicitly refs the subject
  8270  
  8271  
  8272          right.checks.push({
  8273            type: Type.ANCESTOR,
  8274            // type is swapped on right side queries
  8275            ancestor: _ancestor,
  8276            descendant: _descendant // empty for now
  8277  
  8278          });
  8279          replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
  8280  
  8281          selector.currentSubject = subject;
  8282          selector.compoundCount++;
  8283          return _descendant; // now populating the right side's descendant
  8284        } else {
  8285          // ancestor query
  8286          // info for parent query
  8287          var _ancestor2 = newQuery();
  8288  
  8289          var _descendant2 = newQuery();
  8290  
  8291          var adQChecks = [{
  8292            type: Type.ANCESTOR,
  8293            ancestor: _ancestor2,
  8294            descendant: _descendant2
  8295          }]; // the parent-child query takes the place of the query previously being populated
  8296  
  8297          _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
  8298  
  8299          query.checks = adQChecks; // pc query takes over
  8300  
  8301          selector.compoundCount++;
  8302          return _descendant2; // we're now populating the child
  8303        }
  8304      }
  8305    }, {
  8306      name: 'subject',
  8307      modifier: true,
  8308      regex: tokens.subject,
  8309      populate: function populate(selector, query) {
  8310        if (selector.currentSubject != null && selector.currentSubject !== query) {
  8311          warn('Redefinition of subject in selector `' + selector.toString() + '`');
  8312          return false;
  8313        }
  8314  
  8315        selector.currentSubject = query;
  8316        var topQ = selector[selector.length - 1];
  8317        var topChk = topQ.checks[0];
  8318        var topType = topChk == null ? null : topChk.type;
  8319  
  8320        if (topType === Type.DIRECTED_EDGE) {
  8321          // directed edge with subject on the target
  8322          // change to target node check
  8323          topChk.type = Type.NODE_TARGET;
  8324        } else if (topType === Type.UNDIRECTED_EDGE) {
  8325          // undirected edge with subject on the second node
  8326          // change to neighbor check
  8327          topChk.type = Type.NODE_NEIGHBOR;
  8328          topChk.node = topChk.nodes[1]; // second node is subject
  8329  
  8330          topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
  8331  
  8332          topChk.nodes = null;
  8333        }
  8334      }
  8335    }];
  8336    exprs.forEach(function (e) {
  8337      return e.regexObj = new RegExp('^' + e.regex);
  8338    });
  8339  
  8340    /**
  8341     * Of all the expressions, find the first match in the remaining text.
  8342     * @param {string} remaining The remaining text to parse
  8343     * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
  8344     */
  8345  
  8346    var consumeExpr = function consumeExpr(remaining) {
  8347      var expr;
  8348      var match;
  8349      var name;
  8350  
  8351      for (var j = 0; j < exprs.length; j++) {
  8352        var e = exprs[j];
  8353        var n = e.name;
  8354        var m = remaining.match(e.regexObj);
  8355  
  8356        if (m != null) {
  8357          match = m;
  8358          expr = e;
  8359          name = n;
  8360          var consumed = m[0];
  8361          remaining = remaining.substring(consumed.length);
  8362          break; // we've consumed one expr, so we can return now
  8363        }
  8364      }
  8365  
  8366      return {
  8367        expr: expr,
  8368        match: match,
  8369        name: name,
  8370        remaining: remaining
  8371      };
  8372    };
  8373    /**
  8374     * Consume all the leading whitespace
  8375     * @param {string} remaining The text to consume
  8376     * @returns The text with the leading whitespace removed
  8377     */
  8378  
  8379  
  8380    var consumeWhitespace = function consumeWhitespace(remaining) {
  8381      var match = remaining.match(/^\s+/);
  8382  
  8383      if (match) {
  8384        var consumed = match[0];
  8385        remaining = remaining.substring(consumed.length);
  8386      }
  8387  
  8388      return remaining;
  8389    };
  8390    /**
  8391     * Parse the string and store the parsed representation in the Selector.
  8392     * @param {string} selector The selector string
  8393     * @returns `true` if the selector was successfully parsed, `false` otherwise
  8394     */
  8395  
  8396  
  8397    var parse = function parse(selector) {
  8398      var self = this;
  8399      var remaining = self.inputText = selector;
  8400      var currentQuery = self[0] = newQuery();
  8401      self.length = 1;
  8402      remaining = consumeWhitespace(remaining); // get rid of leading whitespace
  8403  
  8404      for (;;) {
  8405        var exprInfo = consumeExpr(remaining);
  8406  
  8407        if (exprInfo.expr == null) {
  8408          warn('The selector `' + selector + '`is invalid');
  8409          return false;
  8410        } else {
  8411          var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
  8412  
  8413          var ret = exprInfo.expr.populate(self, currentQuery, args);
  8414  
  8415          if (ret === false) {
  8416            return false; // exit if population failed
  8417          } else if (ret != null) {
  8418            currentQuery = ret; // change the current query to be filled if the expr specifies
  8419          }
  8420        }
  8421  
  8422        remaining = exprInfo.remaining; // we're done when there's nothing left to parse
  8423  
  8424        if (remaining.match(/^\s*$/)) {
  8425          break;
  8426        }
  8427      }
  8428  
  8429      var lastQ = self[self.length - 1];
  8430  
  8431      if (self.currentSubject != null) {
  8432        lastQ.subject = self.currentSubject;
  8433      }
  8434  
  8435      lastQ.edgeCount = self.edgeCount;
  8436      lastQ.compoundCount = self.compoundCount;
  8437  
  8438      for (var i = 0; i < self.length; i++) {
  8439        var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
  8440  
  8441        if (q.compoundCount > 0 && q.edgeCount > 0) {
  8442          warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
  8443          return false;
  8444        }
  8445  
  8446        if (q.edgeCount > 1) {
  8447          warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
  8448          return false;
  8449        } else if (q.edgeCount === 1) {
  8450          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.');
  8451        }
  8452      }
  8453  
  8454      return true; // success
  8455    };
  8456    /**
  8457     * Get the selector represented as a string.  This value uses default formatting,
  8458     * so things like spacing may differ from the input text passed to the constructor.
  8459     * @returns {string} The selector string
  8460     */
  8461  
  8462  
  8463    var toString = function toString() {
  8464      if (this.toStringCache != null) {
  8465        return this.toStringCache;
  8466      }
  8467  
  8468      var clean = function clean(obj) {
  8469        if (obj == null) {
  8470          return '';
  8471        } else {
  8472          return obj;
  8473        }
  8474      };
  8475  
  8476      var cleanVal = function cleanVal(val) {
  8477        if (string(val)) {
  8478          return '"' + val + '"';
  8479        } else {
  8480          return clean(val);
  8481        }
  8482      };
  8483  
  8484      var space = function space(val) {
  8485        return ' ' + val + ' ';
  8486      };
  8487  
  8488      var checkToString = function checkToString(check, subject) {
  8489        var type = check.type,
  8490            value = check.value;
  8491  
  8492        switch (type) {
  8493          case Type.GROUP:
  8494            {
  8495              var group = clean(value);
  8496              return group.substring(0, group.length - 1);
  8497            }
  8498  
  8499          case Type.DATA_COMPARE:
  8500            {
  8501              var field = check.field,
  8502                  operator = check.operator;
  8503              return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
  8504            }
  8505  
  8506          case Type.DATA_BOOL:
  8507            {
  8508              var _operator = check.operator,
  8509                  _field = check.field;
  8510              return '[' + clean(_operator) + _field + ']';
  8511            }
  8512  
  8513          case Type.DATA_EXIST:
  8514            {
  8515              var _field2 = check.field;
  8516              return '[' + _field2 + ']';
  8517            }
  8518  
  8519          case Type.META_COMPARE:
  8520            {
  8521              var _operator2 = check.operator,
  8522                  _field3 = check.field;
  8523              return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
  8524            }
  8525  
  8526          case Type.STATE:
  8527            {
  8528              return value;
  8529            }
  8530  
  8531          case Type.ID:
  8532            {
  8533              return '#' + value;
  8534            }
  8535  
  8536          case Type.CLASS:
  8537            {
  8538              return '.' + value;
  8539            }
  8540  
  8541          case Type.PARENT:
  8542          case Type.CHILD:
  8543            {
  8544              return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
  8545            }
  8546  
  8547          case Type.ANCESTOR:
  8548          case Type.DESCENDANT:
  8549            {
  8550              return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
  8551            }
  8552  
  8553          case Type.COMPOUND_SPLIT:
  8554            {
  8555              var lhs = queryToString(check.left, subject);
  8556              var sub = queryToString(check.subject, subject);
  8557              var rhs = queryToString(check.right, subject);
  8558              return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
  8559            }
  8560  
  8561          case Type.TRUE:
  8562            {
  8563              return '';
  8564            }
  8565        }
  8566      };
  8567  
  8568      var queryToString = function queryToString(query, subject) {
  8569        return query.checks.reduce(function (str, chk, i) {
  8570          return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
  8571        }, '');
  8572      };
  8573  
  8574      var str = '';
  8575  
  8576      for (var i = 0; i < this.length; i++) {
  8577        var query = this[i];
  8578        str += queryToString(query, query.subject);
  8579  
  8580        if (this.length > 1 && i < this.length - 1) {
  8581          str += ', ';
  8582        }
  8583      }
  8584  
  8585      this.toStringCache = str;
  8586      return str;
  8587    };
  8588    var parse$1 = {
  8589      parse: parse,
  8590      toString: toString
  8591    };
  8592  
  8593    var valCmp = function valCmp(fieldVal, operator, value) {
  8594      var matches;
  8595      var isFieldStr = string(fieldVal);
  8596      var isFieldNum = number(fieldVal);
  8597      var isValStr = string(value);
  8598      var fieldStr, valStr;
  8599      var caseInsensitive = false;
  8600      var notExpr = false;
  8601      var isIneqCmp = false;
  8602  
  8603      if (operator.indexOf('!') >= 0) {
  8604        operator = operator.replace('!', '');
  8605        notExpr = true;
  8606      }
  8607  
  8608      if (operator.indexOf('@') >= 0) {
  8609        operator = operator.replace('@', '');
  8610        caseInsensitive = true;
  8611      }
  8612  
  8613      if (isFieldStr || isValStr || caseInsensitive) {
  8614        fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
  8615        valStr = '' + value;
  8616      } // if we're doing a case insensitive comparison, then we're using a STRING comparison
  8617      // even if we're comparing numbers
  8618  
  8619  
  8620      if (caseInsensitive) {
  8621        fieldVal = fieldStr = fieldStr.toLowerCase();
  8622        value = valStr = valStr.toLowerCase();
  8623      }
  8624  
  8625      switch (operator) {
  8626        case '*=':
  8627          matches = fieldStr.indexOf(valStr) >= 0;
  8628          break;
  8629  
  8630        case '$=':
  8631          matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
  8632          break;
  8633  
  8634        case '^=':
  8635          matches = fieldStr.indexOf(valStr) === 0;
  8636          break;
  8637  
  8638        case '=':
  8639          matches = fieldVal === value;
  8640          break;
  8641  
  8642        case '>':
  8643          isIneqCmp = true;
  8644          matches = fieldVal > value;
  8645          break;
  8646  
  8647        case '>=':
  8648          isIneqCmp = true;
  8649          matches = fieldVal >= value;
  8650          break;
  8651  
  8652        case '<':
  8653          isIneqCmp = true;
  8654          matches = fieldVal < value;
  8655          break;
  8656  
  8657        case '<=':
  8658          isIneqCmp = true;
  8659          matches = fieldVal <= value;
  8660          break;
  8661  
  8662        default:
  8663          matches = false;
  8664          break;
  8665      } // apply the not op, but null vals for inequalities should always stay non-matching
  8666  
  8667  
  8668      if (notExpr && (fieldVal != null || !isIneqCmp)) {
  8669        matches = !matches;
  8670      }
  8671  
  8672      return matches;
  8673    };
  8674    var boolCmp = function boolCmp(fieldVal, operator) {
  8675      switch (operator) {
  8676        case '?':
  8677          return fieldVal ? true : false;
  8678  
  8679        case '!':
  8680          return fieldVal ? false : true;
  8681  
  8682        case '^':
  8683          return fieldVal === undefined;
  8684      }
  8685    };
  8686    var existCmp = function existCmp(fieldVal) {
  8687      return fieldVal !== undefined;
  8688    };
  8689    var data = function data(ele, field) {
  8690      return ele.data(field);
  8691    };
  8692    var meta = function meta(ele, field) {
  8693      return ele[field]();
  8694    };
  8695  
  8696    /** A lookup of `match(check, ele)` functions by `Type` int */
  8697  
  8698    var match = [];
  8699    /**
  8700     * Returns whether the query matches for the element
  8701     * @param query The `{ type, value, ... }` query object
  8702     * @param ele The element to compare against
  8703    */
  8704  
  8705    var matches = function matches(query, ele) {
  8706      return query.checks.every(function (chk) {
  8707        return match[chk.type](chk, ele);
  8708      });
  8709    };
  8710  
  8711    match[Type.GROUP] = function (check, ele) {
  8712      var group = check.value;
  8713      return group === '*' || group === ele.group();
  8714    };
  8715  
  8716    match[Type.STATE] = function (check, ele) {
  8717      var stateSelector = check.value;
  8718      return stateSelectorMatches(stateSelector, ele);
  8719    };
  8720  
  8721    match[Type.ID] = function (check, ele) {
  8722      var id = check.value;
  8723      return ele.id() === id;
  8724    };
  8725  
  8726    match[Type.CLASS] = function (check, ele) {
  8727      var cls = check.value;
  8728      return ele.hasClass(cls);
  8729    };
  8730  
  8731    match[Type.META_COMPARE] = function (check, ele) {
  8732      var field = check.field,
  8733          operator = check.operator,
  8734          value = check.value;
  8735      return valCmp(meta(ele, field), operator, value);
  8736    };
  8737  
  8738    match[Type.DATA_COMPARE] = function (check, ele) {
  8739      var field = check.field,
  8740          operator = check.operator,
  8741          value = check.value;
  8742      return valCmp(data(ele, field), operator, value);
  8743    };
  8744  
  8745    match[Type.DATA_BOOL] = function (check, ele) {
  8746      var field = check.field,
  8747          operator = check.operator;
  8748      return boolCmp(data(ele, field), operator);
  8749    };
  8750  
  8751    match[Type.DATA_EXIST] = function (check, ele) {
  8752      var field = check.field,
  8753          operator = check.operator;
  8754      return existCmp(data(ele, field));
  8755    };
  8756  
  8757    match[Type.UNDIRECTED_EDGE] = function (check, ele) {
  8758      var qA = check.nodes[0];
  8759      var qB = check.nodes[1];
  8760      var src = ele.source();
  8761      var tgt = ele.target();
  8762      return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
  8763    };
  8764  
  8765    match[Type.NODE_NEIGHBOR] = function (check, ele) {
  8766      return matches(check.node, ele) && ele.neighborhood().some(function (n) {
  8767        return n.isNode() && matches(check.neighbor, n);
  8768      });
  8769    };
  8770  
  8771    match[Type.DIRECTED_EDGE] = function (check, ele) {
  8772      return matches(check.source, ele.source()) && matches(check.target, ele.target());
  8773    };
  8774  
  8775    match[Type.NODE_SOURCE] = function (check, ele) {
  8776      return matches(check.source, ele) && ele.outgoers().some(function (n) {
  8777        return n.isNode() && matches(check.target, n);
  8778      });
  8779    };
  8780  
  8781    match[Type.NODE_TARGET] = function (check, ele) {
  8782      return matches(check.target, ele) && ele.incomers().some(function (n) {
  8783        return n.isNode() && matches(check.source, n);
  8784      });
  8785    };
  8786  
  8787    match[Type.CHILD] = function (check, ele) {
  8788      return matches(check.child, ele) && matches(check.parent, ele.parent());
  8789    };
  8790  
  8791    match[Type.PARENT] = function (check, ele) {
  8792      return matches(check.parent, ele) && ele.children().some(function (c) {
  8793        return matches(check.child, c);
  8794      });
  8795    };
  8796  
  8797    match[Type.DESCENDANT] = function (check, ele) {
  8798      return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
  8799        return matches(check.ancestor, a);
  8800      });
  8801    };
  8802  
  8803    match[Type.ANCESTOR] = function (check, ele) {
  8804      return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
  8805        return matches(check.descendant, d);
  8806      });
  8807    };
  8808  
  8809    match[Type.COMPOUND_SPLIT] = function (check, ele) {
  8810      return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
  8811    };
  8812  
  8813    match[Type.TRUE] = function () {
  8814      return true;
  8815    };
  8816  
  8817    match[Type.COLLECTION] = function (check, ele) {
  8818      var collection = check.value;
  8819      return collection.has(ele);
  8820    };
  8821  
  8822    match[Type.FILTER] = function (check, ele) {
  8823      var filter = check.value;
  8824      return filter(ele);
  8825    };
  8826  
  8827    var filter = function filter(collection) {
  8828      var self = this; // for 1 id #foo queries, just get the element
  8829  
  8830      if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
  8831        return collection.getElementById(self[0].checks[0].value).collection();
  8832      }
  8833  
  8834      var selectorFunction = function selectorFunction(element) {
  8835        for (var j = 0; j < self.length; j++) {
  8836          var query = self[j];
  8837  
  8838          if (matches(query, element)) {
  8839            return true;
  8840          }
  8841        }
  8842  
  8843        return false;
  8844      };
  8845  
  8846      if (self.text() == null) {
  8847        selectorFunction = function selectorFunction() {
  8848          return true;
  8849        };
  8850      }
  8851  
  8852      return collection.filter(selectorFunction);
  8853    }; // filter
  8854    // does selector match a single element?
  8855  
  8856  
  8857    var matches$1 = function matches$1(ele) {
  8858      var self = this;
  8859  
  8860      for (var j = 0; j < self.length; j++) {
  8861        var query = self[j];
  8862  
  8863        if (matches(query, ele)) {
  8864          return true;
  8865        }
  8866      }
  8867  
  8868      return false;
  8869    }; // matches
  8870  
  8871  
  8872    var matching = {
  8873      matches: matches$1,
  8874      filter: filter
  8875    };
  8876  
  8877    var Selector = function Selector(selector) {
  8878      this.inputText = selector;
  8879      this.currentSubject = null;
  8880      this.compoundCount = 0;
  8881      this.edgeCount = 0;
  8882      this.length = 0;
  8883  
  8884      if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
  8885        this.addQuery({
  8886          checks: [{
  8887            type: Type.COLLECTION,
  8888            value: selector.collection()
  8889          }]
  8890        });
  8891      } else if (fn(selector)) {
  8892        this.addQuery({
  8893          checks: [{
  8894            type: Type.FILTER,
  8895            value: selector
  8896          }]
  8897        });
  8898      } else if (string(selector)) {
  8899        if (!this.parse(selector)) {
  8900          this.invalid = true;
  8901        }
  8902      } else {
  8903        error('A selector must be created from a string; found ');
  8904      }
  8905    };
  8906  
  8907    var selfn = Selector.prototype;
  8908    [parse$1, matching].forEach(function (p) {
  8909      return extend(selfn, p);
  8910    });
  8911  
  8912    selfn.text = function () {
  8913      return this.inputText;
  8914    };
  8915  
  8916    selfn.size = function () {
  8917      return this.length;
  8918    };
  8919  
  8920    selfn.eq = function (i) {
  8921      return this[i];
  8922    };
  8923  
  8924    selfn.sameText = function (otherSel) {
  8925      return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
  8926    };
  8927  
  8928    selfn.addQuery = function (q) {
  8929      this[this.length++] = q;
  8930    };
  8931  
  8932    selfn.selector = selfn.toString;
  8933  
  8934    var elesfn$f = {
  8935      allAre: function allAre(selector) {
  8936        var selObj = new Selector(selector);
  8937        return this.every(function (ele) {
  8938          return selObj.matches(ele);
  8939        });
  8940      },
  8941      is: function is(selector) {
  8942        var selObj = new Selector(selector);
  8943        return this.some(function (ele) {
  8944          return selObj.matches(ele);
  8945        });
  8946      },
  8947      some: function some(fn, thisArg) {
  8948        for (var i = 0; i < this.length; i++) {
  8949          var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
  8950  
  8951          if (ret) {
  8952            return true;
  8953          }
  8954        }
  8955  
  8956        return false;
  8957      },
  8958      every: function every(fn, thisArg) {
  8959        for (var i = 0; i < this.length; i++) {
  8960          var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
  8961  
  8962          if (!ret) {
  8963            return false;
  8964          }
  8965        }
  8966  
  8967        return true;
  8968      },
  8969      same: function same(collection) {
  8970        // cheap collection ref check
  8971        if (this === collection) {
  8972          return true;
  8973        }
  8974  
  8975        collection = this.cy().collection(collection);
  8976        var thisLength = this.length;
  8977        var collectionLength = collection.length; // cheap length check
  8978  
  8979        if (thisLength !== collectionLength) {
  8980          return false;
  8981        } // cheap element ref check
  8982  
  8983  
  8984        if (thisLength === 1) {
  8985          return this[0] === collection[0];
  8986        }
  8987  
  8988        return this.every(function (ele) {
  8989          return collection.hasElementWithId(ele.id());
  8990        });
  8991      },
  8992      anySame: function anySame(collection) {
  8993        collection = this.cy().collection(collection);
  8994        return this.some(function (ele) {
  8995          return collection.hasElementWithId(ele.id());
  8996        });
  8997      },
  8998      allAreNeighbors: function allAreNeighbors(collection) {
  8999        collection = this.cy().collection(collection);
  9000        var nhood = this.neighborhood();
  9001        return collection.every(function (ele) {
  9002          return nhood.hasElementWithId(ele.id());
  9003        });
  9004      },
  9005      contains: function contains(collection) {
  9006        collection = this.cy().collection(collection);
  9007        var self = this;
  9008        return collection.every(function (ele) {
  9009          return self.hasElementWithId(ele.id());
  9010        });
  9011      }
  9012    };
  9013    elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
  9014    elesfn$f.has = elesfn$f.contains;
  9015    elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
  9016  
  9017    var cache = function cache(fn, name) {
  9018      return function traversalCache(arg1, arg2, arg3, arg4) {
  9019        var selectorOrEles = arg1;
  9020        var eles = this;
  9021        var key;
  9022  
  9023        if (selectorOrEles == null) {
  9024          key = '';
  9025        } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
  9026          key = selectorOrEles.id();
  9027        }
  9028  
  9029        if (eles.length === 1 && key) {
  9030          var _p = eles[0]._private;
  9031          var tch = _p.traversalCache = _p.traversalCache || {};
  9032          var ch = tch[name] = tch[name] || [];
  9033          var hash = hashString(key);
  9034          var cacheHit = ch[hash];
  9035  
  9036          if (cacheHit) {
  9037            return cacheHit;
  9038          } else {
  9039            return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
  9040          }
  9041        } else {
  9042          return fn.call(eles, arg1, arg2, arg3, arg4);
  9043        }
  9044      };
  9045    };
  9046  
  9047    var elesfn$g = {
  9048      parent: function parent(selector) {
  9049        var parents = []; // optimisation for single ele call
  9050  
  9051        if (this.length === 1) {
  9052          var parent = this[0]._private.parent;
  9053  
  9054          if (parent) {
  9055            return parent;
  9056          }
  9057        }
  9058  
  9059        for (var i = 0; i < this.length; i++) {
  9060          var ele = this[i];
  9061          var _parent = ele._private.parent;
  9062  
  9063          if (_parent) {
  9064            parents.push(_parent);
  9065          }
  9066        }
  9067  
  9068        return this.spawn(parents, {
  9069          unique: true
  9070        }).filter(selector);
  9071      },
  9072      parents: function parents(selector) {
  9073        var parents = [];
  9074        var eles = this.parent();
  9075  
  9076        while (eles.nonempty()) {
  9077          for (var i = 0; i < eles.length; i++) {
  9078            var ele = eles[i];
  9079            parents.push(ele);
  9080          }
  9081  
  9082          eles = eles.parent();
  9083        }
  9084  
  9085        return this.spawn(parents, {
  9086          unique: true
  9087        }).filter(selector);
  9088      },
  9089      commonAncestors: function commonAncestors(selector) {
  9090        var ancestors;
  9091  
  9092        for (var i = 0; i < this.length; i++) {
  9093          var ele = this[i];
  9094          var parents = ele.parents();
  9095          ancestors = ancestors || parents;
  9096          ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
  9097        }
  9098  
  9099        return ancestors.filter(selector);
  9100      },
  9101      orphans: function orphans(selector) {
  9102        return this.stdFilter(function (ele) {
  9103          return ele.isOrphan();
  9104        }).filter(selector);
  9105      },
  9106      nonorphans: function nonorphans(selector) {
  9107        return this.stdFilter(function (ele) {
  9108          return ele.isChild();
  9109        }).filter(selector);
  9110      },
  9111      children: cache(function (selector) {
  9112        var children = [];
  9113  
  9114        for (var i = 0; i < this.length; i++) {
  9115          var ele = this[i];
  9116          var eleChildren = ele._private.children;
  9117  
  9118          for (var j = 0; j < eleChildren.length; j++) {
  9119            children.push(eleChildren[j]);
  9120          }
  9121        }
  9122  
  9123        return this.spawn(children, {
  9124          unique: true
  9125        }).filter(selector);
  9126      }, 'children'),
  9127      siblings: function siblings(selector) {
  9128        return this.parent().children().not(this).filter(selector);
  9129      },
  9130      isParent: function isParent() {
  9131        var ele = this[0];
  9132  
  9133        if (ele) {
  9134          return ele.isNode() && ele._private.children.length !== 0;
  9135        }
  9136      },
  9137      isChildless: function isChildless() {
  9138        var ele = this[0];
  9139  
  9140        if (ele) {
  9141          return ele.isNode() && ele._private.children.length === 0;
  9142        }
  9143      },
  9144      isChild: function isChild() {
  9145        var ele = this[0];
  9146  
  9147        if (ele) {
  9148          return ele.isNode() && ele._private.parent != null;
  9149        }
  9150      },
  9151      isOrphan: function isOrphan() {
  9152        var ele = this[0];
  9153  
  9154        if (ele) {
  9155          return ele.isNode() && ele._private.parent == null;
  9156        }
  9157      },
  9158      descendants: function descendants(selector) {
  9159        var elements = [];
  9160  
  9161        function add(eles) {
  9162          for (var i = 0; i < eles.length; i++) {
  9163            var ele = eles[i];
  9164            elements.push(ele);
  9165  
  9166            if (ele.children().nonempty()) {
  9167              add(ele.children());
  9168            }
  9169          }
  9170        }
  9171  
  9172        add(this.children());
  9173        return this.spawn(elements, {
  9174          unique: true
  9175        }).filter(selector);
  9176      }
  9177    };
  9178  
  9179    function forEachCompound(eles, fn, includeSelf, recursiveStep) {
  9180      var q = [];
  9181      var did = new Set$1();
  9182      var cy = eles.cy();
  9183      var hasCompounds = cy.hasCompoundNodes();
  9184  
  9185      for (var i = 0; i < eles.length; i++) {
  9186        var ele = eles[i];
  9187  
  9188        if (includeSelf) {
  9189          q.push(ele);
  9190        } else if (hasCompounds) {
  9191          recursiveStep(q, did, ele);
  9192        }
  9193      }
  9194  
  9195      while (q.length > 0) {
  9196        var _ele = q.shift();
  9197  
  9198        fn(_ele);
  9199        did.add(_ele.id());
  9200  
  9201        if (hasCompounds) {
  9202          recursiveStep(q, did, _ele);
  9203        }
  9204      }
  9205  
  9206      return eles;
  9207    }
  9208  
  9209    function addChildren(q, did, ele) {
  9210      if (ele.isParent()) {
  9211        var children = ele._private.children;
  9212  
  9213        for (var i = 0; i < children.length; i++) {
  9214          var child = children[i];
  9215  
  9216          if (!did.has(child.id())) {
  9217            q.push(child);
  9218          }
  9219        }
  9220      }
  9221    } // very efficient version of eles.add( eles.descendants() ).forEach()
  9222    // for internal use
  9223  
  9224  
  9225    elesfn$g.forEachDown = function (fn) {
  9226      var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  9227      return forEachCompound(this, fn, includeSelf, addChildren);
  9228    };
  9229  
  9230    function addParent(q, did, ele) {
  9231      if (ele.isChild()) {
  9232        var parent = ele._private.parent;
  9233  
  9234        if (!did.has(parent.id())) {
  9235          q.push(parent);
  9236        }
  9237      }
  9238    }
  9239  
  9240    elesfn$g.forEachUp = function (fn) {
  9241      var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  9242      return forEachCompound(this, fn, includeSelf, addParent);
  9243    };
  9244  
  9245    function addParentAndChildren(q, did, ele) {
  9246      addParent(q, did, ele);
  9247      addChildren(q, did, ele);
  9248    }
  9249  
  9250    elesfn$g.forEachUpAndDown = function (fn) {
  9251      var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  9252      return forEachCompound(this, fn, includeSelf, addParentAndChildren);
  9253    }; // aliases
  9254  
  9255  
  9256    elesfn$g.ancestors = elesfn$g.parents;
  9257  
  9258    var fn$1, elesfn$h;
  9259    fn$1 = elesfn$h = {
  9260      data: define$3.data({
  9261        field: 'data',
  9262        bindingEvent: 'data',
  9263        allowBinding: true,
  9264        allowSetting: true,
  9265        settingEvent: 'data',
  9266        settingTriggersEvent: true,
  9267        triggerFnName: 'trigger',
  9268        allowGetting: true,
  9269        immutableKeys: {
  9270          'id': true,
  9271          'source': true,
  9272          'target': true,
  9273          'parent': true
  9274        },
  9275        updateStyle: true
  9276      }),
  9277      removeData: define$3.removeData({
  9278        field: 'data',
  9279        event: 'data',
  9280        triggerFnName: 'trigger',
  9281        triggerEvent: true,
  9282        immutableKeys: {
  9283          'id': true,
  9284          'source': true,
  9285          'target': true,
  9286          'parent': true
  9287        },
  9288        updateStyle: true
  9289      }),
  9290      scratch: define$3.data({
  9291        field: 'scratch',
  9292        bindingEvent: 'scratch',
  9293        allowBinding: true,
  9294        allowSetting: true,
  9295        settingEvent: 'scratch',
  9296        settingTriggersEvent: true,
  9297        triggerFnName: 'trigger',
  9298        allowGetting: true,
  9299        updateStyle: true
  9300      }),
  9301      removeScratch: define$3.removeData({
  9302        field: 'scratch',
  9303        event: 'scratch',
  9304        triggerFnName: 'trigger',
  9305        triggerEvent: true,
  9306        updateStyle: true
  9307      }),
  9308      rscratch: define$3.data({
  9309        field: 'rscratch',
  9310        allowBinding: false,
  9311        allowSetting: true,
  9312        settingTriggersEvent: false,
  9313        allowGetting: true
  9314      }),
  9315      removeRscratch: define$3.removeData({
  9316        field: 'rscratch',
  9317        triggerEvent: false
  9318      }),
  9319      id: function id() {
  9320        var ele = this[0];
  9321  
  9322        if (ele) {
  9323          return ele._private.data.id;
  9324        }
  9325      }
  9326    }; // aliases
  9327  
  9328    fn$1.attr = fn$1.data;
  9329    fn$1.removeAttr = fn$1.removeData;
  9330    var data$1 = elesfn$h;
  9331  
  9332    var elesfn$i = {};
  9333  
  9334    function defineDegreeFunction(callback) {
  9335      return function (includeLoops) {
  9336        var self = this;
  9337  
  9338        if (includeLoops === undefined) {
  9339          includeLoops = true;
  9340        }
  9341  
  9342        if (self.length === 0) {
  9343          return;
  9344        }
  9345  
  9346        if (self.isNode() && !self.removed()) {
  9347          var degree = 0;
  9348          var node = self[0];
  9349          var connectedEdges = node._private.edges;
  9350  
  9351          for (var i = 0; i < connectedEdges.length; i++) {
  9352            var edge = connectedEdges[i];
  9353  
  9354            if (!includeLoops && edge.isLoop()) {
  9355              continue;
  9356            }
  9357  
  9358            degree += callback(node, edge);
  9359          }
  9360  
  9361          return degree;
  9362        } else {
  9363          return;
  9364        }
  9365      };
  9366    }
  9367  
  9368    extend(elesfn$i, {
  9369      degree: defineDegreeFunction(function (node, edge) {
  9370        if (edge.source().same(edge.target())) {
  9371          return 2;
  9372        } else {
  9373          return 1;
  9374        }
  9375      }),
  9376      indegree: defineDegreeFunction(function (node, edge) {
  9377        if (edge.target().same(node)) {
  9378          return 1;
  9379        } else {
  9380          return 0;
  9381        }
  9382      }),
  9383      outdegree: defineDegreeFunction(function (node, edge) {
  9384        if (edge.source().same(node)) {
  9385          return 1;
  9386        } else {
  9387          return 0;
  9388        }
  9389      })
  9390    });
  9391  
  9392    function defineDegreeBoundsFunction(degreeFn, callback) {
  9393      return function (includeLoops) {
  9394        var ret;
  9395        var nodes = this.nodes();
  9396  
  9397        for (var i = 0; i < nodes.length; i++) {
  9398          var ele = nodes[i];
  9399          var degree = ele[degreeFn](includeLoops);
  9400  
  9401          if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
  9402            ret = degree;
  9403          }
  9404        }
  9405  
  9406        return ret;
  9407      };
  9408    }
  9409  
  9410    extend(elesfn$i, {
  9411      minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
  9412        return degree < min;
  9413      }),
  9414      maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
  9415        return degree > max;
  9416      }),
  9417      minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
  9418        return degree < min;
  9419      }),
  9420      maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
  9421        return degree > max;
  9422      }),
  9423      minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
  9424        return degree < min;
  9425      }),
  9426      maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
  9427        return degree > max;
  9428      })
  9429    });
  9430    extend(elesfn$i, {
  9431      totalDegree: function totalDegree(includeLoops) {
  9432        var total = 0;
  9433        var nodes = this.nodes();
  9434  
  9435        for (var i = 0; i < nodes.length; i++) {
  9436          total += nodes[i].degree(includeLoops);
  9437        }
  9438  
  9439        return total;
  9440      }
  9441    });
  9442  
  9443    var fn$2, elesfn$j;
  9444  
  9445    var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
  9446      for (var i = 0; i < eles.length; i++) {
  9447        var ele = eles[i];
  9448  
  9449        if (!ele.locked()) {
  9450          var oldPos = ele._private.position;
  9451          var delta = {
  9452            x: newPos.x != null ? newPos.x - oldPos.x : 0,
  9453            y: newPos.y != null ? newPos.y - oldPos.y : 0
  9454          };
  9455  
  9456          if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
  9457            ele.children().shift(delta, silent);
  9458          }
  9459  
  9460          ele.shiftCachedBoundingBox(delta);
  9461        }
  9462      }
  9463    };
  9464  
  9465    var positionDef = {
  9466      field: 'position',
  9467      bindingEvent: 'position',
  9468      allowBinding: true,
  9469      allowSetting: true,
  9470      settingEvent: 'position',
  9471      settingTriggersEvent: true,
  9472      triggerFnName: 'emitAndNotify',
  9473      allowGetting: true,
  9474      validKeys: ['x', 'y'],
  9475      beforeGet: function beforeGet(ele) {
  9476        ele.updateCompoundBounds();
  9477      },
  9478      beforeSet: function beforeSet(eles, newPos) {
  9479        beforePositionSet(eles, newPos, false);
  9480      },
  9481      onSet: function onSet(eles) {
  9482        eles.dirtyCompoundBoundsCache();
  9483      },
  9484      canSet: function canSet(ele) {
  9485        return !ele.locked();
  9486      }
  9487    };
  9488    fn$2 = elesfn$j = {
  9489      position: define$3.data(positionDef),
  9490      // position but no notification to renderer
  9491      silentPosition: define$3.data(extend({}, positionDef, {
  9492        allowBinding: false,
  9493        allowSetting: true,
  9494        settingTriggersEvent: false,
  9495        allowGetting: false,
  9496        beforeSet: function beforeSet(eles, newPos) {
  9497          beforePositionSet(eles, newPos, true);
  9498        }
  9499      })),
  9500      positions: function positions(pos, silent) {
  9501        if (plainObject(pos)) {
  9502          if (silent) {
  9503            this.silentPosition(pos);
  9504          } else {
  9505            this.position(pos);
  9506          }
  9507        } else if (fn(pos)) {
  9508          var _fn = pos;
  9509          var cy = this.cy();
  9510          cy.startBatch();
  9511  
  9512          for (var i = 0; i < this.length; i++) {
  9513            var ele = this[i];
  9514  
  9515            var _pos = void 0;
  9516  
  9517            if (_pos = _fn(ele, i)) {
  9518              if (silent) {
  9519                ele.silentPosition(_pos);
  9520              } else {
  9521                ele.position(_pos);
  9522              }
  9523            }
  9524          }
  9525  
  9526          cy.endBatch();
  9527        }
  9528  
  9529        return this; // chaining
  9530      },
  9531      silentPositions: function silentPositions(pos) {
  9532        return this.positions(pos, true);
  9533      },
  9534      shift: function shift(dim, val, silent) {
  9535        var delta;
  9536  
  9537        if (plainObject(dim)) {
  9538          delta = {
  9539            x: number(dim.x) ? dim.x : 0,
  9540            y: number(dim.y) ? dim.y : 0
  9541          };
  9542          silent = val;
  9543        } else if (string(dim) && number(val)) {
  9544          delta = {
  9545            x: 0,
  9546            y: 0
  9547          };
  9548          delta[dim] = val;
  9549        }
  9550  
  9551        if (delta != null) {
  9552          var cy = this.cy();
  9553          cy.startBatch();
  9554  
  9555          for (var i = 0; i < this.length; i++) {
  9556            var ele = this[i];
  9557            var pos = ele.position();
  9558            var newPos = {
  9559              x: pos.x + delta.x,
  9560              y: pos.y + delta.y
  9561            };
  9562  
  9563            if (silent) {
  9564              ele.silentPosition(newPos);
  9565            } else {
  9566              ele.position(newPos);
  9567            }
  9568          }
  9569  
  9570          cy.endBatch();
  9571        }
  9572  
  9573        return this;
  9574      },
  9575      silentShift: function silentShift(dim, val) {
  9576        if (plainObject(dim)) {
  9577          this.shift(dim, true);
  9578        } else if (string(dim) && number(val)) {
  9579          this.shift(dim, val, true);
  9580        }
  9581  
  9582        return this;
  9583      },
  9584      // get/set the rendered (i.e. on screen) positon of the element
  9585      renderedPosition: function renderedPosition(dim, val) {
  9586        var ele = this[0];
  9587        var cy = this.cy();
  9588        var zoom = cy.zoom();
  9589        var pan = cy.pan();
  9590        var rpos = plainObject(dim) ? dim : undefined;
  9591        var setting = rpos !== undefined || val !== undefined && string(dim);
  9592  
  9593        if (ele && ele.isNode()) {
  9594          // must have an element and must be a node to return position
  9595          if (setting) {
  9596            for (var i = 0; i < this.length; i++) {
  9597              var _ele = this[i];
  9598  
  9599              if (val !== undefined) {
  9600                // set one dimension
  9601                _ele.position(dim, (val - pan[dim]) / zoom);
  9602              } else if (rpos !== undefined) {
  9603                // set whole position
  9604                _ele.position(renderedToModelPosition(rpos, zoom, pan));
  9605              }
  9606            }
  9607          } else {
  9608            // getting
  9609            var pos = ele.position();
  9610            rpos = modelToRenderedPosition(pos, zoom, pan);
  9611  
  9612            if (dim === undefined) {
  9613              // then return the whole rendered position
  9614              return rpos;
  9615            } else {
  9616              // then return the specified dimension
  9617              return rpos[dim];
  9618            }
  9619          }
  9620        } else if (!setting) {
  9621          return undefined; // for empty collection case
  9622        }
  9623  
  9624        return this; // chaining
  9625      },
  9626      // get/set the position relative to the parent
  9627      relativePosition: function relativePosition(dim, val) {
  9628        var ele = this[0];
  9629        var cy = this.cy();
  9630        var ppos = plainObject(dim) ? dim : undefined;
  9631        var setting = ppos !== undefined || val !== undefined && string(dim);
  9632        var hasCompoundNodes = cy.hasCompoundNodes();
  9633  
  9634        if (ele && ele.isNode()) {
  9635          // must have an element and must be a node to return position
  9636          if (setting) {
  9637            for (var i = 0; i < this.length; i++) {
  9638              var _ele2 = this[i];
  9639              var parent = hasCompoundNodes ? _ele2.parent() : null;
  9640              var hasParent = parent && parent.length > 0;
  9641              var relativeToParent = hasParent;
  9642  
  9643              if (hasParent) {
  9644                parent = parent[0];
  9645              }
  9646  
  9647              var origin = relativeToParent ? parent.position() : {
  9648                x: 0,
  9649                y: 0
  9650              };
  9651  
  9652              if (val !== undefined) {
  9653                // set one dimension
  9654                _ele2.position(dim, val + origin[dim]);
  9655              } else if (ppos !== undefined) {
  9656                // set whole position
  9657                _ele2.position({
  9658                  x: ppos.x + origin.x,
  9659                  y: ppos.y + origin.y
  9660                });
  9661              }
  9662            }
  9663          } else {
  9664            // getting
  9665            var pos = ele.position();
  9666  
  9667            var _parent = hasCompoundNodes ? ele.parent() : null;
  9668  
  9669            var _hasParent = _parent && _parent.length > 0;
  9670  
  9671            var _relativeToParent = _hasParent;
  9672  
  9673            if (_hasParent) {
  9674              _parent = _parent[0];
  9675            }
  9676  
  9677            var _origin = _relativeToParent ? _parent.position() : {
  9678              x: 0,
  9679              y: 0
  9680            };
  9681  
  9682            ppos = {
  9683              x: pos.x - _origin.x,
  9684              y: pos.y - _origin.y
  9685            };
  9686  
  9687            if (dim === undefined) {
  9688              // then return the whole rendered position
  9689              return ppos;
  9690            } else {
  9691              // then return the specified dimension
  9692              return ppos[dim];
  9693            }
  9694          }
  9695        } else if (!setting) {
  9696          return undefined; // for empty collection case
  9697        }
  9698  
  9699        return this; // chaining
  9700      }
  9701    }; // aliases
  9702  
  9703    fn$2.modelPosition = fn$2.point = fn$2.position;
  9704    fn$2.modelPositions = fn$2.points = fn$2.positions;
  9705    fn$2.renderedPoint = fn$2.renderedPosition;
  9706    fn$2.relativePoint = fn$2.relativePosition;
  9707    var position = elesfn$j;
  9708  
  9709    var fn$3, elesfn$k;
  9710    fn$3 = elesfn$k = {};
  9711  
  9712    elesfn$k.renderedBoundingBox = function (options) {
  9713      var bb = this.boundingBox(options);
  9714      var cy = this.cy();
  9715      var zoom = cy.zoom();
  9716      var pan = cy.pan();
  9717      var x1 = bb.x1 * zoom + pan.x;
  9718      var x2 = bb.x2 * zoom + pan.x;
  9719      var y1 = bb.y1 * zoom + pan.y;
  9720      var y2 = bb.y2 * zoom + pan.y;
  9721      return {
  9722        x1: x1,
  9723        x2: x2,
  9724        y1: y1,
  9725        y2: y2,
  9726        w: x2 - x1,
  9727        h: y2 - y1
  9728      };
  9729    };
  9730  
  9731    elesfn$k.dirtyCompoundBoundsCache = function () {
  9732      var cy = this.cy();
  9733  
  9734      if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
  9735        return this;
  9736      }
  9737  
  9738      this.forEachUp(function (ele) {
  9739        if (ele.isParent()) {
  9740          var _p = ele._private;
  9741          _p.compoundBoundsClean = false;
  9742          _p.bbCache = null;
  9743          ele.emitAndNotify('bounds');
  9744        }
  9745      });
  9746      return this;
  9747    };
  9748  
  9749    elesfn$k.updateCompoundBounds = function () {
  9750      var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  9751      var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
  9752  
  9753      if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
  9754        return this;
  9755      } // save cycles when batching -- but bounds will be stale (or not exist yet)
  9756  
  9757  
  9758      if (!force && cy.batching()) {
  9759        return this;
  9760      }
  9761  
  9762      function update(parent) {
  9763        if (!parent.isParent()) {
  9764          return;
  9765        }
  9766  
  9767        var _p = parent._private;
  9768        var children = parent.children();
  9769        var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
  9770        var min = {
  9771          width: {
  9772            val: parent.pstyle('min-width').pfValue,
  9773            left: parent.pstyle('min-width-bias-left'),
  9774            right: parent.pstyle('min-width-bias-right')
  9775          },
  9776          height: {
  9777            val: parent.pstyle('min-height').pfValue,
  9778            top: parent.pstyle('min-height-bias-top'),
  9779            bottom: parent.pstyle('min-height-bias-bottom')
  9780          }
  9781        };
  9782        var bb = children.boundingBox({
  9783          includeLabels: includeLabels,
  9784          includeOverlays: false,
  9785          // updating the compound bounds happens outside of the regular
  9786          // cache cycle (i.e. before fired events)
  9787          useCache: false
  9788        });
  9789        var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
  9790  
  9791        if (bb.w === 0 || bb.h === 0) {
  9792          bb = {
  9793            w: parent.pstyle('width').pfValue,
  9794            h: parent.pstyle('height').pfValue
  9795          };
  9796          bb.x1 = pos.x - bb.w / 2;
  9797          bb.x2 = pos.x + bb.w / 2;
  9798          bb.y1 = pos.y - bb.h / 2;
  9799          bb.y2 = pos.y + bb.h / 2;
  9800        }
  9801  
  9802        function computeBiasValues(propDiff, propBias, propBiasComplement) {
  9803          var biasDiff = 0;
  9804          var biasComplementDiff = 0;
  9805          var biasTotal = propBias + propBiasComplement;
  9806  
  9807          if (propDiff > 0 && biasTotal > 0) {
  9808            biasDiff = propBias / biasTotal * propDiff;
  9809            biasComplementDiff = propBiasComplement / biasTotal * propDiff;
  9810          }
  9811  
  9812          return {
  9813            biasDiff: biasDiff,
  9814            biasComplementDiff: biasComplementDiff
  9815          };
  9816        }
  9817  
  9818        function computePaddingValues(width, height, paddingObject, relativeTo) {
  9819          // Assuming percentage is number from 0 to 1
  9820          if (paddingObject.units === '%') {
  9821            switch (relativeTo) {
  9822              case 'width':
  9823                return width > 0 ? paddingObject.pfValue * width : 0;
  9824  
  9825              case 'height':
  9826                return height > 0 ? paddingObject.pfValue * height : 0;
  9827  
  9828              case 'average':
  9829                return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
  9830  
  9831              case 'min':
  9832                return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
  9833  
  9834              case 'max':
  9835                return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
  9836  
  9837              default:
  9838                return 0;
  9839            }
  9840          } else if (paddingObject.units === 'px') {
  9841            return paddingObject.pfValue;
  9842          } else {
  9843            return 0;
  9844          }
  9845        }
  9846  
  9847        var leftVal = min.width.left.value;
  9848  
  9849        if (min.width.left.units === 'px' && min.width.val > 0) {
  9850          leftVal = leftVal * 100 / min.width.val;
  9851        }
  9852  
  9853        var rightVal = min.width.right.value;
  9854  
  9855        if (min.width.right.units === 'px' && min.width.val > 0) {
  9856          rightVal = rightVal * 100 / min.width.val;
  9857        }
  9858  
  9859        var topVal = min.height.top.value;
  9860  
  9861        if (min.height.top.units === 'px' && min.height.val > 0) {
  9862          topVal = topVal * 100 / min.height.val;
  9863        }
  9864  
  9865        var bottomVal = min.height.bottom.value;
  9866  
  9867        if (min.height.bottom.units === 'px' && min.height.val > 0) {
  9868          bottomVal = bottomVal * 100 / min.height.val;
  9869        }
  9870  
  9871        var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
  9872        var diffLeft = widthBiasDiffs.biasDiff;
  9873        var diffRight = widthBiasDiffs.biasComplementDiff;
  9874        var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
  9875        var diffTop = heightBiasDiffs.biasDiff;
  9876        var diffBottom = heightBiasDiffs.biasComplementDiff;
  9877        _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
  9878        _p.autoWidth = Math.max(bb.w, min.width.val);
  9879        pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
  9880        _p.autoHeight = Math.max(bb.h, min.height.val);
  9881        pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
  9882      }
  9883  
  9884      for (var i = 0; i < this.length; i++) {
  9885        var ele = this[i];
  9886        var _p = ele._private;
  9887  
  9888        if (!_p.compoundBoundsClean) {
  9889          update(ele);
  9890  
  9891          if (!cy.batching()) {
  9892            _p.compoundBoundsClean = true;
  9893          }
  9894        }
  9895      }
  9896  
  9897      return this;
  9898    };
  9899  
  9900    var noninf = function noninf(x) {
  9901      if (x === Infinity || x === -Infinity) {
  9902        return 0;
  9903      }
  9904  
  9905      return x;
  9906    };
  9907  
  9908    var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
  9909      // don't update with zero area boxes
  9910      if (x2 - x1 === 0 || y2 - y1 === 0) {
  9911        return;
  9912      } // don't update with null dim
  9913  
  9914  
  9915      if (x1 == null || y1 == null || x2 == null || y2 == null) {
  9916        return;
  9917      }
  9918  
  9919      b.x1 = x1 < b.x1 ? x1 : b.x1;
  9920      b.x2 = x2 > b.x2 ? x2 : b.x2;
  9921      b.y1 = y1 < b.y1 ? y1 : b.y1;
  9922      b.y2 = y2 > b.y2 ? y2 : b.y2;
  9923      b.w = b.x2 - b.x1;
  9924      b.h = b.y2 - b.y1;
  9925    };
  9926  
  9927    var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
  9928      if (b2 == null) {
  9929        return b;
  9930      }
  9931  
  9932      return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
  9933    };
  9934  
  9935    var prefixedProperty = function prefixedProperty(obj, field, prefix) {
  9936      return getPrefixedProperty(obj, field, prefix);
  9937    };
  9938  
  9939    var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
  9940      if (ele.cy().headless()) {
  9941        return;
  9942      }
  9943  
  9944      var _p = ele._private;
  9945      var rstyle = _p.rstyle;
  9946      var halfArW = rstyle.arrowWidth / 2;
  9947      var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
  9948      var x;
  9949      var y;
  9950  
  9951      if (arrowType !== 'none') {
  9952        if (prefix === 'source') {
  9953          x = rstyle.srcX;
  9954          y = rstyle.srcY;
  9955        } else if (prefix === 'target') {
  9956          x = rstyle.tgtX;
  9957          y = rstyle.tgtY;
  9958        } else {
  9959          x = rstyle.midX;
  9960          y = rstyle.midY;
  9961        } // always store the individual arrow bounds
  9962  
  9963  
  9964        var bbs = _p.arrowBounds = _p.arrowBounds || {};
  9965        var bb = bbs[prefix] = bbs[prefix] || {};
  9966        bb.x1 = x - halfArW;
  9967        bb.y1 = y - halfArW;
  9968        bb.x2 = x + halfArW;
  9969        bb.y2 = y + halfArW;
  9970        bb.w = bb.x2 - bb.x1;
  9971        bb.h = bb.y2 - bb.y1;
  9972        expandBoundingBox(bb, 1);
  9973        updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
  9974      }
  9975    };
  9976  
  9977    var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
  9978      if (ele.cy().headless()) {
  9979        return;
  9980      }
  9981  
  9982      var prefixDash;
  9983  
  9984      if (prefix) {
  9985        prefixDash = prefix + '-';
  9986      } else {
  9987        prefixDash = '';
  9988      }
  9989  
  9990      var _p = ele._private;
  9991      var rstyle = _p.rstyle;
  9992      var label = ele.pstyle(prefixDash + 'label').strValue;
  9993  
  9994      if (label) {
  9995        var halign = ele.pstyle('text-halign');
  9996        var valign = ele.pstyle('text-valign');
  9997        var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
  9998        var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
  9999        var labelX = prefixedProperty(rstyle, 'labelX', prefix);
 10000        var labelY = prefixedProperty(rstyle, 'labelY', prefix);
 10001        var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
 10002        var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
 10003        var isEdge = ele.isEdge();
 10004        var rotation = ele.pstyle(prefixDash + 'text-rotation');
 10005        var outlineWidth = ele.pstyle('text-outline-width').pfValue;
 10006        var borderWidth = ele.pstyle('text-border-width').pfValue;
 10007        var halfBorderWidth = borderWidth / 2;
 10008        var padding = ele.pstyle('text-background-padding').pfValue;
 10009        var lh = labelHeight;
 10010        var lw = labelWidth;
 10011        var lw_2 = lw / 2;
 10012        var lh_2 = lh / 2;
 10013        var lx1, lx2, ly1, ly2;
 10014  
 10015        if (isEdge) {
 10016          lx1 = labelX - lw_2;
 10017          lx2 = labelX + lw_2;
 10018          ly1 = labelY - lh_2;
 10019          ly2 = labelY + lh_2;
 10020        } else {
 10021          switch (halign.value) {
 10022            case 'left':
 10023              lx1 = labelX - lw;
 10024              lx2 = labelX;
 10025              break;
 10026  
 10027            case 'center':
 10028              lx1 = labelX - lw_2;
 10029              lx2 = labelX + lw_2;
 10030              break;
 10031  
 10032            case 'right':
 10033              lx1 = labelX;
 10034              lx2 = labelX + lw;
 10035              break;
 10036          }
 10037  
 10038          switch (valign.value) {
 10039            case 'top':
 10040              ly1 = labelY - lh;
 10041              ly2 = labelY;
 10042              break;
 10043  
 10044            case 'center':
 10045              ly1 = labelY - lh_2;
 10046              ly2 = labelY + lh_2;
 10047              break;
 10048  
 10049            case 'bottom':
 10050              ly1 = labelY;
 10051              ly2 = labelY + lh;
 10052              break;
 10053          }
 10054        } // shift by margin and expand by outline and border
 10055  
 10056  
 10057        lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding;
 10058        lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding;
 10059        ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding;
 10060        ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding; // always store the unrotated label bounds separately
 10061  
 10062        var bbPrefix = prefix || 'main';
 10063        var bbs = _p.labelBounds;
 10064        var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
 10065        bb.x1 = lx1;
 10066        bb.y1 = ly1;
 10067        bb.x2 = lx2;
 10068        bb.y2 = ly2;
 10069        bb.w = lx2 - lx1;
 10070        bb.h = ly2 - ly1;
 10071        expandBoundingBox(bb, 1); // expand to work around browser dimension inaccuracies
 10072  
 10073        var isAutorotate = isEdge && rotation.strValue === 'autorotate';
 10074        var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
 10075  
 10076        if (isAutorotate || isPfValue) {
 10077          var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
 10078          var cos = Math.cos(theta);
 10079          var sin = Math.sin(theta); // rotation point (default value for center-center)
 10080  
 10081          var xo = (lx1 + lx2) / 2;
 10082          var yo = (ly1 + ly2) / 2;
 10083  
 10084          if (!isEdge) {
 10085            switch (halign.value) {
 10086              case 'left':
 10087                xo = lx2;
 10088                break;
 10089  
 10090              case 'right':
 10091                xo = lx1;
 10092                break;
 10093            }
 10094  
 10095            switch (valign.value) {
 10096              case 'top':
 10097                yo = ly2;
 10098                break;
 10099  
 10100              case 'bottom':
 10101                yo = ly1;
 10102                break;
 10103            }
 10104          }
 10105  
 10106          var rotate = function rotate(x, y) {
 10107            x = x - xo;
 10108            y = y - yo;
 10109            return {
 10110              x: x * cos - y * sin + xo,
 10111              y: x * sin + y * cos + yo
 10112            };
 10113          };
 10114  
 10115          var px1y1 = rotate(lx1, ly1);
 10116          var px1y2 = rotate(lx1, ly2);
 10117          var px2y1 = rotate(lx2, ly1);
 10118          var px2y2 = rotate(lx2, ly2);
 10119          lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
 10120          lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
 10121          ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
 10122          ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
 10123        }
 10124  
 10125        var bbPrefixRot = bbPrefix + 'Rot';
 10126        var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
 10127        bbRot.x1 = lx1;
 10128        bbRot.y1 = ly1;
 10129        bbRot.x2 = lx2;
 10130        bbRot.y2 = ly2;
 10131        bbRot.w = lx2 - lx1;
 10132        bbRot.h = ly2 - ly1;
 10133        updateBounds(bounds, lx1, ly1, lx2, ly2);
 10134        updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
 10135      }
 10136  
 10137      return bounds;
 10138    }; // get the bounding box of the elements (in raw model position)
 10139  
 10140  
 10141    var boundingBoxImpl = function boundingBoxImpl(ele, options) {
 10142      var cy = ele._private.cy;
 10143      var styleEnabled = cy.styleEnabled();
 10144      var headless = cy.headless();
 10145      var bounds = makeBoundingBox();
 10146      var _p = ele._private;
 10147      var isNode = ele.isNode();
 10148      var isEdge = ele.isEdge();
 10149      var ex1, ex2, ey1, ey2; // extrema of body / lines
 10150  
 10151      var x, y; // node pos
 10152  
 10153      var rstyle = _p.rstyle;
 10154      var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
 10155      // (other factors like width values will be considered later in this function anyway)
 10156  
 10157      var isDisplayed = function isDisplayed(ele) {
 10158        return ele.pstyle('display').value !== 'none';
 10159      };
 10160  
 10161      var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
 10162      && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
 10163  
 10164      if (displayed) {
 10165        // displayed suffices, since we will find zero area eles anyway
 10166        var overlayOpacity = 0;
 10167        var overlayPadding = 0;
 10168  
 10169        if (styleEnabled && options.includeOverlays) {
 10170          overlayOpacity = ele.pstyle('overlay-opacity').value;
 10171  
 10172          if (overlayOpacity !== 0) {
 10173            overlayPadding = ele.pstyle('overlay-padding').value;
 10174          }
 10175        }
 10176  
 10177        var w = 0;
 10178        var wHalf = 0;
 10179  
 10180        if (styleEnabled) {
 10181          w = ele.pstyle('width').pfValue;
 10182          wHalf = w / 2;
 10183        }
 10184  
 10185        if (isNode && options.includeNodes) {
 10186          var pos = ele.position();
 10187          x = pos.x;
 10188          y = pos.y;
 10189  
 10190          var _w = ele.outerWidth();
 10191  
 10192          var halfW = _w / 2;
 10193          var h = ele.outerHeight();
 10194          var halfH = h / 2; // handle node dimensions
 10195          /////////////////////////
 10196  
 10197          ex1 = x - halfW;
 10198          ex2 = x + halfW;
 10199          ey1 = y - halfH;
 10200          ey2 = y + halfH;
 10201          updateBounds(bounds, ex1, ey1, ex2, ey2);
 10202        } else if (isEdge && options.includeEdges) {
 10203          if (styleEnabled && !headless) {
 10204            var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
 10205            //////////////////////////////////////////////
 10206  
 10207            ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
 10208            ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
 10209            ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
 10210            ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
 10211  
 10212            ex1 -= wHalf;
 10213            ex2 += wHalf;
 10214            ey1 -= wHalf;
 10215            ey2 += wHalf;
 10216            updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
 10217            ////////////////
 10218  
 10219            if (curveStyle === 'haystack') {
 10220              var hpts = rstyle.haystackPts;
 10221  
 10222              if (hpts && hpts.length === 2) {
 10223                ex1 = hpts[0].x;
 10224                ey1 = hpts[0].y;
 10225                ex2 = hpts[1].x;
 10226                ey2 = hpts[1].y;
 10227  
 10228                if (ex1 > ex2) {
 10229                  var temp = ex1;
 10230                  ex1 = ex2;
 10231                  ex2 = temp;
 10232                }
 10233  
 10234                if (ey1 > ey2) {
 10235                  var _temp = ey1;
 10236                  ey1 = ey2;
 10237                  ey2 = _temp;
 10238                }
 10239  
 10240                updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
 10241              }
 10242            } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
 10243              var pts;
 10244  
 10245              switch (curveStyle) {
 10246                case 'bezier':
 10247                case 'unbundled-bezier':
 10248                  pts = rstyle.bezierPts;
 10249                  break;
 10250  
 10251                case 'segments':
 10252                case 'taxi':
 10253                  pts = rstyle.linePts;
 10254                  break;
 10255              }
 10256  
 10257              if (pts != null) {
 10258                for (var j = 0; j < pts.length; j++) {
 10259                  var pt = pts[j];
 10260                  ex1 = pt.x - wHalf;
 10261                  ex2 = pt.x + wHalf;
 10262                  ey1 = pt.y - wHalf;
 10263                  ey2 = pt.y + wHalf;
 10264                  updateBounds(bounds, ex1, ey1, ex2, ey2);
 10265                }
 10266              }
 10267            } // bezier-like or segment-like edge
 10268  
 10269          } else {
 10270            // headless or style disabled
 10271            // fallback on source and target positions
 10272            //////////////////////////////////////////
 10273            var n1 = ele.source();
 10274            var n1pos = n1.position();
 10275            var n2 = ele.target();
 10276            var n2pos = n2.position();
 10277            ex1 = n1pos.x;
 10278            ex2 = n2pos.x;
 10279            ey1 = n1pos.y;
 10280            ey2 = n2pos.y;
 10281  
 10282            if (ex1 > ex2) {
 10283              var _temp2 = ex1;
 10284              ex1 = ex2;
 10285              ex2 = _temp2;
 10286            }
 10287  
 10288            if (ey1 > ey2) {
 10289              var _temp3 = ey1;
 10290              ey1 = ey2;
 10291              ey2 = _temp3;
 10292            } // take into account edge width
 10293  
 10294  
 10295            ex1 -= wHalf;
 10296            ex2 += wHalf;
 10297            ey1 -= wHalf;
 10298            ey2 += wHalf;
 10299            updateBounds(bounds, ex1, ey1, ex2, ey2);
 10300          } // headless or style disabled
 10301  
 10302        } // edges
 10303        // handle edge arrow size
 10304        /////////////////////////
 10305  
 10306  
 10307        if (styleEnabled && options.includeEdges && isEdge) {
 10308          updateBoundsFromArrow(bounds, ele, 'mid-source');
 10309          updateBoundsFromArrow(bounds, ele, 'mid-target');
 10310          updateBoundsFromArrow(bounds, ele, 'source');
 10311          updateBoundsFromArrow(bounds, ele, 'target');
 10312        } // ghost
 10313        ////////
 10314  
 10315  
 10316        if (styleEnabled) {
 10317          var ghost = ele.pstyle('ghost').value === 'yes';
 10318  
 10319          if (ghost) {
 10320            var gx = ele.pstyle('ghost-offset-x').pfValue;
 10321            var gy = ele.pstyle('ghost-offset-y').pfValue;
 10322            updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
 10323          }
 10324        } // always store the body bounds separately from the labels
 10325  
 10326  
 10327        var bbBody = _p.bodyBounds = _p.bodyBounds || {};
 10328        assignBoundingBox(bbBody, bounds);
 10329        expandBoundingBoxSides(bbBody, manualExpansion);
 10330        expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
 10331        // overlay
 10332        //////////
 10333  
 10334        if (styleEnabled) {
 10335          ex1 = bounds.x1;
 10336          ex2 = bounds.x2;
 10337          ey1 = bounds.y1;
 10338          ey2 = bounds.y2;
 10339          updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding);
 10340        } // always store the body bounds separately from the labels
 10341  
 10342  
 10343        var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
 10344        assignBoundingBox(bbOverlay, bounds);
 10345        expandBoundingBoxSides(bbOverlay, manualExpansion);
 10346        expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
 10347        // handle label dimensions
 10348        //////////////////////////
 10349  
 10350        var bbLabels = _p.labelBounds = _p.labelBounds || {};
 10351  
 10352        if (bbLabels.all != null) {
 10353          clearBoundingBox(bbLabels.all);
 10354        } else {
 10355          bbLabels.all = makeBoundingBox();
 10356        }
 10357  
 10358        if (styleEnabled && options.includeLabels) {
 10359          if (options.includeMainLabels) {
 10360            updateBoundsFromLabel(bounds, ele, null);
 10361          }
 10362  
 10363          if (isEdge) {
 10364            if (options.includeSourceLabels) {
 10365              updateBoundsFromLabel(bounds, ele, 'source');
 10366            }
 10367  
 10368            if (options.includeTargetLabels) {
 10369              updateBoundsFromLabel(bounds, ele, 'target');
 10370            }
 10371          }
 10372        } // style enabled for labels
 10373  
 10374      } // if displayed
 10375  
 10376  
 10377      bounds.x1 = noninf(bounds.x1);
 10378      bounds.y1 = noninf(bounds.y1);
 10379      bounds.x2 = noninf(bounds.x2);
 10380      bounds.y2 = noninf(bounds.y2);
 10381      bounds.w = noninf(bounds.x2 - bounds.x1);
 10382      bounds.h = noninf(bounds.y2 - bounds.y1);
 10383  
 10384      if (bounds.w > 0 && bounds.h > 0 && displayed) {
 10385        expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
 10386  
 10387        expandBoundingBox(bounds, 1);
 10388      }
 10389  
 10390      return bounds;
 10391    };
 10392  
 10393    var getKey = function getKey(opts) {
 10394      var i = 0;
 10395  
 10396      var tf = function tf(val) {
 10397        return (val ? 1 : 0) << i++;
 10398      };
 10399  
 10400      var key = 0;
 10401      key += tf(opts.incudeNodes);
 10402      key += tf(opts.includeEdges);
 10403      key += tf(opts.includeLabels);
 10404      key += tf(opts.includeMainLabels);
 10405      key += tf(opts.includeSourceLabels);
 10406      key += tf(opts.includeTargetLabels);
 10407      key += tf(opts.includeOverlays);
 10408      return key;
 10409    };
 10410  
 10411    var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
 10412      if (ele.isEdge()) {
 10413        var p1 = ele.source().position();
 10414        var p2 = ele.target().position();
 10415  
 10416        var r = function r(x) {
 10417          return Math.round(x);
 10418        };
 10419  
 10420        return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
 10421      } else {
 10422        return 0;
 10423      }
 10424    };
 10425  
 10426    var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
 10427      var _p = ele._private;
 10428      var bb;
 10429      var isEdge = ele.isEdge();
 10430      var key = opts == null ? defBbOptsKey : getKey(opts);
 10431      var usingDefOpts = key === defBbOptsKey;
 10432      var currPosKey = getBoundingBoxPosKey(ele);
 10433      var isPosKeySame = _p.bbCachePosKey === currPosKey;
 10434      var useCache = opts.useCache && isPosKeySame;
 10435  
 10436      var isDirty = function isDirty(ele) {
 10437        return ele._private.bbCache == null;
 10438      };
 10439  
 10440      var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
 10441  
 10442      if (needRecalc) {
 10443        if (!isPosKeySame) {
 10444          ele.recalculateRenderedStyle();
 10445        }
 10446  
 10447        bb = boundingBoxImpl(ele, defBbOpts);
 10448        _p.bbCache = bb;
 10449        _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
 10450        _p.bbCachePosKey = currPosKey;
 10451      } else {
 10452        bb = _p.bbCache;
 10453      }
 10454  
 10455      if (!needRecalc && (_p.bbCacheShift.x !== 0 || _p.bbCacheShift.y !== 0)) {
 10456        var shift = assignShiftToBoundingBox;
 10457        var delta = _p.bbCacheShift;
 10458  
 10459        var safeShift = function safeShift(bb, delta) {
 10460          if (bb != null) {
 10461            shift(bb, delta);
 10462          }
 10463        };
 10464  
 10465        shift(bb, delta);
 10466        var bodyBounds = _p.bodyBounds,
 10467            overlayBounds = _p.overlayBounds,
 10468            labelBounds = _p.labelBounds,
 10469            arrowBounds = _p.arrowBounds;
 10470        safeShift(bodyBounds, delta);
 10471        safeShift(overlayBounds, delta);
 10472  
 10473        if (arrowBounds != null) {
 10474          safeShift(arrowBounds.source, delta);
 10475          safeShift(arrowBounds.target, delta);
 10476          safeShift(arrowBounds['mid-source'], delta);
 10477          safeShift(arrowBounds['mid-target'], delta);
 10478        }
 10479  
 10480        if (labelBounds != null) {
 10481          safeShift(labelBounds.main, delta);
 10482          safeShift(labelBounds.all, delta);
 10483          safeShift(labelBounds.source, delta);
 10484          safeShift(labelBounds.target, delta);
 10485        }
 10486      } // always reset the shift, because we either applied the shift or cleared it by doing a fresh recalc
 10487  
 10488  
 10489      _p.bbCacheShift.x = _p.bbCacheShift.y = 0; // not using def opts => need to build up bb from combination of sub bbs
 10490  
 10491      if (!usingDefOpts) {
 10492        var isNode = ele.isNode();
 10493        bb = makeBoundingBox();
 10494  
 10495        if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
 10496          if (opts.includeOverlays) {
 10497            updateBoundsFromBox(bb, _p.overlayBounds);
 10498          } else {
 10499            updateBoundsFromBox(bb, _p.bodyBounds);
 10500          }
 10501        }
 10502  
 10503        if (opts.includeLabels) {
 10504          if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
 10505            updateBoundsFromBox(bb, _p.labelBounds.all);
 10506          } else {
 10507            if (opts.includeMainLabels) {
 10508              updateBoundsFromBox(bb, _p.labelBounds.mainRot);
 10509            }
 10510  
 10511            if (opts.includeSourceLabels) {
 10512              updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
 10513            }
 10514  
 10515            if (opts.includeTargetLabels) {
 10516              updateBoundsFromBox(bb, _p.labelBounds.targetRot);
 10517            }
 10518          }
 10519        }
 10520  
 10521        bb.w = bb.x2 - bb.x1;
 10522        bb.h = bb.y2 - bb.y1;
 10523      }
 10524  
 10525      return bb;
 10526    };
 10527  
 10528    var defBbOpts = {
 10529      includeNodes: true,
 10530      includeEdges: true,
 10531      includeLabels: true,
 10532      includeMainLabels: true,
 10533      includeSourceLabels: true,
 10534      includeTargetLabels: true,
 10535      includeOverlays: true,
 10536      useCache: true
 10537    };
 10538    var defBbOptsKey = getKey(defBbOpts);
 10539    var filledBbOpts = defaults(defBbOpts);
 10540  
 10541    elesfn$k.boundingBox = function (options) {
 10542      var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
 10543      // specified s.t. the cache is used, so check for this case to make it faster by
 10544      // avoiding the overhead of the rest of the function
 10545  
 10546      if (this.length === 1 && this[0]._private.bbCache != null && (options === undefined || options.useCache === undefined || options.useCache === true)) {
 10547        if (options === undefined) {
 10548          options = defBbOpts;
 10549        } else {
 10550          options = filledBbOpts(options);
 10551        }
 10552  
 10553        bounds = cachedBoundingBoxImpl(this[0], options);
 10554      } else {
 10555        bounds = makeBoundingBox();
 10556        options = options || defBbOpts;
 10557        var opts = filledBbOpts(options);
 10558        var eles = this;
 10559        var cy = eles.cy();
 10560        var styleEnabled = cy.styleEnabled();
 10561  
 10562        if (styleEnabled) {
 10563          for (var i = 0; i < eles.length; i++) {
 10564            var ele = eles[i];
 10565            var _p = ele._private;
 10566            var currPosKey = getBoundingBoxPosKey(ele);
 10567            var isPosKeySame = _p.bbCachePosKey === currPosKey;
 10568            var useCache = opts.useCache && isPosKeySame;
 10569            ele.recalculateRenderedStyle(useCache);
 10570          }
 10571        }
 10572  
 10573        this.updateCompoundBounds();
 10574  
 10575        for (var _i = 0; _i < eles.length; _i++) {
 10576          var _ele = eles[_i];
 10577          updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
 10578        }
 10579      }
 10580  
 10581      bounds.x1 = noninf(bounds.x1);
 10582      bounds.y1 = noninf(bounds.y1);
 10583      bounds.x2 = noninf(bounds.x2);
 10584      bounds.y2 = noninf(bounds.y2);
 10585      bounds.w = noninf(bounds.x2 - bounds.x1);
 10586      bounds.h = noninf(bounds.y2 - bounds.y1);
 10587      return bounds;
 10588    };
 10589  
 10590    elesfn$k.dirtyBoundingBoxCache = function () {
 10591      for (var i = 0; i < this.length; i++) {
 10592        var _p = this[i]._private;
 10593        _p.bbCache = null;
 10594        _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
 10595        _p.bbCachePosKey = null;
 10596        _p.bodyBounds = null;
 10597        _p.overlayBounds = null;
 10598        _p.labelBounds.all = null;
 10599        _p.labelBounds.source = null;
 10600        _p.labelBounds.target = null;
 10601        _p.labelBounds.main = null;
 10602        _p.labelBounds.sourceRot = null;
 10603        _p.labelBounds.targetRot = null;
 10604        _p.labelBounds.mainRot = null;
 10605        _p.arrowBounds.source = null;
 10606        _p.arrowBounds.target = null;
 10607        _p.arrowBounds['mid-source'] = null;
 10608        _p.arrowBounds['mid-target'] = null;
 10609      }
 10610  
 10611      this.emitAndNotify('bounds');
 10612      return this;
 10613    };
 10614  
 10615    elesfn$k.shiftCachedBoundingBox = function (delta) {
 10616      for (var i = 0; i < this.length; i++) {
 10617        var ele = this[i];
 10618        var _p = ele._private;
 10619        var bb = _p.bbCache;
 10620  
 10621        if (bb != null) {
 10622          _p.bbCacheShift.x += delta.x;
 10623          _p.bbCacheShift.y += delta.y;
 10624        }
 10625      }
 10626  
 10627      this.emitAndNotify('bounds');
 10628      return this;
 10629    }; // private helper to get bounding box for custom node positions
 10630    // - good for perf in certain cases but currently requires dirtying the rendered style
 10631    // - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
 10632    // - try to use for only things like discrete layouts where the node position would change anyway
 10633  
 10634  
 10635    elesfn$k.boundingBoxAt = function (fn) {
 10636      var nodes = this.nodes();
 10637      var cy = this.cy();
 10638      var hasCompoundNodes = cy.hasCompoundNodes();
 10639  
 10640      if (hasCompoundNodes) {
 10641        nodes = nodes.filter(function (node) {
 10642          return !node.isParent();
 10643        });
 10644      }
 10645  
 10646      if (plainObject(fn)) {
 10647        var obj = fn;
 10648  
 10649        fn = function fn() {
 10650          return obj;
 10651        };
 10652      }
 10653  
 10654      var storeOldPos = function storeOldPos(node, i) {
 10655        return node._private.bbAtOldPos = fn(node, i);
 10656      };
 10657  
 10658      var getOldPos = function getOldPos(node) {
 10659        return node._private.bbAtOldPos;
 10660      };
 10661  
 10662      cy.startBatch();
 10663      nodes.forEach(storeOldPos).silentPositions(fn);
 10664  
 10665      if (hasCompoundNodes) {
 10666        this.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
 10667      }
 10668  
 10669      var bb = copyBoundingBox(this.boundingBox({
 10670        useCache: false
 10671      }));
 10672      nodes.silentPositions(getOldPos);
 10673      cy.endBatch();
 10674      return bb;
 10675    };
 10676  
 10677    fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
 10678    fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
 10679    var bounds = elesfn$k;
 10680  
 10681    var fn$4, elesfn$l;
 10682    fn$4 = elesfn$l = {};
 10683  
 10684    var defineDimFns = function defineDimFns(opts) {
 10685      opts.uppercaseName = capitalize(opts.name);
 10686      opts.autoName = 'auto' + opts.uppercaseName;
 10687      opts.labelName = 'label' + opts.uppercaseName;
 10688      opts.outerName = 'outer' + opts.uppercaseName;
 10689      opts.uppercaseOuterName = capitalize(opts.outerName);
 10690  
 10691      fn$4[opts.name] = function dimImpl() {
 10692        var ele = this[0];
 10693        var _p = ele._private;
 10694        var cy = _p.cy;
 10695        var styleEnabled = cy._private.styleEnabled;
 10696  
 10697        if (ele) {
 10698          if (styleEnabled) {
 10699            if (ele.isParent()) {
 10700              ele.updateCompoundBounds();
 10701              return _p[opts.autoName] || 0;
 10702            }
 10703  
 10704            var d = ele.pstyle(opts.name);
 10705  
 10706            switch (d.strValue) {
 10707              case 'label':
 10708                ele.recalculateRenderedStyle();
 10709                return _p.rstyle[opts.labelName] || 0;
 10710  
 10711              default:
 10712                return d.pfValue;
 10713            }
 10714          } else {
 10715            return 1;
 10716          }
 10717        }
 10718      };
 10719  
 10720      fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
 10721        var ele = this[0];
 10722        var _p = ele._private;
 10723        var cy = _p.cy;
 10724        var styleEnabled = cy._private.styleEnabled;
 10725  
 10726        if (ele) {
 10727          if (styleEnabled) {
 10728            var dim = ele[opts.name]();
 10729            var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
 10730  
 10731            var padding = 2 * ele.padding();
 10732            return dim + border + padding;
 10733          } else {
 10734            return 1;
 10735          }
 10736        }
 10737      };
 10738  
 10739      fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
 10740        var ele = this[0];
 10741  
 10742        if (ele) {
 10743          var d = ele[opts.name]();
 10744          return d * this.cy().zoom();
 10745        }
 10746      };
 10747  
 10748      fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
 10749        var ele = this[0];
 10750  
 10751        if (ele) {
 10752          var od = ele[opts.outerName]();
 10753          return od * this.cy().zoom();
 10754        }
 10755      };
 10756    };
 10757  
 10758    defineDimFns({
 10759      name: 'width'
 10760    });
 10761    defineDimFns({
 10762      name: 'height'
 10763    });
 10764  
 10765    elesfn$l.padding = function () {
 10766      var ele = this[0];
 10767      var _p = ele._private;
 10768  
 10769      if (ele.isParent()) {
 10770        ele.updateCompoundBounds();
 10771  
 10772        if (_p.autoPadding !== undefined) {
 10773          return _p.autoPadding;
 10774        } else {
 10775          return ele.pstyle('padding').pfValue;
 10776        }
 10777      } else {
 10778        return ele.pstyle('padding').pfValue;
 10779      }
 10780    };
 10781  
 10782    elesfn$l.paddedHeight = function () {
 10783      var ele = this[0];
 10784      return ele.height() + 2 * ele.padding();
 10785    };
 10786  
 10787    elesfn$l.paddedWidth = function () {
 10788      var ele = this[0];
 10789      return ele.width() + 2 * ele.padding();
 10790    };
 10791  
 10792    var widthHeight = elesfn$l;
 10793  
 10794    var ifEdge = function ifEdge(ele, getValue) {
 10795      if (ele.isEdge()) {
 10796        return getValue(ele);
 10797      }
 10798    };
 10799  
 10800    var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
 10801      if (ele.isEdge()) {
 10802        var cy = ele.cy();
 10803        return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
 10804      }
 10805    };
 10806  
 10807    var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
 10808      if (ele.isEdge()) {
 10809        var cy = ele.cy();
 10810        var pan = cy.pan();
 10811        var zoom = cy.zoom();
 10812        return getPoints(ele).map(function (p) {
 10813          return modelToRenderedPosition(p, zoom, pan);
 10814        });
 10815      }
 10816    };
 10817  
 10818    var controlPoints = function controlPoints(ele) {
 10819      return ele.renderer().getControlPoints(ele);
 10820    };
 10821  
 10822    var segmentPoints = function segmentPoints(ele) {
 10823      return ele.renderer().getSegmentPoints(ele);
 10824    };
 10825  
 10826    var sourceEndpoint = function sourceEndpoint(ele) {
 10827      return ele.renderer().getSourceEndpoint(ele);
 10828    };
 10829  
 10830    var targetEndpoint = function targetEndpoint(ele) {
 10831      return ele.renderer().getTargetEndpoint(ele);
 10832    };
 10833  
 10834    var midpoint = function midpoint(ele) {
 10835      return ele.renderer().getEdgeMidpoint(ele);
 10836    };
 10837  
 10838    var pts = {
 10839      controlPoints: {
 10840        get: controlPoints,
 10841        mult: true
 10842      },
 10843      segmentPoints: {
 10844        get: segmentPoints,
 10845        mult: true
 10846      },
 10847      sourceEndpoint: {
 10848        get: sourceEndpoint
 10849      },
 10850      targetEndpoint: {
 10851        get: targetEndpoint
 10852      },
 10853      midpoint: {
 10854        get: midpoint
 10855      }
 10856    };
 10857  
 10858    var renderedName = function renderedName(name) {
 10859      return 'rendered' + name[0].toUpperCase() + name.substr(1);
 10860    };
 10861  
 10862    var edgePoints = Object.keys(pts).reduce(function (obj, name) {
 10863      var spec = pts[name];
 10864      var rName = renderedName(name);
 10865  
 10866      obj[name] = function () {
 10867        return ifEdge(this, spec.get);
 10868      };
 10869  
 10870      if (spec.mult) {
 10871        obj[rName] = function () {
 10872          return ifEdgeRenderedPositions(this, spec.get);
 10873        };
 10874      } else {
 10875        obj[rName] = function () {
 10876          return ifEdgeRenderedPosition(this, spec.get);
 10877        };
 10878      }
 10879  
 10880      return obj;
 10881    }, {});
 10882  
 10883    var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
 10884  
 10885    /*!
 10886    Event object based on jQuery events, MIT license
 10887  
 10888    https://jquery.org/license/
 10889    https://tldrlegal.com/license/mit-license
 10890    https://github.com/jquery/jquery/blob/master/src/event.js
 10891    */
 10892    var Event = function Event(src, props) {
 10893      this.recycle(src, props);
 10894    };
 10895  
 10896    function returnFalse() {
 10897      return false;
 10898    }
 10899  
 10900    function returnTrue() {
 10901      return true;
 10902    } // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
 10903  
 10904  
 10905    Event.prototype = {
 10906      instanceString: function instanceString() {
 10907        return 'event';
 10908      },
 10909      recycle: function recycle(src, props) {
 10910        this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
 10911  
 10912        if (src != null && src.preventDefault) {
 10913          // Browser Event object
 10914          this.type = src.type; // Events bubbling up the document may have been marked as prevented
 10915          // by a handler lower down the tree; reflect the correct value.
 10916  
 10917          this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
 10918        } else if (src != null && src.type) {
 10919          // Plain object containing all event details
 10920          props = src;
 10921        } else {
 10922          // Event string
 10923          this.type = src;
 10924        } // Put explicitly provided properties onto the event object
 10925  
 10926  
 10927        if (props != null) {
 10928          // more efficient to manually copy fields we use
 10929          this.originalEvent = props.originalEvent;
 10930          this.type = props.type != null ? props.type : this.type;
 10931          this.cy = props.cy;
 10932          this.target = props.target;
 10933          this.position = props.position;
 10934          this.renderedPosition = props.renderedPosition;
 10935          this.namespace = props.namespace;
 10936          this.layout = props.layout;
 10937        }
 10938  
 10939        if (this.cy != null && this.position != null && this.renderedPosition == null) {
 10940          // create a rendered position based on the passed position
 10941          var pos = this.position;
 10942          var zoom = this.cy.zoom();
 10943          var pan = this.cy.pan();
 10944          this.renderedPosition = {
 10945            x: pos.x * zoom + pan.x,
 10946            y: pos.y * zoom + pan.y
 10947          };
 10948        } // Create a timestamp if incoming event doesn't have one
 10949  
 10950  
 10951        this.timeStamp = src && src.timeStamp || Date.now();
 10952      },
 10953      preventDefault: function preventDefault() {
 10954        this.isDefaultPrevented = returnTrue;
 10955        var e = this.originalEvent;
 10956  
 10957        if (!e) {
 10958          return;
 10959        } // if preventDefault exists run it on the original event
 10960  
 10961  
 10962        if (e.preventDefault) {
 10963          e.preventDefault();
 10964        }
 10965      },
 10966      stopPropagation: function stopPropagation() {
 10967        this.isPropagationStopped = returnTrue;
 10968        var e = this.originalEvent;
 10969  
 10970        if (!e) {
 10971          return;
 10972        } // if stopPropagation exists run it on the original event
 10973  
 10974  
 10975        if (e.stopPropagation) {
 10976          e.stopPropagation();
 10977        }
 10978      },
 10979      stopImmediatePropagation: function stopImmediatePropagation() {
 10980        this.isImmediatePropagationStopped = returnTrue;
 10981        this.stopPropagation();
 10982      },
 10983      isDefaultPrevented: returnFalse,
 10984      isPropagationStopped: returnFalse,
 10985      isImmediatePropagationStopped: returnFalse
 10986    };
 10987  
 10988    var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
 10989  
 10990    var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
 10991  
 10992    var defaults$8 = {
 10993      qualifierCompare: function qualifierCompare(q1, q2) {
 10994        return q1 === q2;
 10995      },
 10996      eventMatches: function eventMatches()
 10997      /*context, listener, eventObj*/
 10998      {
 10999        return true;
 11000      },
 11001      addEventFields: function addEventFields()
 11002      /*context, evt*/
 11003      {},
 11004      callbackContext: function callbackContext(context
 11005      /*, listener, eventObj*/
 11006      ) {
 11007        return context;
 11008      },
 11009      beforeEmit: function beforeEmit()
 11010      /* context, listener, eventObj */
 11011      {},
 11012      afterEmit: function afterEmit()
 11013      /* context, listener, eventObj */
 11014      {},
 11015      bubble: function bubble()
 11016      /*context*/
 11017      {
 11018        return false;
 11019      },
 11020      parent: function parent()
 11021      /*context*/
 11022      {
 11023        return null;
 11024      },
 11025      context: null
 11026    };
 11027    var defaultsKeys = Object.keys(defaults$8);
 11028    var emptyOpts = {};
 11029  
 11030    function Emitter() {
 11031      var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
 11032      var context = arguments.length > 1 ? arguments[1] : undefined;
 11033  
 11034      // micro-optimisation vs Object.assign() -- reduces Element instantiation time
 11035      for (var i = 0; i < defaultsKeys.length; i++) {
 11036        var key = defaultsKeys[i];
 11037        this[key] = opts[key] || defaults$8[key];
 11038      }
 11039  
 11040      this.context = context || this.context;
 11041      this.listeners = [];
 11042      this.emitting = 0;
 11043    }
 11044  
 11045    var p = Emitter.prototype;
 11046  
 11047    var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
 11048      if (fn(qualifier)) {
 11049        callback = qualifier;
 11050        qualifier = null;
 11051      }
 11052  
 11053      if (confOverrides) {
 11054        if (conf == null) {
 11055          conf = confOverrides;
 11056        } else {
 11057          conf = extend({}, conf, confOverrides);
 11058        }
 11059      }
 11060  
 11061      var eventList = array(events) ? events : events.split(/\s+/);
 11062  
 11063      for (var i = 0; i < eventList.length; i++) {
 11064        var evt = eventList[i];
 11065  
 11066        if (emptyString(evt)) {
 11067          continue;
 11068        }
 11069  
 11070        var match = evt.match(eventRegex); // type[.namespace]
 11071  
 11072        if (match) {
 11073          var type = match[1];
 11074          var namespace = match[2] ? match[2] : null;
 11075          var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
 11076  
 11077          if (ret === false) {
 11078            break;
 11079          } // allow exiting early
 11080  
 11081        }
 11082      }
 11083    };
 11084  
 11085    var makeEventObj = function makeEventObj(self, obj) {
 11086      self.addEventFields(self.context, obj);
 11087      return new Event(obj.type, obj);
 11088    };
 11089  
 11090    var forEachEventObj = function forEachEventObj(self, handler, events) {
 11091      if (event(events)) {
 11092        handler(self, events);
 11093        return;
 11094      } else if (plainObject(events)) {
 11095        handler(self, makeEventObj(self, events));
 11096        return;
 11097      }
 11098  
 11099      var eventList = array(events) ? events : events.split(/\s+/);
 11100  
 11101      for (var i = 0; i < eventList.length; i++) {
 11102        var evt = eventList[i];
 11103  
 11104        if (emptyString(evt)) {
 11105          continue;
 11106        }
 11107  
 11108        var match = evt.match(eventRegex); // type[.namespace]
 11109  
 11110        if (match) {
 11111          var type = match[1];
 11112          var namespace = match[2] ? match[2] : null;
 11113          var eventObj = makeEventObj(self, {
 11114            type: type,
 11115            namespace: namespace,
 11116            target: self.context
 11117          });
 11118          handler(self, eventObj);
 11119        }
 11120      }
 11121    };
 11122  
 11123    p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
 11124      forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
 11125        if (fn(callback)) {
 11126          self.listeners.push({
 11127            event: event,
 11128            // full event string
 11129            callback: callback,
 11130            // callback to run
 11131            type: type,
 11132            // the event type (e.g. 'click')
 11133            namespace: namespace,
 11134            // the event namespace (e.g. ".foo")
 11135            qualifier: qualifier,
 11136            // a restriction on whether to match this emitter
 11137            conf: conf // additional configuration
 11138  
 11139          });
 11140        }
 11141      }, events, qualifier, callback, conf, confOverrides);
 11142      return this;
 11143    };
 11144  
 11145    p.one = function (events, qualifier, callback, conf) {
 11146      return this.on(events, qualifier, callback, conf, {
 11147        one: true
 11148      });
 11149    };
 11150  
 11151    p.removeListener = p.off = function (events, qualifier, callback, conf) {
 11152      var _this = this;
 11153  
 11154      if (this.emitting !== 0) {
 11155        this.listeners = copyArray(this.listeners);
 11156      }
 11157  
 11158      var listeners = this.listeners;
 11159  
 11160      var _loop = function _loop(i) {
 11161        var listener = listeners[i];
 11162        forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
 11163        /*, conf*/
 11164        ) {
 11165          if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
 11166            listeners.splice(i, 1);
 11167            return false;
 11168          }
 11169        }, events, qualifier, callback, conf);
 11170      };
 11171  
 11172      for (var i = listeners.length - 1; i >= 0; i--) {
 11173        _loop(i);
 11174      }
 11175  
 11176      return this;
 11177    };
 11178  
 11179    p.removeAllListeners = function () {
 11180      return this.removeListener('*');
 11181    };
 11182  
 11183    p.emit = p.trigger = function (events, extraParams, manualCallback) {
 11184      var listeners = this.listeners;
 11185      var numListenersBeforeEmit = listeners.length;
 11186      this.emitting++;
 11187  
 11188      if (!array(extraParams)) {
 11189        extraParams = [extraParams];
 11190      }
 11191  
 11192      forEachEventObj(this, function (self, eventObj) {
 11193        if (manualCallback != null) {
 11194          listeners = [{
 11195            event: eventObj.event,
 11196            type: eventObj.type,
 11197            namespace: eventObj.namespace,
 11198            callback: manualCallback
 11199          }];
 11200          numListenersBeforeEmit = listeners.length;
 11201        }
 11202  
 11203        var _loop2 = function _loop2(i) {
 11204          var listener = listeners[i];
 11205  
 11206          if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
 11207            var args = [eventObj];
 11208  
 11209            if (extraParams != null) {
 11210              push(args, extraParams);
 11211            }
 11212  
 11213            self.beforeEmit(self.context, listener, eventObj);
 11214  
 11215            if (listener.conf && listener.conf.one) {
 11216              self.listeners = self.listeners.filter(function (l) {
 11217                return l !== listener;
 11218              });
 11219            }
 11220  
 11221            var context = self.callbackContext(self.context, listener, eventObj);
 11222            var ret = listener.callback.apply(context, args);
 11223            self.afterEmit(self.context, listener, eventObj);
 11224  
 11225            if (ret === false) {
 11226              eventObj.stopPropagation();
 11227              eventObj.preventDefault();
 11228            }
 11229          } // if listener matches
 11230  
 11231        };
 11232  
 11233        for (var i = 0; i < numListenersBeforeEmit; i++) {
 11234          _loop2(i);
 11235        } // for listener
 11236  
 11237  
 11238        if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
 11239          self.parent(self.context).emit(eventObj, extraParams);
 11240        }
 11241      }, events);
 11242      this.emitting--;
 11243      return this;
 11244    };
 11245  
 11246    var emitterOptions = {
 11247      qualifierCompare: function qualifierCompare(selector1, selector2) {
 11248        if (selector1 == null || selector2 == null) {
 11249          return selector1 == null && selector2 == null;
 11250        } else {
 11251          return selector1.sameText(selector2);
 11252        }
 11253      },
 11254      eventMatches: function eventMatches(ele, listener, eventObj) {
 11255        var selector = listener.qualifier;
 11256  
 11257        if (selector != null) {
 11258          return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
 11259        }
 11260  
 11261        return true;
 11262      },
 11263      addEventFields: function addEventFields(ele, evt) {
 11264        evt.cy = ele.cy();
 11265        evt.target = ele;
 11266      },
 11267      callbackContext: function callbackContext(ele, listener, eventObj) {
 11268        return listener.qualifier != null ? eventObj.target : ele;
 11269      },
 11270      beforeEmit: function beforeEmit(context, listener
 11271      /*, eventObj*/
 11272      ) {
 11273        if (listener.conf && listener.conf.once) {
 11274          listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
 11275        }
 11276      },
 11277      bubble: function bubble() {
 11278        return true;
 11279      },
 11280      parent: function parent(ele) {
 11281        return ele.isChild() ? ele.parent() : ele.cy();
 11282      }
 11283    };
 11284  
 11285    var argSelector = function argSelector(arg) {
 11286      if (string(arg)) {
 11287        return new Selector(arg);
 11288      } else {
 11289        return arg;
 11290      }
 11291    };
 11292  
 11293    var elesfn$m = {
 11294      createEmitter: function createEmitter() {
 11295        for (var i = 0; i < this.length; i++) {
 11296          var ele = this[i];
 11297          var _p = ele._private;
 11298  
 11299          if (!_p.emitter) {
 11300            _p.emitter = new Emitter(emitterOptions, ele);
 11301          }
 11302        }
 11303  
 11304        return this;
 11305      },
 11306      emitter: function emitter() {
 11307        return this._private.emitter;
 11308      },
 11309      on: function on(events, selector, callback) {
 11310        var argSel = argSelector(selector);
 11311  
 11312        for (var i = 0; i < this.length; i++) {
 11313          var ele = this[i];
 11314          ele.emitter().on(events, argSel, callback);
 11315        }
 11316  
 11317        return this;
 11318      },
 11319      removeListener: function removeListener(events, selector, callback) {
 11320        var argSel = argSelector(selector);
 11321  
 11322        for (var i = 0; i < this.length; i++) {
 11323          var ele = this[i];
 11324          ele.emitter().removeListener(events, argSel, callback);
 11325        }
 11326  
 11327        return this;
 11328      },
 11329      removeAllListeners: function removeAllListeners() {
 11330        for (var i = 0; i < this.length; i++) {
 11331          var ele = this[i];
 11332          ele.emitter().removeAllListeners();
 11333        }
 11334  
 11335        return this;
 11336      },
 11337      one: function one(events, selector, callback) {
 11338        var argSel = argSelector(selector);
 11339  
 11340        for (var i = 0; i < this.length; i++) {
 11341          var ele = this[i];
 11342          ele.emitter().one(events, argSel, callback);
 11343        }
 11344  
 11345        return this;
 11346      },
 11347      once: function once(events, selector, callback) {
 11348        var argSel = argSelector(selector);
 11349  
 11350        for (var i = 0; i < this.length; i++) {
 11351          var ele = this[i];
 11352          ele.emitter().on(events, argSel, callback, {
 11353            once: true,
 11354            onceCollection: this
 11355          });
 11356        }
 11357      },
 11358      emit: function emit(events, extraParams) {
 11359        for (var i = 0; i < this.length; i++) {
 11360          var ele = this[i];
 11361          ele.emitter().emit(events, extraParams);
 11362        }
 11363  
 11364        return this;
 11365      },
 11366      emitAndNotify: function emitAndNotify(event, extraParams) {
 11367        // for internal use only
 11368        if (this.length === 0) {
 11369          return;
 11370        } // empty collections don't need to notify anything
 11371        // notify renderer
 11372  
 11373  
 11374        this.cy().notify(event, this);
 11375        this.emit(event, extraParams);
 11376        return this;
 11377      }
 11378    };
 11379    define$3.eventAliasesOn(elesfn$m);
 11380  
 11381    var elesfn$n = {
 11382      nodes: function nodes(selector) {
 11383        return this.filter(function (ele) {
 11384          return ele.isNode();
 11385        }).filter(selector);
 11386      },
 11387      edges: function edges(selector) {
 11388        return this.filter(function (ele) {
 11389          return ele.isEdge();
 11390        }).filter(selector);
 11391      },
 11392      // internal helper to get nodes and edges as separate collections with single iteration over elements
 11393      byGroup: function byGroup() {
 11394        var nodes = this.spawn();
 11395        var edges = this.spawn();
 11396  
 11397        for (var i = 0; i < this.length; i++) {
 11398          var ele = this[i];
 11399  
 11400          if (ele.isNode()) {
 11401            nodes.merge(ele);
 11402          } else {
 11403            edges.merge(ele);
 11404          }
 11405        }
 11406  
 11407        return {
 11408          nodes: nodes,
 11409          edges: edges
 11410        };
 11411      },
 11412      filter: function filter(_filter, thisArg) {
 11413        if (_filter === undefined) {
 11414          // check this first b/c it's the most common/performant case
 11415          return this;
 11416        } else if (string(_filter) || elementOrCollection(_filter)) {
 11417          return new Selector(_filter).filter(this);
 11418        } else if (fn(_filter)) {
 11419          var filterEles = this.spawn();
 11420          var eles = this;
 11421  
 11422          for (var i = 0; i < eles.length; i++) {
 11423            var ele = eles[i];
 11424            var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
 11425  
 11426            if (include) {
 11427              filterEles.merge(ele);
 11428            }
 11429          }
 11430  
 11431          return filterEles;
 11432        }
 11433  
 11434        return this.spawn(); // if not handled by above, give 'em an empty collection
 11435      },
 11436      not: function not(toRemove) {
 11437        if (!toRemove) {
 11438          return this;
 11439        } else {
 11440          if (string(toRemove)) {
 11441            toRemove = this.filter(toRemove);
 11442          }
 11443  
 11444          var elements = [];
 11445          var rMap = toRemove._private.map;
 11446  
 11447          for (var i = 0; i < this.length; i++) {
 11448            var element = this[i];
 11449            var remove = rMap.has(element.id());
 11450  
 11451            if (!remove) {
 11452              elements.push(element);
 11453            }
 11454          }
 11455  
 11456          return this.spawn(elements);
 11457        }
 11458      },
 11459      absoluteComplement: function absoluteComplement() {
 11460        var cy = this.cy();
 11461        return cy.mutableElements().not(this);
 11462      },
 11463      intersect: function intersect(other) {
 11464        // if a selector is specified, then filter by it instead
 11465        if (string(other)) {
 11466          var selector = other;
 11467          return this.filter(selector);
 11468        }
 11469  
 11470        var elements = [];
 11471        var col1 = this;
 11472        var col2 = other;
 11473        var col1Smaller = this.length < other.length;
 11474        var map2 = col1Smaller ? col2._private.map : col1._private.map;
 11475        var col = col1Smaller ? col1 : col2;
 11476  
 11477        for (var i = 0; i < col.length; i++) {
 11478          var id = col[i]._private.data.id;
 11479          var entry = map2.get(id);
 11480  
 11481          if (entry) {
 11482            elements.push(entry.ele);
 11483          }
 11484        }
 11485  
 11486        return this.spawn(elements);
 11487      },
 11488      xor: function xor(other) {
 11489        var cy = this._private.cy;
 11490  
 11491        if (string(other)) {
 11492          other = cy.$(other);
 11493        }
 11494  
 11495        var elements = [];
 11496        var col1 = this;
 11497        var col2 = other;
 11498  
 11499        var add = function add(col, other) {
 11500          for (var i = 0; i < col.length; i++) {
 11501            var ele = col[i];
 11502            var id = ele._private.data.id;
 11503            var inOther = other.hasElementWithId(id);
 11504  
 11505            if (!inOther) {
 11506              elements.push(ele);
 11507            }
 11508          }
 11509        };
 11510  
 11511        add(col1, col2);
 11512        add(col2, col1);
 11513        return this.spawn(elements);
 11514      },
 11515      diff: function diff(other) {
 11516        var cy = this._private.cy;
 11517  
 11518        if (string(other)) {
 11519          other = cy.$(other);
 11520        }
 11521  
 11522        var left = [];
 11523        var right = [];
 11524        var both = [];
 11525        var col1 = this;
 11526        var col2 = other;
 11527  
 11528        var add = function add(col, other, retEles) {
 11529          for (var i = 0; i < col.length; i++) {
 11530            var ele = col[i];
 11531            var id = ele._private.data.id;
 11532            var inOther = other.hasElementWithId(id);
 11533  
 11534            if (inOther) {
 11535              both.push(ele);
 11536            } else {
 11537              retEles.push(ele);
 11538            }
 11539          }
 11540        };
 11541  
 11542        add(col1, col2, left);
 11543        add(col2, col1, right);
 11544        return {
 11545          left: this.spawn(left, {
 11546            unique: true
 11547          }),
 11548          right: this.spawn(right, {
 11549            unique: true
 11550          }),
 11551          both: this.spawn(both, {
 11552            unique: true
 11553          })
 11554        };
 11555      },
 11556      add: function add(toAdd) {
 11557        var cy = this._private.cy;
 11558  
 11559        if (!toAdd) {
 11560          return this;
 11561        }
 11562  
 11563        if (string(toAdd)) {
 11564          var selector = toAdd;
 11565          toAdd = cy.mutableElements().filter(selector);
 11566        }
 11567  
 11568        var elements = [];
 11569  
 11570        for (var i = 0; i < this.length; i++) {
 11571          elements.push(this[i]);
 11572        }
 11573  
 11574        var map = this._private.map;
 11575  
 11576        for (var _i = 0; _i < toAdd.length; _i++) {
 11577          var add = !map.has(toAdd[_i].id());
 11578  
 11579          if (add) {
 11580            elements.push(toAdd[_i]);
 11581          }
 11582        }
 11583  
 11584        return this.spawn(elements);
 11585      },
 11586      // in place merge on calling collection
 11587      merge: function merge(toAdd) {
 11588        var _p = this._private;
 11589        var cy = _p.cy;
 11590  
 11591        if (!toAdd) {
 11592          return this;
 11593        }
 11594  
 11595        if (toAdd && string(toAdd)) {
 11596          var selector = toAdd;
 11597          toAdd = cy.mutableElements().filter(selector);
 11598        }
 11599  
 11600        var map = _p.map;
 11601  
 11602        for (var i = 0; i < toAdd.length; i++) {
 11603          var toAddEle = toAdd[i];
 11604          var id = toAddEle._private.data.id;
 11605          var add = !map.has(id);
 11606  
 11607          if (add) {
 11608            var index = this.length++;
 11609            this[index] = toAddEle;
 11610            map.set(id, {
 11611              ele: toAddEle,
 11612              index: index
 11613            });
 11614          } else {
 11615            // replace
 11616            var _index = map.get(id).index;
 11617            this[_index] = toAddEle;
 11618            map.set(id, {
 11619              ele: toAddEle,
 11620              index: _index
 11621            });
 11622          }
 11623        }
 11624  
 11625        return this; // chaining
 11626      },
 11627      unmergeAt: function unmergeAt(i) {
 11628        var ele = this[i];
 11629        var id = ele.id();
 11630        var _p = this._private;
 11631        var map = _p.map; // remove ele
 11632  
 11633        this[i] = undefined;
 11634        map["delete"](id);
 11635        var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
 11636  
 11637        if (this.length > 1 && !unmergedLastEle) {
 11638          var lastEleI = this.length - 1;
 11639          var lastEle = this[lastEleI];
 11640          var lastEleId = lastEle._private.data.id;
 11641          this[lastEleI] = undefined;
 11642          this[i] = lastEle;
 11643          map.set(lastEleId, {
 11644            ele: lastEle,
 11645            index: i
 11646          });
 11647        } // the collection is now 1 ele smaller
 11648  
 11649  
 11650        this.length--;
 11651        return this;
 11652      },
 11653      // remove single ele in place in calling collection
 11654      unmergeOne: function unmergeOne(ele) {
 11655        ele = ele[0];
 11656        var _p = this._private;
 11657        var id = ele._private.data.id;
 11658        var map = _p.map;
 11659        var entry = map.get(id);
 11660  
 11661        if (!entry) {
 11662          return this; // no need to remove
 11663        }
 11664  
 11665        var i = entry.index;
 11666        this.unmergeAt(i);
 11667        return this;
 11668      },
 11669      // remove eles in place on calling collection
 11670      unmerge: function unmerge(toRemove) {
 11671        var cy = this._private.cy;
 11672  
 11673        if (!toRemove) {
 11674          return this;
 11675        }
 11676  
 11677        if (toRemove && string(toRemove)) {
 11678          var selector = toRemove;
 11679          toRemove = cy.mutableElements().filter(selector);
 11680        }
 11681  
 11682        for (var i = 0; i < toRemove.length; i++) {
 11683          this.unmergeOne(toRemove[i]);
 11684        }
 11685  
 11686        return this; // chaining
 11687      },
 11688      unmergeBy: function unmergeBy(toRmFn) {
 11689        for (var i = this.length - 1; i >= 0; i--) {
 11690          var ele = this[i];
 11691  
 11692          if (toRmFn(ele)) {
 11693            this.unmergeAt(i);
 11694          }
 11695        }
 11696  
 11697        return this;
 11698      },
 11699      map: function map(mapFn, thisArg) {
 11700        var arr = [];
 11701        var eles = this;
 11702  
 11703        for (var i = 0; i < eles.length; i++) {
 11704          var ele = eles[i];
 11705          var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
 11706          arr.push(ret);
 11707        }
 11708  
 11709        return arr;
 11710      },
 11711      reduce: function reduce(fn, initialValue) {
 11712        var val = initialValue;
 11713        var eles = this;
 11714  
 11715        for (var i = 0; i < eles.length; i++) {
 11716          val = fn(val, eles[i], i, eles);
 11717        }
 11718  
 11719        return val;
 11720      },
 11721      max: function max(valFn, thisArg) {
 11722        var max = -Infinity;
 11723        var maxEle;
 11724        var eles = this;
 11725  
 11726        for (var i = 0; i < eles.length; i++) {
 11727          var ele = eles[i];
 11728          var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
 11729  
 11730          if (val > max) {
 11731            max = val;
 11732            maxEle = ele;
 11733          }
 11734        }
 11735  
 11736        return {
 11737          value: max,
 11738          ele: maxEle
 11739        };
 11740      },
 11741      min: function min(valFn, thisArg) {
 11742        var min = Infinity;
 11743        var minEle;
 11744        var eles = this;
 11745  
 11746        for (var i = 0; i < eles.length; i++) {
 11747          var ele = eles[i];
 11748          var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
 11749  
 11750          if (val < min) {
 11751            min = val;
 11752            minEle = ele;
 11753          }
 11754        }
 11755  
 11756        return {
 11757          value: min,
 11758          ele: minEle
 11759        };
 11760      }
 11761    }; // aliases
 11762  
 11763    var fn$5 = elesfn$n;
 11764    fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
 11765    fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
 11766    fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
 11767    fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
 11768    fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
 11769    fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
 11770  
 11771    var elesfn$o = {
 11772      isNode: function isNode() {
 11773        return this.group() === 'nodes';
 11774      },
 11775      isEdge: function isEdge() {
 11776        return this.group() === 'edges';
 11777      },
 11778      isLoop: function isLoop() {
 11779        return this.isEdge() && this.source()[0] === this.target()[0];
 11780      },
 11781      isSimple: function isSimple() {
 11782        return this.isEdge() && this.source()[0] !== this.target()[0];
 11783      },
 11784      group: function group() {
 11785        var ele = this[0];
 11786  
 11787        if (ele) {
 11788          return ele._private.group;
 11789        }
 11790      }
 11791    };
 11792  
 11793    /**
 11794     *  Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
 11795     *  and z-index (low to high).  These styles affect how this applies:
 11796     *
 11797     *  z-compound-depth: May be `bottom | orphan | auto | top`.  The first drawn is `bottom`, then `orphan` which is the
 11798     *      same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
 11799     *      root to leaves of the compound graph.  The last drawn is `top`.
 11800     *  z-index-compare: May be `auto | manual`.  The default value is `auto` which always draws edges under nodes.
 11801     *      `manual` ignores this convention and draws based on the `z-index` value setting.
 11802     *  z-index: An integer value that affects the relative draw order of elements.  In general, an element with a higher
 11803     *      `z-index` will be drawn on top of an element with a lower `z-index`.
 11804     */
 11805  
 11806    var zIndexSort = function zIndexSort(a, b) {
 11807      var cy = a.cy();
 11808      var hasCompoundNodes = cy.hasCompoundNodes();
 11809  
 11810      function getDepth(ele) {
 11811        var style = ele.pstyle('z-compound-depth');
 11812  
 11813        if (style.value === 'auto') {
 11814          return hasCompoundNodes ? ele.zDepth() : 0;
 11815        } else if (style.value === 'bottom') {
 11816          return -1;
 11817        } else if (style.value === 'top') {
 11818          return MAX_INT;
 11819        } // 'orphan'
 11820  
 11821  
 11822        return 0;
 11823      }
 11824  
 11825      var depthDiff = getDepth(a) - getDepth(b);
 11826  
 11827      if (depthDiff !== 0) {
 11828        return depthDiff;
 11829      }
 11830  
 11831      function getEleDepth(ele) {
 11832        var style = ele.pstyle('z-index-compare');
 11833  
 11834        if (style.value === 'auto') {
 11835          return ele.isNode() ? 1 : 0;
 11836        } // 'manual'
 11837  
 11838  
 11839        return 0;
 11840      }
 11841  
 11842      var eleDiff = getEleDepth(a) - getEleDepth(b);
 11843  
 11844      if (eleDiff !== 0) {
 11845        return eleDiff;
 11846      }
 11847  
 11848      var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
 11849  
 11850      if (zDiff !== 0) {
 11851        return zDiff;
 11852      } // compare indices in the core (order added to graph w/ last on top)
 11853  
 11854  
 11855      return a.poolIndex() - b.poolIndex();
 11856    };
 11857  
 11858    var elesfn$p = {
 11859      forEach: function forEach(fn$1, thisArg) {
 11860        if (fn(fn$1)) {
 11861          var N = this.length;
 11862  
 11863          for (var i = 0; i < N; i++) {
 11864            var ele = this[i];
 11865            var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
 11866  
 11867            if (ret === false) {
 11868              break;
 11869            } // exit each early on return false
 11870  
 11871          }
 11872        }
 11873  
 11874        return this;
 11875      },
 11876      toArray: function toArray() {
 11877        var array = [];
 11878  
 11879        for (var i = 0; i < this.length; i++) {
 11880          array.push(this[i]);
 11881        }
 11882  
 11883        return array;
 11884      },
 11885      slice: function slice(start, end) {
 11886        var array = [];
 11887        var thisSize = this.length;
 11888  
 11889        if (end == null) {
 11890          end = thisSize;
 11891        }
 11892  
 11893        if (start == null) {
 11894          start = 0;
 11895        }
 11896  
 11897        if (start < 0) {
 11898          start = thisSize + start;
 11899        }
 11900  
 11901        if (end < 0) {
 11902          end = thisSize + end;
 11903        }
 11904  
 11905        for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
 11906          array.push(this[i]);
 11907        }
 11908  
 11909        return this.spawn(array);
 11910      },
 11911      size: function size() {
 11912        return this.length;
 11913      },
 11914      eq: function eq(i) {
 11915        return this[i] || this.spawn();
 11916      },
 11917      first: function first() {
 11918        return this[0] || this.spawn();
 11919      },
 11920      last: function last() {
 11921        return this[this.length - 1] || this.spawn();
 11922      },
 11923      empty: function empty() {
 11924        return this.length === 0;
 11925      },
 11926      nonempty: function nonempty() {
 11927        return !this.empty();
 11928      },
 11929      sort: function sort(sortFn) {
 11930        if (!fn(sortFn)) {
 11931          return this;
 11932        }
 11933  
 11934        var sorted = this.toArray().sort(sortFn);
 11935        return this.spawn(sorted);
 11936      },
 11937      sortByZIndex: function sortByZIndex() {
 11938        return this.sort(zIndexSort);
 11939      },
 11940      zDepth: function zDepth() {
 11941        var ele = this[0];
 11942  
 11943        if (!ele) {
 11944          return undefined;
 11945        } // let cy = ele.cy();
 11946  
 11947  
 11948        var _p = ele._private;
 11949        var group = _p.group;
 11950  
 11951        if (group === 'nodes') {
 11952          var depth = _p.data.parent ? ele.parents().size() : 0;
 11953  
 11954          if (!ele.isParent()) {
 11955            return MAX_INT - 1; // childless nodes always on top
 11956          }
 11957  
 11958          return depth;
 11959        } else {
 11960          var src = _p.source;
 11961          var tgt = _p.target;
 11962          var srcDepth = src.zDepth();
 11963          var tgtDepth = tgt.zDepth();
 11964          return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
 11965        }
 11966      }
 11967    };
 11968    elesfn$p.each = elesfn$p.forEach;
 11969  
 11970    var defineSymbolIterator = function defineSymbolIterator() {
 11971      var typeofUndef =  "undefined" ;
 11972      var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
 11973  
 11974      if (isIteratorSupported) {
 11975        elesfn$p[Symbol.iterator] = function () {
 11976          var _this = this;
 11977  
 11978          // eslint-disable-line no-undef
 11979          var entry = {
 11980            value: undefined,
 11981            done: false
 11982          };
 11983          var i = 0;
 11984          var length = this.length;
 11985          return _defineProperty({
 11986            next: function next() {
 11987              if (i < length) {
 11988                entry.value = _this[i++];
 11989              } else {
 11990                entry.value = undefined;
 11991                entry.done = true;
 11992              }
 11993  
 11994              return entry;
 11995            }
 11996          }, Symbol.iterator, function () {
 11997            // eslint-disable-line no-undef
 11998            return this;
 11999          });
 12000        };
 12001      }
 12002    };
 12003  
 12004    defineSymbolIterator();
 12005  
 12006    var getLayoutDimensionOptions = defaults({
 12007      nodeDimensionsIncludeLabels: false
 12008    });
 12009    var elesfn$q = {
 12010      // Calculates and returns node dimensions { x, y } based on options given
 12011      layoutDimensions: function layoutDimensions(options) {
 12012        options = getLayoutDimensionOptions(options);
 12013        var dims;
 12014  
 12015        if (!this.takesUpSpace()) {
 12016          dims = {
 12017            w: 0,
 12018            h: 0
 12019          };
 12020        } else if (options.nodeDimensionsIncludeLabels) {
 12021          var bbDim = this.boundingBox();
 12022          dims = {
 12023            w: bbDim.w,
 12024            h: bbDim.h
 12025          };
 12026        } else {
 12027          dims = {
 12028            w: this.outerWidth(),
 12029            h: this.outerHeight()
 12030          };
 12031        } // sanitise the dimensions for external layouts (avoid division by zero)
 12032  
 12033  
 12034        if (dims.w === 0 || dims.h === 0) {
 12035          dims.w = dims.h = 1;
 12036        }
 12037  
 12038        return dims;
 12039      },
 12040      // using standard layout options, apply position function (w/ or w/o animation)
 12041      layoutPositions: function layoutPositions(layout, options, fn) {
 12042        var nodes = this.nodes();
 12043        var cy = this.cy();
 12044        var layoutEles = options.eles; // nodes & edges
 12045  
 12046        var getMemoizeKey = function getMemoizeKey(node) {
 12047          return node.id();
 12048        };
 12049  
 12050        var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
 12051  
 12052        layout.emit({
 12053          type: 'layoutstart',
 12054          layout: layout
 12055        });
 12056        layout.animations = [];
 12057  
 12058        var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
 12059          var center = {
 12060            x: nodesBb.x1 + nodesBb.w / 2,
 12061            y: nodesBb.y1 + nodesBb.h / 2
 12062          };
 12063          var spacingVector = {
 12064            // scale from center of bounding box (not necessarily 0,0)
 12065            x: (pos.x - center.x) * spacing,
 12066            y: (pos.y - center.y) * spacing
 12067          };
 12068          return {
 12069            x: center.x + spacingVector.x,
 12070            y: center.y + spacingVector.y
 12071          };
 12072        };
 12073  
 12074        var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
 12075  
 12076        var spacingBb = function spacingBb() {
 12077          if (!useSpacingFactor) {
 12078            return null;
 12079          }
 12080  
 12081          var bb = makeBoundingBox();
 12082  
 12083          for (var i = 0; i < nodes.length; i++) {
 12084            var node = nodes[i];
 12085            var pos = fnMem(node, i);
 12086            expandBoundingBoxByPoint(bb, pos.x, pos.y);
 12087          }
 12088  
 12089          return bb;
 12090        };
 12091  
 12092        var bb = spacingBb();
 12093        var getFinalPos = memoize(function (node, i) {
 12094          var newPos = fnMem(node, i);
 12095  
 12096          if (useSpacingFactor) {
 12097            var spacing = Math.abs(options.spacingFactor);
 12098            newPos = calculateSpacing(spacing, bb, newPos);
 12099          }
 12100  
 12101          if (options.transform != null) {
 12102            newPos = options.transform(node, newPos);
 12103          }
 12104  
 12105          return newPos;
 12106        }, getMemoizeKey);
 12107  
 12108        if (options.animate) {
 12109          for (var i = 0; i < nodes.length; i++) {
 12110            var node = nodes[i];
 12111            var newPos = getFinalPos(node, i);
 12112            var animateNode = options.animateFilter == null || options.animateFilter(node, i);
 12113  
 12114            if (animateNode) {
 12115              var ani = node.animation({
 12116                position: newPos,
 12117                duration: options.animationDuration,
 12118                easing: options.animationEasing
 12119              });
 12120              layout.animations.push(ani);
 12121            } else {
 12122              node.position(newPos);
 12123            }
 12124          }
 12125  
 12126          if (options.fit) {
 12127            var fitAni = cy.animation({
 12128              fit: {
 12129                boundingBox: layoutEles.boundingBoxAt(getFinalPos),
 12130                padding: options.padding
 12131              },
 12132              duration: options.animationDuration,
 12133              easing: options.animationEasing
 12134            });
 12135            layout.animations.push(fitAni);
 12136          } else if (options.zoom !== undefined && options.pan !== undefined) {
 12137            var zoomPanAni = cy.animation({
 12138              zoom: options.zoom,
 12139              pan: options.pan,
 12140              duration: options.animationDuration,
 12141              easing: options.animationEasing
 12142            });
 12143            layout.animations.push(zoomPanAni);
 12144          }
 12145  
 12146          layout.animations.forEach(function (ani) {
 12147            return ani.play();
 12148          });
 12149          layout.one('layoutready', options.ready);
 12150          layout.emit({
 12151            type: 'layoutready',
 12152            layout: layout
 12153          });
 12154          Promise$1.all(layout.animations.map(function (ani) {
 12155            return ani.promise();
 12156          })).then(function () {
 12157            layout.one('layoutstop', options.stop);
 12158            layout.emit({
 12159              type: 'layoutstop',
 12160              layout: layout
 12161            });
 12162          });
 12163        } else {
 12164          nodes.positions(getFinalPos);
 12165  
 12166          if (options.fit) {
 12167            cy.fit(options.eles, options.padding);
 12168          }
 12169  
 12170          if (options.zoom != null) {
 12171            cy.zoom(options.zoom);
 12172          }
 12173  
 12174          if (options.pan) {
 12175            cy.pan(options.pan);
 12176          }
 12177  
 12178          layout.one('layoutready', options.ready);
 12179          layout.emit({
 12180            type: 'layoutready',
 12181            layout: layout
 12182          });
 12183          layout.one('layoutstop', options.stop);
 12184          layout.emit({
 12185            type: 'layoutstop',
 12186            layout: layout
 12187          });
 12188        }
 12189  
 12190        return this; // chaining
 12191      },
 12192      layout: function layout(options) {
 12193        var cy = this.cy();
 12194        return cy.makeLayout(extend({}, options, {
 12195          eles: this
 12196        }));
 12197      }
 12198    }; // aliases:
 12199  
 12200    elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
 12201  
 12202    function styleCache(key, fn, ele) {
 12203      var _p = ele._private;
 12204      var cache = _p.styleCache = _p.styleCache || [];
 12205      var val;
 12206  
 12207      if ((val = cache[key]) != null) {
 12208        return val;
 12209      } else {
 12210        val = cache[key] = fn(ele);
 12211        return val;
 12212      }
 12213    }
 12214  
 12215    function cacheStyleFunction(key, fn) {
 12216      key = hashString(key);
 12217      return function cachedStyleFunction(ele) {
 12218        return styleCache(key, fn, ele);
 12219      };
 12220    }
 12221  
 12222    function cachePrototypeStyleFunction(key, fn) {
 12223      key = hashString(key);
 12224  
 12225      var selfFn = function selfFn(ele) {
 12226        return fn.call(ele);
 12227      };
 12228  
 12229      return function cachedPrototypeStyleFunction() {
 12230        var ele = this[0];
 12231  
 12232        if (ele) {
 12233          return styleCache(key, selfFn, ele);
 12234        }
 12235      };
 12236    }
 12237  
 12238    var elesfn$r = {
 12239      recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
 12240        var cy = this.cy();
 12241        var renderer = cy.renderer();
 12242        var styleEnabled = cy.styleEnabled();
 12243  
 12244        if (renderer && styleEnabled) {
 12245          renderer.recalculateRenderedStyle(this, useCache);
 12246        }
 12247  
 12248        return this;
 12249      },
 12250      dirtyStyleCache: function dirtyStyleCache() {
 12251        var cy = this.cy();
 12252  
 12253        var dirty = function dirty(ele) {
 12254          return ele._private.styleCache = null;
 12255        };
 12256  
 12257        if (cy.hasCompoundNodes()) {
 12258          var eles;
 12259          eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
 12260          eles.merge(eles.connectedEdges());
 12261          eles.forEach(dirty);
 12262        } else {
 12263          this.forEach(function (ele) {
 12264            dirty(ele);
 12265            ele.connectedEdges().forEach(dirty);
 12266          });
 12267        }
 12268  
 12269        return this;
 12270      },
 12271      // fully updates (recalculates) the style for the elements
 12272      updateStyle: function updateStyle(notifyRenderer) {
 12273        var cy = this._private.cy;
 12274  
 12275        if (!cy.styleEnabled()) {
 12276          return this;
 12277        }
 12278  
 12279        if (cy.batching()) {
 12280          var bEles = cy._private.batchStyleEles;
 12281          bEles.merge(this);
 12282          return this; // chaining and exit early when batching
 12283        }
 12284  
 12285        var hasCompounds = cy.hasCompoundNodes();
 12286        var style = cy.style();
 12287        var updatedEles = this;
 12288        notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
 12289  
 12290        if (hasCompounds) {
 12291          // then add everything up and down for compound selector checks
 12292          updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
 12293        }
 12294  
 12295        var changedEles = style.apply(updatedEles);
 12296  
 12297        if (notifyRenderer) {
 12298          changedEles.emitAndNotify('style'); // let renderer know we changed style
 12299        } else {
 12300          changedEles.emit('style'); // just fire the event
 12301        }
 12302  
 12303        return this; // chaining
 12304      },
 12305      // get the internal parsed style object for the specified property
 12306      parsedStyle: function parsedStyle(property) {
 12307        var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
 12308        var ele = this[0];
 12309        var cy = ele.cy();
 12310  
 12311        if (!cy.styleEnabled()) {
 12312          return;
 12313        }
 12314  
 12315        if (ele) {
 12316          var overriddenStyle = ele._private.style[property];
 12317  
 12318          if (overriddenStyle != null) {
 12319            return overriddenStyle;
 12320          } else if (includeNonDefault) {
 12321            return cy.style().getDefaultProperty(property);
 12322          } else {
 12323            return null;
 12324          }
 12325        }
 12326      },
 12327      numericStyle: function numericStyle(property) {
 12328        var ele = this[0];
 12329  
 12330        if (!ele.cy().styleEnabled()) {
 12331          return;
 12332        }
 12333  
 12334        if (ele) {
 12335          var pstyle = ele.pstyle(property);
 12336          return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
 12337        }
 12338      },
 12339      numericStyleUnits: function numericStyleUnits(property) {
 12340        var ele = this[0];
 12341  
 12342        if (!ele.cy().styleEnabled()) {
 12343          return;
 12344        }
 12345  
 12346        if (ele) {
 12347          return ele.pstyle(property).units;
 12348        }
 12349      },
 12350      // get the specified css property as a rendered value (i.e. on-screen value)
 12351      // or get the whole rendered style if no property specified (NB doesn't allow setting)
 12352      renderedStyle: function renderedStyle(property) {
 12353        var cy = this.cy();
 12354  
 12355        if (!cy.styleEnabled()) {
 12356          return this;
 12357        }
 12358  
 12359        var ele = this[0];
 12360  
 12361        if (ele) {
 12362          return cy.style().getRenderedStyle(ele, property);
 12363        }
 12364      },
 12365      // read the calculated css style of the element or override the style (via a bypass)
 12366      style: function style(name, value) {
 12367        var cy = this.cy();
 12368  
 12369        if (!cy.styleEnabled()) {
 12370          return this;
 12371        }
 12372  
 12373        var updateTransitions = false;
 12374        var style = cy.style();
 12375  
 12376        if (plainObject(name)) {
 12377          // then extend the bypass
 12378          var props = name;
 12379          style.applyBypass(this, props, updateTransitions);
 12380          this.emitAndNotify('style'); // let the renderer know we've updated style
 12381        } else if (string(name)) {
 12382          if (value === undefined) {
 12383            // then get the property from the style
 12384            var ele = this[0];
 12385  
 12386            if (ele) {
 12387              return style.getStylePropertyValue(ele, name);
 12388            } else {
 12389              // empty collection => can't get any value
 12390              return;
 12391            }
 12392          } else {
 12393            // then set the bypass with the property value
 12394            style.applyBypass(this, name, value, updateTransitions);
 12395            this.emitAndNotify('style'); // let the renderer know we've updated style
 12396          }
 12397        } else if (name === undefined) {
 12398          var _ele = this[0];
 12399  
 12400          if (_ele) {
 12401            return style.getRawStyle(_ele);
 12402          } else {
 12403            // empty collection => can't get any value
 12404            return;
 12405          }
 12406        }
 12407  
 12408        return this; // chaining
 12409      },
 12410      removeStyle: function removeStyle(names) {
 12411        var cy = this.cy();
 12412  
 12413        if (!cy.styleEnabled()) {
 12414          return this;
 12415        }
 12416  
 12417        var updateTransitions = false;
 12418        var style = cy.style();
 12419        var eles = this;
 12420  
 12421        if (names === undefined) {
 12422          for (var i = 0; i < eles.length; i++) {
 12423            var ele = eles[i];
 12424            style.removeAllBypasses(ele, updateTransitions);
 12425          }
 12426        } else {
 12427          names = names.split(/\s+/);
 12428  
 12429          for (var _i = 0; _i < eles.length; _i++) {
 12430            var _ele2 = eles[_i];
 12431            style.removeBypasses(_ele2, names, updateTransitions);
 12432          }
 12433        }
 12434  
 12435        this.emitAndNotify('style'); // let the renderer know we've updated style
 12436  
 12437        return this; // chaining
 12438      },
 12439      show: function show() {
 12440        this.css('display', 'element');
 12441        return this; // chaining
 12442      },
 12443      hide: function hide() {
 12444        this.css('display', 'none');
 12445        return this; // chaining
 12446      },
 12447      effectiveOpacity: function effectiveOpacity() {
 12448        var cy = this.cy();
 12449  
 12450        if (!cy.styleEnabled()) {
 12451          return 1;
 12452        }
 12453  
 12454        var hasCompoundNodes = cy.hasCompoundNodes();
 12455        var ele = this[0];
 12456  
 12457        if (ele) {
 12458          var _p = ele._private;
 12459          var parentOpacity = ele.pstyle('opacity').value;
 12460  
 12461          if (!hasCompoundNodes) {
 12462            return parentOpacity;
 12463          }
 12464  
 12465          var parents = !_p.data.parent ? null : ele.parents();
 12466  
 12467          if (parents) {
 12468            for (var i = 0; i < parents.length; i++) {
 12469              var parent = parents[i];
 12470              var opacity = parent.pstyle('opacity').value;
 12471              parentOpacity = opacity * parentOpacity;
 12472            }
 12473          }
 12474  
 12475          return parentOpacity;
 12476        }
 12477      },
 12478      transparent: function transparent() {
 12479        var cy = this.cy();
 12480  
 12481        if (!cy.styleEnabled()) {
 12482          return false;
 12483        }
 12484  
 12485        var ele = this[0];
 12486        var hasCompoundNodes = ele.cy().hasCompoundNodes();
 12487  
 12488        if (ele) {
 12489          if (!hasCompoundNodes) {
 12490            return ele.pstyle('opacity').value === 0;
 12491          } else {
 12492            return ele.effectiveOpacity() === 0;
 12493          }
 12494        }
 12495      },
 12496      backgrounding: function backgrounding() {
 12497        var cy = this.cy();
 12498  
 12499        if (!cy.styleEnabled()) {
 12500          return false;
 12501        }
 12502  
 12503        var ele = this[0];
 12504        return ele._private.backgrounding ? true : false;
 12505      }
 12506    };
 12507  
 12508    function checkCompound(ele, parentOk) {
 12509      var _p = ele._private;
 12510      var parents = _p.data.parent ? ele.parents() : null;
 12511  
 12512      if (parents) {
 12513        for (var i = 0; i < parents.length; i++) {
 12514          var parent = parents[i];
 12515  
 12516          if (!parentOk(parent)) {
 12517            return false;
 12518          }
 12519        }
 12520      }
 12521  
 12522      return true;
 12523    }
 12524  
 12525    function defineDerivedStateFunction(specs) {
 12526      var ok = specs.ok;
 12527      var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
 12528      var parentOk = specs.parentOk || specs.ok;
 12529      return function () {
 12530        var cy = this.cy();
 12531  
 12532        if (!cy.styleEnabled()) {
 12533          return true;
 12534        }
 12535  
 12536        var ele = this[0];
 12537        var hasCompoundNodes = cy.hasCompoundNodes();
 12538  
 12539        if (ele) {
 12540          var _p = ele._private;
 12541  
 12542          if (!ok(ele)) {
 12543            return false;
 12544          }
 12545  
 12546          if (ele.isNode()) {
 12547            return !hasCompoundNodes || checkCompound(ele, parentOk);
 12548          } else {
 12549            var src = _p.source;
 12550            var tgt = _p.target;
 12551            return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
 12552          }
 12553        }
 12554      };
 12555    }
 12556  
 12557    var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
 12558      return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
 12559    });
 12560    elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
 12561      ok: eleTakesUpSpace
 12562    }));
 12563    var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
 12564      return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
 12565    });
 12566    var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
 12567      return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
 12568    });
 12569    elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
 12570      ok: eleInteractive,
 12571      parentOk: parentInteractive,
 12572      edgeOkViaNode: eleTakesUpSpace
 12573    }));
 12574  
 12575    elesfn$r.noninteractive = function () {
 12576      var ele = this[0];
 12577  
 12578      if (ele) {
 12579        return !ele.interactive();
 12580      }
 12581    };
 12582  
 12583    var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
 12584      return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
 12585    });
 12586    var edgeVisibleViaNode = eleTakesUpSpace;
 12587    elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
 12588      ok: eleVisible,
 12589      edgeOkViaNode: edgeVisibleViaNode
 12590    }));
 12591  
 12592    elesfn$r.hidden = function () {
 12593      var ele = this[0];
 12594  
 12595      if (ele) {
 12596        return !ele.visible();
 12597      }
 12598    };
 12599  
 12600    elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
 12601      if (!this.cy().styleEnabled()) {
 12602        return false;
 12603      }
 12604  
 12605      return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
 12606    });
 12607    elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
 12608    elesfn$r.renderedCss = elesfn$r.renderedStyle;
 12609    elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
 12610    elesfn$r.pstyle = elesfn$r.parsedStyle;
 12611  
 12612    var elesfn$s = {};
 12613  
 12614    function defineSwitchFunction(params) {
 12615      return function () {
 12616        var args = arguments;
 12617        var changedEles = []; // e.g. cy.nodes().select( data, handler )
 12618  
 12619        if (args.length === 2) {
 12620          var data = args[0];
 12621          var handler = args[1];
 12622          this.on(params.event, data, handler);
 12623        } // e.g. cy.nodes().select( handler )
 12624        else if (args.length === 1 && fn(args[0])) {
 12625            var _handler = args[0];
 12626            this.on(params.event, _handler);
 12627          } // e.g. cy.nodes().select()
 12628          // e.g. (private) cy.nodes().select(['tapselect'])
 12629          else if (args.length === 0 || args.length === 1 && array(args[0])) {
 12630              var addlEvents = args.length === 1 ? args[0] : null;
 12631  
 12632              for (var i = 0; i < this.length; i++) {
 12633                var ele = this[i];
 12634                var able = !params.ableField || ele._private[params.ableField];
 12635                var changed = ele._private[params.field] != params.value;
 12636  
 12637                if (params.overrideAble) {
 12638                  var overrideAble = params.overrideAble(ele);
 12639  
 12640                  if (overrideAble !== undefined) {
 12641                    able = overrideAble;
 12642  
 12643                    if (!overrideAble) {
 12644                      return this;
 12645                    } // to save cycles assume not able for all on override
 12646  
 12647                  }
 12648                }
 12649  
 12650                if (able) {
 12651                  ele._private[params.field] = params.value;
 12652  
 12653                  if (changed) {
 12654                    changedEles.push(ele);
 12655                  }
 12656                }
 12657              }
 12658  
 12659              var changedColl = this.spawn(changedEles);
 12660              changedColl.updateStyle(); // change of state => possible change of style
 12661  
 12662              changedColl.emit(params.event);
 12663  
 12664              if (addlEvents) {
 12665                changedColl.emit(addlEvents);
 12666              }
 12667            }
 12668  
 12669        return this;
 12670      };
 12671    }
 12672  
 12673    function defineSwitchSet(params) {
 12674      elesfn$s[params.field] = function () {
 12675        var ele = this[0];
 12676  
 12677        if (ele) {
 12678          if (params.overrideField) {
 12679            var val = params.overrideField(ele);
 12680  
 12681            if (val !== undefined) {
 12682              return val;
 12683            }
 12684          }
 12685  
 12686          return ele._private[params.field];
 12687        }
 12688      };
 12689  
 12690      elesfn$s[params.on] = defineSwitchFunction({
 12691        event: params.on,
 12692        field: params.field,
 12693        ableField: params.ableField,
 12694        overrideAble: params.overrideAble,
 12695        value: true
 12696      });
 12697      elesfn$s[params.off] = defineSwitchFunction({
 12698        event: params.off,
 12699        field: params.field,
 12700        ableField: params.ableField,
 12701        overrideAble: params.overrideAble,
 12702        value: false
 12703      });
 12704    }
 12705  
 12706    defineSwitchSet({
 12707      field: 'locked',
 12708      overrideField: function overrideField(ele) {
 12709        return ele.cy().autolock() ? true : undefined;
 12710      },
 12711      on: 'lock',
 12712      off: 'unlock'
 12713    });
 12714    defineSwitchSet({
 12715      field: 'grabbable',
 12716      overrideField: function overrideField(ele) {
 12717        return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
 12718      },
 12719      on: 'grabify',
 12720      off: 'ungrabify'
 12721    });
 12722    defineSwitchSet({
 12723      field: 'selected',
 12724      ableField: 'selectable',
 12725      overrideAble: function overrideAble(ele) {
 12726        return ele.cy().autounselectify() ? false : undefined;
 12727      },
 12728      on: 'select',
 12729      off: 'unselect'
 12730    });
 12731    defineSwitchSet({
 12732      field: 'selectable',
 12733      overrideField: function overrideField(ele) {
 12734        return ele.cy().autounselectify() ? false : undefined;
 12735      },
 12736      on: 'selectify',
 12737      off: 'unselectify'
 12738    });
 12739    elesfn$s.deselect = elesfn$s.unselect;
 12740  
 12741    elesfn$s.grabbed = function () {
 12742      var ele = this[0];
 12743  
 12744      if (ele) {
 12745        return ele._private.grabbed;
 12746      }
 12747    };
 12748  
 12749    defineSwitchSet({
 12750      field: 'active',
 12751      on: 'activate',
 12752      off: 'unactivate'
 12753    });
 12754    defineSwitchSet({
 12755      field: 'pannable',
 12756      on: 'panify',
 12757      off: 'unpanify'
 12758    });
 12759  
 12760    elesfn$s.inactive = function () {
 12761      var ele = this[0];
 12762  
 12763      if (ele) {
 12764        return !ele._private.active;
 12765      }
 12766    };
 12767  
 12768    var elesfn$t = {}; // DAG functions
 12769    ////////////////
 12770  
 12771    var defineDagExtremity = function defineDagExtremity(params) {
 12772      return function dagExtremityImpl(selector) {
 12773        var eles = this;
 12774        var ret = [];
 12775  
 12776        for (var i = 0; i < eles.length; i++) {
 12777          var ele = eles[i];
 12778  
 12779          if (!ele.isNode()) {
 12780            continue;
 12781          }
 12782  
 12783          var disqualified = false;
 12784          var edges = ele.connectedEdges();
 12785  
 12786          for (var j = 0; j < edges.length; j++) {
 12787            var edge = edges[j];
 12788            var src = edge.source();
 12789            var tgt = edge.target();
 12790  
 12791            if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
 12792              disqualified = true;
 12793              break;
 12794            }
 12795          }
 12796  
 12797          if (!disqualified) {
 12798            ret.push(ele);
 12799          }
 12800        }
 12801  
 12802        return this.spawn(ret, {
 12803          unique: true
 12804        }).filter(selector);
 12805      };
 12806    };
 12807  
 12808    var defineDagOneHop = function defineDagOneHop(params) {
 12809      return function (selector) {
 12810        var eles = this;
 12811        var oEles = [];
 12812  
 12813        for (var i = 0; i < eles.length; i++) {
 12814          var ele = eles[i];
 12815  
 12816          if (!ele.isNode()) {
 12817            continue;
 12818          }
 12819  
 12820          var edges = ele.connectedEdges();
 12821  
 12822          for (var j = 0; j < edges.length; j++) {
 12823            var edge = edges[j];
 12824            var src = edge.source();
 12825            var tgt = edge.target();
 12826  
 12827            if (params.outgoing && src === ele) {
 12828              oEles.push(edge);
 12829              oEles.push(tgt);
 12830            } else if (params.incoming && tgt === ele) {
 12831              oEles.push(edge);
 12832              oEles.push(src);
 12833            }
 12834          }
 12835        }
 12836  
 12837        return this.spawn(oEles, {
 12838          unique: true
 12839        }).filter(selector);
 12840      };
 12841    };
 12842  
 12843    var defineDagAllHops = function defineDagAllHops(params) {
 12844      return function (selector) {
 12845        var eles = this;
 12846        var sEles = [];
 12847        var sElesIds = {};
 12848  
 12849        for (;;) {
 12850          var next = params.outgoing ? eles.outgoers() : eles.incomers();
 12851  
 12852          if (next.length === 0) {
 12853            break;
 12854          } // done if none left
 12855  
 12856  
 12857          var newNext = false;
 12858  
 12859          for (var i = 0; i < next.length; i++) {
 12860            var n = next[i];
 12861            var nid = n.id();
 12862  
 12863            if (!sElesIds[nid]) {
 12864              sElesIds[nid] = true;
 12865              sEles.push(n);
 12866              newNext = true;
 12867            }
 12868          }
 12869  
 12870          if (!newNext) {
 12871            break;
 12872          } // done if touched all outgoers already
 12873  
 12874  
 12875          eles = next;
 12876        }
 12877  
 12878        return this.spawn(sEles, {
 12879          unique: true
 12880        }).filter(selector);
 12881      };
 12882    };
 12883  
 12884    elesfn$t.clearTraversalCache = function () {
 12885      for (var i = 0; i < this.length; i++) {
 12886        this[i]._private.traversalCache = null;
 12887      }
 12888    };
 12889  
 12890    extend(elesfn$t, {
 12891      // get the root nodes in the DAG
 12892      roots: defineDagExtremity({
 12893        noIncomingEdges: true
 12894      }),
 12895      // get the leaf nodes in the DAG
 12896      leaves: defineDagExtremity({
 12897        noOutgoingEdges: true
 12898      }),
 12899      // normally called children in graph theory
 12900      // these nodes =edges=> outgoing nodes
 12901      outgoers: cache(defineDagOneHop({
 12902        outgoing: true
 12903      }), 'outgoers'),
 12904      // aka DAG descendants
 12905      successors: defineDagAllHops({
 12906        outgoing: true
 12907      }),
 12908      // normally called parents in graph theory
 12909      // these nodes <=edges= incoming nodes
 12910      incomers: cache(defineDagOneHop({
 12911        incoming: true
 12912      }), 'incomers'),
 12913      // aka DAG ancestors
 12914      predecessors: defineDagAllHops({
 12915        incoming: true
 12916      })
 12917    }); // Neighbourhood functions
 12918    //////////////////////////
 12919  
 12920    extend(elesfn$t, {
 12921      neighborhood: cache(function (selector) {
 12922        var elements = [];
 12923        var nodes = this.nodes();
 12924  
 12925        for (var i = 0; i < nodes.length; i++) {
 12926          // for all nodes
 12927          var node = nodes[i];
 12928          var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
 12929  
 12930          for (var j = 0; j < connectedEdges.length; j++) {
 12931            var edge = connectedEdges[j];
 12932            var src = edge.source();
 12933            var tgt = edge.target();
 12934            var otherNode = node === src ? tgt : src; // need check in case of loop
 12935  
 12936            if (otherNode.length > 0) {
 12937              elements.push(otherNode[0]); // add node 1 hop away
 12938            } // add connected edge
 12939  
 12940  
 12941            elements.push(edge[0]);
 12942          }
 12943        }
 12944  
 12945        return this.spawn(elements, {
 12946          unique: true
 12947        }).filter(selector);
 12948      }, 'neighborhood'),
 12949      closedNeighborhood: function closedNeighborhood(selector) {
 12950        return this.neighborhood().add(this).filter(selector);
 12951      },
 12952      openNeighborhood: function openNeighborhood(selector) {
 12953        return this.neighborhood(selector);
 12954      }
 12955    }); // aliases
 12956  
 12957    elesfn$t.neighbourhood = elesfn$t.neighborhood;
 12958    elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
 12959    elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
 12960    /////////////////
 12961  
 12962    extend(elesfn$t, {
 12963      source: cache(function sourceImpl(selector) {
 12964        var ele = this[0];
 12965        var src;
 12966  
 12967        if (ele) {
 12968          src = ele._private.source || ele.cy().collection();
 12969        }
 12970  
 12971        return src && selector ? src.filter(selector) : src;
 12972      }, 'source'),
 12973      target: cache(function targetImpl(selector) {
 12974        var ele = this[0];
 12975        var tgt;
 12976  
 12977        if (ele) {
 12978          tgt = ele._private.target || ele.cy().collection();
 12979        }
 12980  
 12981        return tgt && selector ? tgt.filter(selector) : tgt;
 12982      }, 'target'),
 12983      sources: defineSourceFunction({
 12984        attr: 'source'
 12985      }),
 12986      targets: defineSourceFunction({
 12987        attr: 'target'
 12988      })
 12989    });
 12990  
 12991    function defineSourceFunction(params) {
 12992      return function sourceImpl(selector) {
 12993        var sources = [];
 12994  
 12995        for (var i = 0; i < this.length; i++) {
 12996          var ele = this[i];
 12997          var src = ele._private[params.attr];
 12998  
 12999          if (src) {
 13000            sources.push(src);
 13001          }
 13002        }
 13003  
 13004        return this.spawn(sources, {
 13005          unique: true
 13006        }).filter(selector);
 13007      };
 13008    }
 13009  
 13010    extend(elesfn$t, {
 13011      edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
 13012      edgesTo: cache(defineEdgesWithFunction({
 13013        thisIsSrc: true
 13014      }), 'edgesTo')
 13015    });
 13016  
 13017    function defineEdgesWithFunction(params) {
 13018      return function edgesWithImpl(otherNodes) {
 13019        var elements = [];
 13020        var cy = this._private.cy;
 13021        var p = params || {}; // get elements if a selector is specified
 13022  
 13023        if (string(otherNodes)) {
 13024          otherNodes = cy.$(otherNodes);
 13025        }
 13026  
 13027        for (var h = 0; h < otherNodes.length; h++) {
 13028          var edges = otherNodes[h]._private.edges;
 13029  
 13030          for (var i = 0; i < edges.length; i++) {
 13031            var edge = edges[i];
 13032            var edgeData = edge._private.data;
 13033            var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
 13034            var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
 13035            var edgeConnectsThisAndOther = thisToOther || otherToThis;
 13036  
 13037            if (!edgeConnectsThisAndOther) {
 13038              continue;
 13039            }
 13040  
 13041            if (p.thisIsSrc || p.thisIsTgt) {
 13042              if (p.thisIsSrc && !thisToOther) {
 13043                continue;
 13044              }
 13045  
 13046              if (p.thisIsTgt && !otherToThis) {
 13047                continue;
 13048              }
 13049            }
 13050  
 13051            elements.push(edge);
 13052          }
 13053        }
 13054  
 13055        return this.spawn(elements, {
 13056          unique: true
 13057        });
 13058      };
 13059    }
 13060  
 13061    extend(elesfn$t, {
 13062      connectedEdges: cache(function (selector) {
 13063        var retEles = [];
 13064        var eles = this;
 13065  
 13066        for (var i = 0; i < eles.length; i++) {
 13067          var node = eles[i];
 13068  
 13069          if (!node.isNode()) {
 13070            continue;
 13071          }
 13072  
 13073          var edges = node._private.edges;
 13074  
 13075          for (var j = 0; j < edges.length; j++) {
 13076            var edge = edges[j];
 13077            retEles.push(edge);
 13078          }
 13079        }
 13080  
 13081        return this.spawn(retEles, {
 13082          unique: true
 13083        }).filter(selector);
 13084      }, 'connectedEdges'),
 13085      connectedNodes: cache(function (selector) {
 13086        var retEles = [];
 13087        var eles = this;
 13088  
 13089        for (var i = 0; i < eles.length; i++) {
 13090          var edge = eles[i];
 13091  
 13092          if (!edge.isEdge()) {
 13093            continue;
 13094          }
 13095  
 13096          retEles.push(edge.source()[0]);
 13097          retEles.push(edge.target()[0]);
 13098        }
 13099  
 13100        return this.spawn(retEles, {
 13101          unique: true
 13102        }).filter(selector);
 13103      }, 'connectedNodes'),
 13104      parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
 13105      codirectedEdges: cache(defineParallelEdgesFunction({
 13106        codirected: true
 13107      }), 'codirectedEdges')
 13108    });
 13109  
 13110    function defineParallelEdgesFunction(params) {
 13111      var defaults = {
 13112        codirected: false
 13113      };
 13114      params = extend({}, defaults, params);
 13115      return function parallelEdgesImpl(selector) {
 13116        // micro-optimised for renderer
 13117        var elements = [];
 13118        var edges = this.edges();
 13119        var p = params; // look at all the edges in the collection
 13120  
 13121        for (var i = 0; i < edges.length; i++) {
 13122          var edge1 = edges[i];
 13123          var edge1_p = edge1._private;
 13124          var src1 = edge1_p.source;
 13125          var srcid1 = src1._private.data.id;
 13126          var tgtid1 = edge1_p.data.target;
 13127          var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
 13128  
 13129          for (var j = 0; j < srcEdges1.length; j++) {
 13130            var edge2 = srcEdges1[j];
 13131            var edge2data = edge2._private.data;
 13132            var tgtid2 = edge2data.target;
 13133            var srcid2 = edge2data.source;
 13134            var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
 13135            var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
 13136  
 13137            if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
 13138              elements.push(edge2);
 13139            }
 13140          }
 13141        }
 13142  
 13143        return this.spawn(elements, {
 13144          unique: true
 13145        }).filter(selector);
 13146      };
 13147    } // Misc functions
 13148    /////////////////
 13149  
 13150  
 13151    extend(elesfn$t, {
 13152      components: function components(root) {
 13153        var self = this;
 13154        var cy = self.cy();
 13155        var visited = cy.collection();
 13156        var unvisited = root == null ? self.nodes() : root.nodes();
 13157        var components = [];
 13158  
 13159        if (root != null && unvisited.empty()) {
 13160          // root may contain only edges
 13161          unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
 13162        }
 13163  
 13164        var visitInComponent = function visitInComponent(node, component) {
 13165          visited.merge(node);
 13166          unvisited.unmerge(node);
 13167          component.merge(node);
 13168        };
 13169  
 13170        if (unvisited.empty()) {
 13171          return self.spawn();
 13172        }
 13173  
 13174        var _loop = function _loop() {
 13175          // each iteration yields a component
 13176          var cmpt = cy.collection();
 13177          components.push(cmpt);
 13178          var root = unvisited[0];
 13179          visitInComponent(root, cmpt);
 13180          self.bfs({
 13181            directed: false,
 13182            roots: root,
 13183            visit: function visit(v) {
 13184              return visitInComponent(v, cmpt);
 13185            }
 13186          });
 13187          cmpt.forEach(function (node) {
 13188            node.connectedEdges().forEach(function (e) {
 13189              // connectedEdges() usually cached
 13190              if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
 13191                // has() is cheap
 13192                cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
 13193              }
 13194            });
 13195          });
 13196        };
 13197  
 13198        do {
 13199          _loop();
 13200        } while (unvisited.length > 0);
 13201  
 13202        return components;
 13203      },
 13204      component: function component() {
 13205        var ele = this[0];
 13206        return ele.cy().mutableElements().components(ele)[0];
 13207      }
 13208    });
 13209    elesfn$t.componentsOf = elesfn$t.components;
 13210  
 13211    var idFactory = {
 13212      generate: function generate(cy, element, tryThisId) {
 13213        var id = tryThisId != null ? tryThisId : uuid();
 13214  
 13215        while (cy.hasElementWithId(id)) {
 13216          id = uuid();
 13217        }
 13218  
 13219        return id;
 13220      }
 13221    }; // represents a set of nodes, edges, or both together
 13222  
 13223    var Collection = function Collection(cy, elements, options) {
 13224      if (cy === undefined || !core(cy)) {
 13225        error('A collection must have a reference to the core');
 13226        return;
 13227      }
 13228  
 13229      var map = new Map$1();
 13230      var createdElements = false;
 13231  
 13232      if (!elements) {
 13233        elements = [];
 13234      } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
 13235        createdElements = true; // make elements from json and restore all at once later
 13236  
 13237        var eles = [];
 13238        var elesIds = new Set$1();
 13239  
 13240        for (var i = 0, l = elements.length; i < l; i++) {
 13241          var json = elements[i];
 13242  
 13243          if (json.data == null) {
 13244            json.data = {};
 13245          }
 13246  
 13247          var _data = json.data; // make sure newly created elements have valid ids
 13248  
 13249          if (_data.id == null) {
 13250            _data.id = idFactory.generate(cy, json);
 13251          } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
 13252            continue; // can't create element if prior id already exists
 13253          }
 13254  
 13255          var ele = new Element(cy, json, false);
 13256          eles.push(ele);
 13257          elesIds.add(_data.id);
 13258        }
 13259  
 13260        elements = eles;
 13261      }
 13262  
 13263      this.length = 0;
 13264  
 13265      for (var _i = 0, _l = elements.length; _i < _l; _i++) {
 13266        var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
 13267  
 13268        if (element$1 == null) {
 13269          continue;
 13270        }
 13271  
 13272        var id = element$1._private.data.id;
 13273  
 13274        if (options == null || options.unique && !map.has(id)) {
 13275          map.set(id, {
 13276            index: this.length,
 13277            ele: element$1
 13278          });
 13279          this[this.length] = element$1;
 13280          this.length++;
 13281        }
 13282      }
 13283  
 13284      this._private = {
 13285        cy: cy,
 13286        map: map
 13287      }; // restore the elements if we created them from json
 13288  
 13289      if (createdElements) {
 13290        this.restore();
 13291      }
 13292    }; // Functions
 13293    ////////////////////////////////////////////////////////////////////////////////////////////////////
 13294    // keep the prototypes in sync (an element has the same functions as a collection)
 13295    // and use elefn and elesfn as shorthands to the prototypes
 13296  
 13297  
 13298    var elesfn$u = Element.prototype = Collection.prototype;
 13299  
 13300    elesfn$u.instanceString = function () {
 13301      return 'collection';
 13302    };
 13303  
 13304    elesfn$u.spawn = function (cy, eles, opts) {
 13305      if (!core(cy)) {
 13306        // cy is optional
 13307        opts = eles;
 13308        eles = cy;
 13309        cy = this.cy();
 13310      }
 13311  
 13312      return new Collection(cy, eles, opts);
 13313    };
 13314  
 13315    elesfn$u.spawnSelf = function () {
 13316      return this.spawn(this);
 13317    };
 13318  
 13319    elesfn$u.cy = function () {
 13320      return this._private.cy;
 13321    };
 13322  
 13323    elesfn$u.renderer = function () {
 13324      return this._private.cy.renderer();
 13325    };
 13326  
 13327    elesfn$u.element = function () {
 13328      return this[0];
 13329    };
 13330  
 13331    elesfn$u.collection = function () {
 13332      if (collection(this)) {
 13333        return this;
 13334      } else {
 13335        // an element
 13336        return new Collection(this._private.cy, [this]);
 13337      }
 13338    };
 13339  
 13340    elesfn$u.unique = function () {
 13341      return new Collection(this._private.cy, this, {
 13342        unique: true
 13343      });
 13344    };
 13345  
 13346    elesfn$u.hasElementWithId = function (id) {
 13347      id = '' + id; // id must be string
 13348  
 13349      return this._private.map.has(id);
 13350    };
 13351  
 13352    elesfn$u.getElementById = function (id) {
 13353      id = '' + id; // id must be string
 13354  
 13355      var cy = this._private.cy;
 13356  
 13357      var entry = this._private.map.get(id);
 13358  
 13359      return entry ? entry.ele : new Collection(cy); // get ele or empty collection
 13360    };
 13361  
 13362    elesfn$u.$id = elesfn$u.getElementById;
 13363  
 13364    elesfn$u.poolIndex = function () {
 13365      var cy = this._private.cy;
 13366      var eles = cy._private.elements;
 13367      var id = this[0]._private.data.id;
 13368      return eles._private.map.get(id).index;
 13369    };
 13370  
 13371    elesfn$u.indexOf = function (ele) {
 13372      var id = ele[0]._private.data.id;
 13373      return this._private.map.get(id).index;
 13374    };
 13375  
 13376    elesfn$u.indexOfId = function (id) {
 13377      id = '' + id; // id must be string
 13378  
 13379      return this._private.map.get(id).index;
 13380    };
 13381  
 13382    elesfn$u.json = function (obj) {
 13383      var ele = this.element();
 13384      var cy = this.cy();
 13385  
 13386      if (ele == null && obj) {
 13387        return this;
 13388      } // can't set to no eles
 13389  
 13390  
 13391      if (ele == null) {
 13392        return undefined;
 13393      } // can't get from no eles
 13394  
 13395  
 13396      var p = ele._private;
 13397  
 13398      if (plainObject(obj)) {
 13399        // set
 13400        cy.startBatch();
 13401  
 13402        if (obj.data) {
 13403          ele.data(obj.data);
 13404          var _data2 = p.data;
 13405  
 13406          if (ele.isEdge()) {
 13407            // source and target are immutable via data()
 13408            var move = false;
 13409            var spec = {};
 13410            var src = obj.data.source;
 13411            var tgt = obj.data.target;
 13412  
 13413            if (src != null && src != _data2.source) {
 13414              spec.source = '' + src; // id must be string
 13415  
 13416              move = true;
 13417            }
 13418  
 13419            if (tgt != null && tgt != _data2.target) {
 13420              spec.target = '' + tgt; // id must be string
 13421  
 13422              move = true;
 13423            }
 13424  
 13425            if (move) {
 13426              ele = ele.move(spec);
 13427            }
 13428          } else {
 13429            // parent is immutable via data()
 13430            var newParentValSpecd = 'parent' in obj.data;
 13431            var parent = obj.data.parent;
 13432  
 13433            if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
 13434              if (parent === undefined) {
 13435                // can't set undefined imperatively, so use null
 13436                parent = null;
 13437              }
 13438  
 13439              if (parent != null) {
 13440                parent = '' + parent; // id must be string
 13441              }
 13442  
 13443              ele = ele.move({
 13444                parent: parent
 13445              });
 13446            }
 13447          }
 13448        }
 13449  
 13450        if (obj.position) {
 13451          ele.position(obj.position);
 13452        } // ignore group -- immutable
 13453  
 13454  
 13455        var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
 13456          var obj_k = obj[k];
 13457  
 13458          if (obj_k != null && obj_k !== p[k]) {
 13459            if (obj_k) {
 13460              ele[trueFnName]();
 13461            } else {
 13462              ele[falseFnName]();
 13463            }
 13464          }
 13465        };
 13466  
 13467        checkSwitch('removed', 'remove', 'restore');
 13468        checkSwitch('selected', 'select', 'unselect');
 13469        checkSwitch('selectable', 'selectify', 'unselectify');
 13470        checkSwitch('locked', 'lock', 'unlock');
 13471        checkSwitch('grabbable', 'grabify', 'ungrabify');
 13472        checkSwitch('pannable', 'panify', 'unpanify');
 13473  
 13474        if (obj.classes != null) {
 13475          ele.classes(obj.classes);
 13476        }
 13477  
 13478        cy.endBatch();
 13479        return this;
 13480      } else if (obj === undefined) {
 13481        // get
 13482        var json = {
 13483          data: copy(p.data),
 13484          position: copy(p.position),
 13485          group: p.group,
 13486          removed: p.removed,
 13487          selected: p.selected,
 13488          selectable: p.selectable,
 13489          locked: p.locked,
 13490          grabbable: p.grabbable,
 13491          pannable: p.pannable,
 13492          classes: null
 13493        };
 13494        json.classes = '';
 13495        var i = 0;
 13496        p.classes.forEach(function (cls) {
 13497          return json.classes += i++ === 0 ? cls : ' ' + cls;
 13498        });
 13499        return json;
 13500      }
 13501    };
 13502  
 13503    elesfn$u.jsons = function () {
 13504      var jsons = [];
 13505  
 13506      for (var i = 0; i < this.length; i++) {
 13507        var ele = this[i];
 13508        var json = ele.json();
 13509        jsons.push(json);
 13510      }
 13511  
 13512      return jsons;
 13513    };
 13514  
 13515    elesfn$u.clone = function () {
 13516      var cy = this.cy();
 13517      var elesArr = [];
 13518  
 13519      for (var i = 0; i < this.length; i++) {
 13520        var ele = this[i];
 13521        var json = ele.json();
 13522        var clone = new Element(cy, json, false); // NB no restore
 13523  
 13524        elesArr.push(clone);
 13525      }
 13526  
 13527      return new Collection(cy, elesArr);
 13528    };
 13529  
 13530    elesfn$u.copy = elesfn$u.clone;
 13531  
 13532    elesfn$u.restore = function () {
 13533      var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
 13534      var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
 13535      var self = this;
 13536      var cy = self.cy();
 13537      var cy_p = cy._private; // create arrays of nodes and edges, since we need to
 13538      // restore the nodes first
 13539  
 13540      var nodes = [];
 13541      var edges = [];
 13542      var elements;
 13543  
 13544      for (var _i2 = 0, l = self.length; _i2 < l; _i2++) {
 13545        var ele = self[_i2];
 13546  
 13547        if (addToPool && !ele.removed()) {
 13548          // don't need to handle this ele
 13549          continue;
 13550        } // keep nodes first in the array and edges after
 13551  
 13552  
 13553        if (ele.isNode()) {
 13554          // put to front of array if node
 13555          nodes.push(ele);
 13556        } else {
 13557          // put to end of array if edge
 13558          edges.push(ele);
 13559        }
 13560      }
 13561  
 13562      elements = nodes.concat(edges);
 13563      var i;
 13564  
 13565      var removeFromElements = function removeFromElements() {
 13566        elements.splice(i, 1);
 13567        i--;
 13568      }; // now, restore each element
 13569  
 13570  
 13571      for (i = 0; i < elements.length; i++) {
 13572        var _ele = elements[i];
 13573        var _private = _ele._private;
 13574        var _data3 = _private.data; // the traversal cache should start fresh when ele is added
 13575  
 13576        _ele.clearTraversalCache(); // set id and validate
 13577  
 13578  
 13579        if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
 13580          _data3.id = idFactory.generate(cy, _ele);
 13581        } else if (number(_data3.id)) {
 13582          _data3.id = '' + _data3.id; // now it's a string
 13583        } else if (emptyString(_data3.id) || !string(_data3.id)) {
 13584          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
 13585  
 13586          removeFromElements();
 13587          continue;
 13588        } else if (cy.hasElementWithId(_data3.id)) {
 13589          error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
 13590  
 13591          removeFromElements();
 13592          continue;
 13593        }
 13594  
 13595        var id = _data3.id; // id is finalised, now let's keep a ref
 13596  
 13597        if (_ele.isNode()) {
 13598          // extra checks for nodes
 13599          var pos = _private.position; // make sure the nodes have a defined position
 13600  
 13601          if (pos.x == null) {
 13602            pos.x = 0;
 13603          }
 13604  
 13605          if (pos.y == null) {
 13606            pos.y = 0;
 13607          }
 13608        }
 13609  
 13610        if (_ele.isEdge()) {
 13611          // extra checks for edges
 13612          var edge = _ele;
 13613          var fields = ['source', 'target'];
 13614          var fieldsLength = fields.length;
 13615          var badSourceOrTarget = false;
 13616  
 13617          for (var j = 0; j < fieldsLength; j++) {
 13618            var field = fields[j];
 13619            var val = _data3[field];
 13620  
 13621            if (number(val)) {
 13622              val = _data3[field] = '' + _data3[field]; // now string
 13623            }
 13624  
 13625            if (val == null || val === '') {
 13626              // can't create if source or target is not defined properly
 13627              error('Can not create edge `' + id + '` with unspecified ' + field);
 13628              badSourceOrTarget = true;
 13629            } else if (!cy.hasElementWithId(val)) {
 13630              // can't create edge if one of its nodes doesn't exist
 13631              error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
 13632              badSourceOrTarget = true;
 13633            }
 13634          }
 13635  
 13636          if (badSourceOrTarget) {
 13637            removeFromElements();
 13638            continue;
 13639          } // can't create this
 13640  
 13641  
 13642          var src = cy.getElementById(_data3.source);
 13643          var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
 13644  
 13645          if (src.same(tgt)) {
 13646            src._private.edges.push(edge);
 13647          } else {
 13648            src._private.edges.push(edge);
 13649  
 13650            tgt._private.edges.push(edge);
 13651          }
 13652  
 13653          edge._private.source = src;
 13654          edge._private.target = tgt;
 13655        } // if is edge
 13656        // create mock ids / indexes maps for element so it can be used like collections
 13657  
 13658  
 13659        _private.map = new Map$1();
 13660  
 13661        _private.map.set(id, {
 13662          ele: _ele,
 13663          index: 0
 13664        });
 13665  
 13666        _private.removed = false;
 13667  
 13668        if (addToPool) {
 13669          cy.addToPool(_ele);
 13670        }
 13671      } // for each element
 13672      // do compound node sanity checks
 13673  
 13674  
 13675      for (var _i3 = 0; _i3 < nodes.length; _i3++) {
 13676        // each node
 13677        var node = nodes[_i3];
 13678        var _data4 = node._private.data;
 13679  
 13680        if (number(_data4.parent)) {
 13681          // then automake string
 13682          _data4.parent = '' + _data4.parent;
 13683        }
 13684  
 13685        var parentId = _data4.parent;
 13686        var specifiedParent = parentId != null;
 13687  
 13688        if (specifiedParent) {
 13689          var parent = cy.getElementById(parentId);
 13690  
 13691          if (parent.empty()) {
 13692            // non-existant parent; just remove it
 13693            _data4.parent = undefined;
 13694          } else {
 13695            var selfAsParent = false;
 13696            var ancestor = parent;
 13697  
 13698            while (!ancestor.empty()) {
 13699              if (node.same(ancestor)) {
 13700                // mark self as parent and remove from data
 13701                selfAsParent = true;
 13702                _data4.parent = undefined; // remove parent reference
 13703                // exit or we loop forever
 13704  
 13705                break;
 13706              }
 13707  
 13708              ancestor = ancestor.parent();
 13709            }
 13710  
 13711            if (!selfAsParent) {
 13712              // connect with children
 13713              parent[0]._private.children.push(node);
 13714  
 13715              node._private.parent = parent[0]; // let the core know we have a compound graph
 13716  
 13717              cy_p.hasCompoundNodes = true;
 13718            }
 13719          } // else
 13720  
 13721        } // if specified parent
 13722  
 13723      } // for each node
 13724  
 13725  
 13726      if (elements.length > 0) {
 13727        var restored = new Collection(cy, elements);
 13728  
 13729        for (var _i4 = 0; _i4 < restored.length; _i4++) {
 13730          var _ele2 = restored[_i4];
 13731  
 13732          if (_ele2.isNode()) {
 13733            continue;
 13734          } // adding an edge invalidates the traversal caches for the parallel edges
 13735  
 13736  
 13737          _ele2.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
 13738  
 13739  
 13740          _ele2.source().clearTraversalCache();
 13741  
 13742          _ele2.target().clearTraversalCache();
 13743        }
 13744  
 13745        var toUpdateStyle;
 13746  
 13747        if (cy_p.hasCompoundNodes) {
 13748          toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
 13749        } else {
 13750          toUpdateStyle = restored;
 13751        }
 13752  
 13753        toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
 13754  
 13755        if (notifyRenderer) {
 13756          restored.emitAndNotify('add');
 13757        } else if (addToPool) {
 13758          restored.emit('add');
 13759        }
 13760      }
 13761  
 13762      return self; // chainability
 13763    };
 13764  
 13765    elesfn$u.removed = function () {
 13766      var ele = this[0];
 13767      return ele && ele._private.removed;
 13768    };
 13769  
 13770    elesfn$u.inside = function () {
 13771      var ele = this[0];
 13772      return ele && !ele._private.removed;
 13773    };
 13774  
 13775    elesfn$u.remove = function () {
 13776      var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
 13777      var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
 13778      var self = this;
 13779      var elesToRemove = [];
 13780      var elesToRemoveIds = {};
 13781      var cy = self._private.cy; // add connected edges
 13782  
 13783      function addConnectedEdges(node) {
 13784        var edges = node._private.edges;
 13785  
 13786        for (var i = 0; i < edges.length; i++) {
 13787          add(edges[i]);
 13788        }
 13789      } // add descendant nodes
 13790  
 13791  
 13792      function addChildren(node) {
 13793        var children = node._private.children;
 13794  
 13795        for (var i = 0; i < children.length; i++) {
 13796          add(children[i]);
 13797        }
 13798      }
 13799  
 13800      function add(ele) {
 13801        var alreadyAdded = elesToRemoveIds[ele.id()];
 13802  
 13803        if (removeFromPool && ele.removed() || alreadyAdded) {
 13804          return;
 13805        } else {
 13806          elesToRemoveIds[ele.id()] = true;
 13807        }
 13808  
 13809        if (ele.isNode()) {
 13810          elesToRemove.push(ele); // nodes are removed last
 13811  
 13812          addConnectedEdges(ele);
 13813          addChildren(ele);
 13814        } else {
 13815          elesToRemove.unshift(ele); // edges are removed first
 13816        }
 13817      } // make the list of elements to remove
 13818      // (may be removing more than specified due to connected edges etc)
 13819  
 13820  
 13821      for (var i = 0, l = self.length; i < l; i++) {
 13822        var ele = self[i];
 13823        add(ele);
 13824      }
 13825  
 13826      function removeEdgeRef(node, edge) {
 13827        var connectedEdges = node._private.edges;
 13828        removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
 13829  
 13830        node.clearTraversalCache();
 13831      }
 13832  
 13833      function removeParallelRef(pllEdge) {
 13834        // removing an edge invalidates the traversal caches for the parallel edges
 13835        pllEdge.clearTraversalCache();
 13836      }
 13837  
 13838      var alteredParents = [];
 13839      alteredParents.ids = {};
 13840  
 13841      function removeChildRef(parent, ele) {
 13842        ele = ele[0];
 13843        parent = parent[0];
 13844        var children = parent._private.children;
 13845        var pid = parent.id();
 13846        removeFromArray(children, ele); // remove parent => child ref
 13847  
 13848        ele._private.parent = null; // remove child => parent ref
 13849  
 13850        if (!alteredParents.ids[pid]) {
 13851          alteredParents.ids[pid] = true;
 13852          alteredParents.push(parent);
 13853        }
 13854      }
 13855  
 13856      self.dirtyCompoundBoundsCache();
 13857  
 13858      if (removeFromPool) {
 13859        cy.removeFromPool(elesToRemove); // remove from core pool
 13860      }
 13861  
 13862      for (var _i5 = 0; _i5 < elesToRemove.length; _i5++) {
 13863        var _ele3 = elesToRemove[_i5];
 13864  
 13865        if (_ele3.isEdge()) {
 13866          // remove references to this edge in its connected nodes
 13867          var src = _ele3.source()[0];
 13868  
 13869          var tgt = _ele3.target()[0];
 13870  
 13871          removeEdgeRef(src, _ele3);
 13872          removeEdgeRef(tgt, _ele3);
 13873  
 13874          var pllEdges = _ele3.parallelEdges();
 13875  
 13876          for (var j = 0; j < pllEdges.length; j++) {
 13877            var pllEdge = pllEdges[j];
 13878            removeParallelRef(pllEdge);
 13879  
 13880            if (pllEdge.isBundledBezier()) {
 13881              pllEdge.dirtyBoundingBoxCache();
 13882            }
 13883          }
 13884        } else {
 13885          // remove reference to parent
 13886          var parent = _ele3.parent();
 13887  
 13888          if (parent.length !== 0) {
 13889            removeChildRef(parent, _ele3);
 13890          }
 13891        }
 13892  
 13893        if (removeFromPool) {
 13894          // mark as removed
 13895          _ele3._private.removed = true;
 13896        }
 13897      } // check to see if we have a compound graph or not
 13898  
 13899  
 13900      var elesStillInside = cy._private.elements;
 13901      cy._private.hasCompoundNodes = false;
 13902  
 13903      for (var _i6 = 0; _i6 < elesStillInside.length; _i6++) {
 13904        var _ele4 = elesStillInside[_i6];
 13905  
 13906        if (_ele4.isParent()) {
 13907          cy._private.hasCompoundNodes = true;
 13908          break;
 13909        }
 13910      }
 13911  
 13912      var removedElements = new Collection(this.cy(), elesToRemove);
 13913  
 13914      if (removedElements.size() > 0) {
 13915        // must manually notify since trigger won't do this automatically once removed
 13916        if (notifyRenderer) {
 13917          removedElements.emitAndNotify('remove');
 13918        } else if (removeFromPool) {
 13919          removedElements.emit('remove');
 13920        }
 13921      } // the parents who were modified by the removal need their style updated
 13922  
 13923  
 13924      for (var _i7 = 0; _i7 < alteredParents.length; _i7++) {
 13925        var _ele5 = alteredParents[_i7];
 13926  
 13927        if (!removeFromPool || !_ele5.removed()) {
 13928          _ele5.updateStyle();
 13929        }
 13930      }
 13931  
 13932      return removedElements;
 13933    };
 13934  
 13935    elesfn$u.move = function (struct) {
 13936      var cy = this._private.cy;
 13937      var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
 13938      // (our calls to remove/restore do not remove from the graph or make events)
 13939  
 13940      var notifyRenderer = false;
 13941      var modifyPool = false;
 13942  
 13943      var toString = function toString(id) {
 13944        return id == null ? id : '' + id;
 13945      }; // id must be string
 13946  
 13947  
 13948      if (struct.source !== undefined || struct.target !== undefined) {
 13949        var srcId = toString(struct.source);
 13950        var tgtId = toString(struct.target);
 13951        var srcExists = srcId != null && cy.hasElementWithId(srcId);
 13952        var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
 13953  
 13954        if (srcExists || tgtExists) {
 13955          cy.batch(function () {
 13956            // avoid duplicate style updates
 13957            eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
 13958  
 13959            eles.emitAndNotify('moveout');
 13960  
 13961            for (var i = 0; i < eles.length; i++) {
 13962              var ele = eles[i];
 13963              var _data5 = ele._private.data;
 13964  
 13965              if (ele.isEdge()) {
 13966                if (srcExists) {
 13967                  _data5.source = srcId;
 13968                }
 13969  
 13970                if (tgtExists) {
 13971                  _data5.target = tgtId;
 13972                }
 13973              }
 13974            }
 13975  
 13976            eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
 13977          });
 13978          eles.emitAndNotify('move');
 13979        }
 13980      } else if (struct.parent !== undefined) {
 13981        // move node to new parent
 13982        var parentId = toString(struct.parent);
 13983        var parentExists = parentId === null || cy.hasElementWithId(parentId);
 13984  
 13985        if (parentExists) {
 13986          var pidToAssign = parentId === null ? undefined : parentId;
 13987          cy.batch(function () {
 13988            // avoid duplicate style updates
 13989            var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
 13990  
 13991            updated.emitAndNotify('moveout');
 13992  
 13993            for (var i = 0; i < eles.length; i++) {
 13994              var ele = eles[i];
 13995              var _data6 = ele._private.data;
 13996  
 13997              if (ele.isNode()) {
 13998                _data6.parent = pidToAssign;
 13999              }
 14000            }
 14001  
 14002            updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
 14003          });
 14004          eles.emitAndNotify('move');
 14005        }
 14006      }
 14007  
 14008      return this;
 14009    };
 14010  
 14011    [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) {
 14012      extend(elesfn$u, props);
 14013    });
 14014  
 14015    var corefn = {
 14016      add: function add(opts) {
 14017        var elements;
 14018        var cy = this; // add the elements
 14019  
 14020        if (elementOrCollection(opts)) {
 14021          var eles = opts;
 14022  
 14023          if (eles._private.cy === cy) {
 14024            // same instance => just restore
 14025            elements = eles.restore();
 14026          } else {
 14027            // otherwise, copy from json
 14028            var jsons = [];
 14029  
 14030            for (var i = 0; i < eles.length; i++) {
 14031              var ele = eles[i];
 14032              jsons.push(ele.json());
 14033            }
 14034  
 14035            elements = new Collection(cy, jsons);
 14036          }
 14037        } // specify an array of options
 14038        else if (array(opts)) {
 14039            var _jsons = opts;
 14040            elements = new Collection(cy, _jsons);
 14041          } // specify via opts.nodes and opts.edges
 14042          else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
 14043              var elesByGroup = opts;
 14044              var _jsons2 = [];
 14045              var grs = ['nodes', 'edges'];
 14046  
 14047              for (var _i = 0, il = grs.length; _i < il; _i++) {
 14048                var group = grs[_i];
 14049                var elesArray = elesByGroup[group];
 14050  
 14051                if (array(elesArray)) {
 14052                  for (var j = 0, jl = elesArray.length; j < jl; j++) {
 14053                    var json = extend({
 14054                      group: group
 14055                    }, elesArray[j]);
 14056  
 14057                    _jsons2.push(json);
 14058                  }
 14059                }
 14060              }
 14061  
 14062              elements = new Collection(cy, _jsons2);
 14063            } // specify options for one element
 14064            else {
 14065                var _json = opts;
 14066                elements = new Element(cy, _json).collection();
 14067              }
 14068  
 14069        return elements;
 14070      },
 14071      remove: function remove(collection) {
 14072        if (elementOrCollection(collection)) ; else if (string(collection)) {
 14073          var selector = collection;
 14074          collection = this.$(selector);
 14075        }
 14076  
 14077        return collection.remove();
 14078      }
 14079    };
 14080  
 14081    /* global Float32Array */
 14082  
 14083    /*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
 14084    function generateCubicBezier(mX1, mY1, mX2, mY2) {
 14085      var NEWTON_ITERATIONS = 4,
 14086          NEWTON_MIN_SLOPE = 0.001,
 14087          SUBDIVISION_PRECISION = 0.0000001,
 14088          SUBDIVISION_MAX_ITERATIONS = 10,
 14089          kSplineTableSize = 11,
 14090          kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
 14091          float32ArraySupported = typeof Float32Array !== 'undefined';
 14092      /* Must contain four arguments. */
 14093  
 14094      if (arguments.length !== 4) {
 14095        return false;
 14096      }
 14097      /* Arguments must be numbers. */
 14098  
 14099  
 14100      for (var i = 0; i < 4; ++i) {
 14101        if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
 14102          return false;
 14103        }
 14104      }
 14105      /* X values must be in the [0, 1] range. */
 14106  
 14107  
 14108      mX1 = Math.min(mX1, 1);
 14109      mX2 = Math.min(mX2, 1);
 14110      mX1 = Math.max(mX1, 0);
 14111      mX2 = Math.max(mX2, 0);
 14112      var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
 14113  
 14114      function A(aA1, aA2) {
 14115        return 1.0 - 3.0 * aA2 + 3.0 * aA1;
 14116      }
 14117  
 14118      function B(aA1, aA2) {
 14119        return 3.0 * aA2 - 6.0 * aA1;
 14120      }
 14121  
 14122      function C(aA1) {
 14123        return 3.0 * aA1;
 14124      }
 14125  
 14126      function calcBezier(aT, aA1, aA2) {
 14127        return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
 14128      }
 14129  
 14130      function getSlope(aT, aA1, aA2) {
 14131        return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
 14132      }
 14133  
 14134      function newtonRaphsonIterate(aX, aGuessT) {
 14135        for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
 14136          var currentSlope = getSlope(aGuessT, mX1, mX2);
 14137  
 14138          if (currentSlope === 0.0) {
 14139            return aGuessT;
 14140          }
 14141  
 14142          var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
 14143          aGuessT -= currentX / currentSlope;
 14144        }
 14145  
 14146        return aGuessT;
 14147      }
 14148  
 14149      function calcSampleValues() {
 14150        for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
 14151          mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
 14152        }
 14153      }
 14154  
 14155      function binarySubdivide(aX, aA, aB) {
 14156        var currentX,
 14157            currentT,
 14158            i = 0;
 14159  
 14160        do {
 14161          currentT = aA + (aB - aA) / 2.0;
 14162          currentX = calcBezier(currentT, mX1, mX2) - aX;
 14163  
 14164          if (currentX > 0.0) {
 14165            aB = currentT;
 14166          } else {
 14167            aA = currentT;
 14168          }
 14169        } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
 14170  
 14171        return currentT;
 14172      }
 14173  
 14174      function getTForX(aX) {
 14175        var intervalStart = 0.0,
 14176            currentSample = 1,
 14177            lastSample = kSplineTableSize - 1;
 14178  
 14179        for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
 14180          intervalStart += kSampleStepSize;
 14181        }
 14182  
 14183        --currentSample;
 14184        var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
 14185            guessForT = intervalStart + dist * kSampleStepSize,
 14186            initialSlope = getSlope(guessForT, mX1, mX2);
 14187  
 14188        if (initialSlope >= NEWTON_MIN_SLOPE) {
 14189          return newtonRaphsonIterate(aX, guessForT);
 14190        } else if (initialSlope === 0.0) {
 14191          return guessForT;
 14192        } else {
 14193          return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
 14194        }
 14195      }
 14196  
 14197      var _precomputed = false;
 14198  
 14199      function precompute() {
 14200        _precomputed = true;
 14201  
 14202        if (mX1 !== mY1 || mX2 !== mY2) {
 14203          calcSampleValues();
 14204        }
 14205      }
 14206  
 14207      var f = function f(aX) {
 14208        if (!_precomputed) {
 14209          precompute();
 14210        }
 14211  
 14212        if (mX1 === mY1 && mX2 === mY2) {
 14213          return aX;
 14214        }
 14215  
 14216        if (aX === 0) {
 14217          return 0;
 14218        }
 14219  
 14220        if (aX === 1) {
 14221          return 1;
 14222        }
 14223  
 14224        return calcBezier(getTForX(aX), mY1, mY2);
 14225      };
 14226  
 14227      f.getControlPoints = function () {
 14228        return [{
 14229          x: mX1,
 14230          y: mY1
 14231        }, {
 14232          x: mX2,
 14233          y: mY2
 14234        }];
 14235      };
 14236  
 14237      var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
 14238  
 14239      f.toString = function () {
 14240        return str;
 14241      };
 14242  
 14243      return f;
 14244    }
 14245  
 14246    /*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
 14247  
 14248    /* 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
 14249       then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
 14250    var generateSpringRK4 = function () {
 14251      function springAccelerationForState(state) {
 14252        return -state.tension * state.x - state.friction * state.v;
 14253      }
 14254  
 14255      function springEvaluateStateWithDerivative(initialState, dt, derivative) {
 14256        var state = {
 14257          x: initialState.x + derivative.dx * dt,
 14258          v: initialState.v + derivative.dv * dt,
 14259          tension: initialState.tension,
 14260          friction: initialState.friction
 14261        };
 14262        return {
 14263          dx: state.v,
 14264          dv: springAccelerationForState(state)
 14265        };
 14266      }
 14267  
 14268      function springIntegrateState(state, dt) {
 14269        var a = {
 14270          dx: state.v,
 14271          dv: springAccelerationForState(state)
 14272        },
 14273            b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
 14274            c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
 14275            d = springEvaluateStateWithDerivative(state, dt, c),
 14276            dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
 14277            dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
 14278        state.x = state.x + dxdt * dt;
 14279        state.v = state.v + dvdt * dt;
 14280        return state;
 14281      }
 14282  
 14283      return function springRK4Factory(tension, friction, duration) {
 14284        var initState = {
 14285          x: -1,
 14286          v: 0,
 14287          tension: null,
 14288          friction: null
 14289        },
 14290            path = [0],
 14291            time_lapsed = 0,
 14292            tolerance = 1 / 10000,
 14293            DT = 16 / 1000,
 14294            have_duration,
 14295            dt,
 14296            last_state;
 14297        tension = parseFloat(tension) || 500;
 14298        friction = parseFloat(friction) || 20;
 14299        duration = duration || null;
 14300        initState.tension = tension;
 14301        initState.friction = friction;
 14302        have_duration = duration !== null;
 14303        /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
 14304  
 14305        if (have_duration) {
 14306          /* Run the simulation without a duration. */
 14307          time_lapsed = springRK4Factory(tension, friction);
 14308          /* Compute the adjusted time delta. */
 14309  
 14310          dt = time_lapsed / duration * DT;
 14311        } else {
 14312          dt = DT;
 14313        }
 14314  
 14315        for (;;) {
 14316          /* Next/step function .*/
 14317          last_state = springIntegrateState(last_state || initState, dt);
 14318          /* Store the position. */
 14319  
 14320          path.push(1 + last_state.x);
 14321          time_lapsed += 16;
 14322          /* If the change threshold is reached, break. */
 14323  
 14324          if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
 14325            break;
 14326          }
 14327        }
 14328        /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
 14329           computed path and returns a snapshot of the position according to a given percentComplete. */
 14330  
 14331  
 14332        return !have_duration ? time_lapsed : function (percentComplete) {
 14333          return path[percentComplete * (path.length - 1) | 0];
 14334        };
 14335      };
 14336    }();
 14337  
 14338    var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
 14339      var bezier = generateCubicBezier(t1, p1, t2, p2);
 14340      return function (start, end, percent) {
 14341        return start + (end - start) * bezier(percent);
 14342      };
 14343    };
 14344  
 14345    var easings = {
 14346      'linear': function linear(start, end, percent) {
 14347        return start + (end - start) * percent;
 14348      },
 14349      // default easings
 14350      'ease': cubicBezier(0.25, 0.1, 0.25, 1),
 14351      'ease-in': cubicBezier(0.42, 0, 1, 1),
 14352      'ease-out': cubicBezier(0, 0, 0.58, 1),
 14353      'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
 14354      // sine
 14355      'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
 14356      'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
 14357      'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
 14358      // quad
 14359      'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
 14360      'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
 14361      'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
 14362      // cubic
 14363      'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
 14364      'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
 14365      'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
 14366      // quart
 14367      'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
 14368      'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
 14369      'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
 14370      // quint
 14371      'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
 14372      'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
 14373      'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
 14374      // expo
 14375      'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
 14376      'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
 14377      'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
 14378      // circ
 14379      'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
 14380      'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
 14381      'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
 14382      // user param easings...
 14383      'spring': function spring(tension, friction, duration) {
 14384        if (duration === 0) {
 14385          // can't get a spring w/ duration 0
 14386          return easings.linear; // duration 0 => jump to end so impl doesn't matter
 14387        }
 14388  
 14389        var spring = generateSpringRK4(tension, friction, duration);
 14390        return function (start, end, percent) {
 14391          return start + (end - start) * spring(percent);
 14392        };
 14393      },
 14394      'cubic-bezier': cubicBezier
 14395    };
 14396  
 14397    function getEasedValue(type, start, end, percent, easingFn) {
 14398      if (percent === 1) {
 14399        return end;
 14400      }
 14401  
 14402      if (start === end) {
 14403        return end;
 14404      }
 14405  
 14406      var val = easingFn(start, end, percent);
 14407  
 14408      if (type == null) {
 14409        return val;
 14410      }
 14411  
 14412      if (type.roundValue || type.color) {
 14413        val = Math.round(val);
 14414      }
 14415  
 14416      if (type.min !== undefined) {
 14417        val = Math.max(val, type.min);
 14418      }
 14419  
 14420      if (type.max !== undefined) {
 14421        val = Math.min(val, type.max);
 14422      }
 14423  
 14424      return val;
 14425    }
 14426  
 14427    function getValue(prop, spec) {
 14428      if (prop.pfValue != null || prop.value != null) {
 14429        if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
 14430          return prop.pfValue;
 14431        } else {
 14432          return prop.value;
 14433        }
 14434      } else {
 14435        return prop;
 14436      }
 14437    }
 14438  
 14439    function ease(startProp, endProp, percent, easingFn, propSpec) {
 14440      var type = propSpec != null ? propSpec.type : null;
 14441  
 14442      if (percent < 0) {
 14443        percent = 0;
 14444      } else if (percent > 1) {
 14445        percent = 1;
 14446      }
 14447  
 14448      var start = getValue(startProp, propSpec);
 14449      var end = getValue(endProp, propSpec);
 14450  
 14451      if (number(start) && number(end)) {
 14452        return getEasedValue(type, start, end, percent, easingFn);
 14453      } else if (array(start) && array(end)) {
 14454        var easedArr = [];
 14455  
 14456        for (var i = 0; i < end.length; i++) {
 14457          var si = start[i];
 14458          var ei = end[i];
 14459  
 14460          if (si != null && ei != null) {
 14461            var val = getEasedValue(type, si, ei, percent, easingFn);
 14462            easedArr.push(val);
 14463          } else {
 14464            easedArr.push(ei);
 14465          }
 14466        }
 14467  
 14468        return easedArr;
 14469      }
 14470  
 14471      return undefined;
 14472    }
 14473  
 14474    function step(self, ani, now, isCore) {
 14475      var isEles = !isCore;
 14476      var _p = self._private;
 14477      var ani_p = ani._private;
 14478      var pEasing = ani_p.easing;
 14479      var startTime = ani_p.startTime;
 14480      var cy = isCore ? self : self.cy();
 14481      var style = cy.style();
 14482  
 14483      if (!ani_p.easingImpl) {
 14484        if (pEasing == null) {
 14485          // use default
 14486          ani_p.easingImpl = easings['linear'];
 14487        } else {
 14488          // then define w/ name
 14489          var easingVals;
 14490  
 14491          if (string(pEasing)) {
 14492            var easingProp = style.parse('transition-timing-function', pEasing);
 14493            easingVals = easingProp.value;
 14494          } else {
 14495            // then assume preparsed array
 14496            easingVals = pEasing;
 14497          }
 14498  
 14499          var name, args;
 14500  
 14501          if (string(easingVals)) {
 14502            name = easingVals;
 14503            args = [];
 14504          } else {
 14505            name = easingVals[1];
 14506            args = easingVals.slice(2).map(function (n) {
 14507              return +n;
 14508            });
 14509          }
 14510  
 14511          if (args.length > 0) {
 14512            // create with args
 14513            if (name === 'spring') {
 14514              args.push(ani_p.duration); // need duration to generate spring
 14515            }
 14516  
 14517            ani_p.easingImpl = easings[name].apply(null, args);
 14518          } else {
 14519            // static impl by name
 14520            ani_p.easingImpl = easings[name];
 14521          }
 14522        }
 14523      }
 14524  
 14525      var easing = ani_p.easingImpl;
 14526      var percent;
 14527  
 14528      if (ani_p.duration === 0) {
 14529        percent = 1;
 14530      } else {
 14531        percent = (now - startTime) / ani_p.duration;
 14532      }
 14533  
 14534      if (ani_p.applying) {
 14535        percent = ani_p.progress;
 14536      }
 14537  
 14538      if (percent < 0) {
 14539        percent = 0;
 14540      } else if (percent > 1) {
 14541        percent = 1;
 14542      }
 14543  
 14544      if (ani_p.delay == null) {
 14545        // then update
 14546        var startPos = ani_p.startPosition;
 14547        var endPos = ani_p.position;
 14548  
 14549        if (endPos && isEles && !self.locked()) {
 14550          var newPos = {};
 14551  
 14552          if (valid(startPos.x, endPos.x)) {
 14553            newPos.x = ease(startPos.x, endPos.x, percent, easing);
 14554          }
 14555  
 14556          if (valid(startPos.y, endPos.y)) {
 14557            newPos.y = ease(startPos.y, endPos.y, percent, easing);
 14558          }
 14559  
 14560          self.position(newPos);
 14561        }
 14562  
 14563        var startPan = ani_p.startPan;
 14564        var endPan = ani_p.pan;
 14565        var pan = _p.pan;
 14566        var animatingPan = endPan != null && isCore;
 14567  
 14568        if (animatingPan) {
 14569          if (valid(startPan.x, endPan.x)) {
 14570            pan.x = ease(startPan.x, endPan.x, percent, easing);
 14571          }
 14572  
 14573          if (valid(startPan.y, endPan.y)) {
 14574            pan.y = ease(startPan.y, endPan.y, percent, easing);
 14575          }
 14576  
 14577          self.emit('pan');
 14578        }
 14579  
 14580        var startZoom = ani_p.startZoom;
 14581        var endZoom = ani_p.zoom;
 14582        var animatingZoom = endZoom != null && isCore;
 14583  
 14584        if (animatingZoom) {
 14585          if (valid(startZoom, endZoom)) {
 14586            _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
 14587          }
 14588  
 14589          self.emit('zoom');
 14590        }
 14591  
 14592        if (animatingPan || animatingZoom) {
 14593          self.emit('viewport');
 14594        }
 14595  
 14596        var props = ani_p.style;
 14597  
 14598        if (props && props.length > 0 && isEles) {
 14599          for (var i = 0; i < props.length; i++) {
 14600            var prop = props[i];
 14601            var _name = prop.name;
 14602            var end = prop;
 14603            var start = ani_p.startStyle[_name];
 14604            var propSpec = style.properties[start.name];
 14605            var easedVal = ease(start, end, percent, easing, propSpec);
 14606            style.overrideBypass(self, _name, easedVal);
 14607          } // for props
 14608  
 14609  
 14610          self.emit('style');
 14611        } // if
 14612  
 14613      }
 14614  
 14615      ani_p.progress = percent;
 14616      return percent;
 14617    }
 14618  
 14619    function valid(start, end) {
 14620      if (start == null || end == null) {
 14621        return false;
 14622      }
 14623  
 14624      if (number(start) && number(end)) {
 14625        return true;
 14626      } else if (start && end) {
 14627        return true;
 14628      }
 14629  
 14630      return false;
 14631    }
 14632  
 14633    function startAnimation(self, ani, now, isCore) {
 14634      var ani_p = ani._private;
 14635      ani_p.started = true;
 14636      ani_p.startTime = now - ani_p.progress * ani_p.duration;
 14637    }
 14638  
 14639    function stepAll(now, cy) {
 14640      var eles = cy._private.aniEles;
 14641      var doneEles = [];
 14642  
 14643      function stepOne(ele, isCore) {
 14644        var _p = ele._private;
 14645        var current = _p.animation.current;
 14646        var queue = _p.animation.queue;
 14647        var ranAnis = false; // cancel all animations on display:none ele
 14648  
 14649        if (!isCore && ele.pstyle('display').value === 'none') {
 14650          // put all current and queue animations in this tick's current list
 14651          // and empty the lists for the element
 14652          current = current.splice(0, current.length).concat(queue.splice(0, queue.length)); // stop all animations
 14653  
 14654          for (var i = 0; i < current.length; i++) {
 14655            current[i].stop();
 14656          }
 14657        } // if nothing currently animating, get something from the queue
 14658  
 14659  
 14660        if (current.length === 0) {
 14661          var next = queue.shift();
 14662  
 14663          if (next) {
 14664            current.push(next);
 14665          }
 14666        }
 14667  
 14668        var callbacks = function callbacks(_callbacks) {
 14669          for (var j = _callbacks.length - 1; j >= 0; j--) {
 14670            var cb = _callbacks[j];
 14671            cb();
 14672          }
 14673  
 14674          _callbacks.splice(0, _callbacks.length);
 14675        }; // step and remove if done
 14676  
 14677  
 14678        for (var _i = current.length - 1; _i >= 0; _i--) {
 14679          var ani = current[_i];
 14680          var ani_p = ani._private;
 14681  
 14682          if (ani_p.stopped) {
 14683            current.splice(_i, 1);
 14684            ani_p.hooked = false;
 14685            ani_p.playing = false;
 14686            ani_p.started = false;
 14687            callbacks(ani_p.frames);
 14688            continue;
 14689          }
 14690  
 14691          if (!ani_p.playing && !ani_p.applying) {
 14692            continue;
 14693          } // an apply() while playing shouldn't do anything
 14694  
 14695  
 14696          if (ani_p.playing && ani_p.applying) {
 14697            ani_p.applying = false;
 14698          }
 14699  
 14700          if (!ani_p.started) {
 14701            startAnimation(ele, ani, now);
 14702          }
 14703  
 14704          step(ele, ani, now, isCore);
 14705  
 14706          if (ani_p.applying) {
 14707            ani_p.applying = false;
 14708          }
 14709  
 14710          callbacks(ani_p.frames);
 14711  
 14712          if (ani_p.step != null) {
 14713            ani_p.step(now);
 14714          }
 14715  
 14716          if (ani.completed()) {
 14717            current.splice(_i, 1);
 14718            ani_p.hooked = false;
 14719            ani_p.playing = false;
 14720            ani_p.started = false;
 14721            callbacks(ani_p.completes);
 14722          }
 14723  
 14724          ranAnis = true;
 14725        }
 14726  
 14727        if (!isCore && current.length === 0 && queue.length === 0) {
 14728          doneEles.push(ele);
 14729        }
 14730  
 14731        return ranAnis;
 14732      } // stepElement
 14733      // handle all eles
 14734  
 14735  
 14736      var ranEleAni = false;
 14737  
 14738      for (var e = 0; e < eles.length; e++) {
 14739        var ele = eles[e];
 14740        var handledThisEle = stepOne(ele);
 14741        ranEleAni = ranEleAni || handledThisEle;
 14742      } // each element
 14743  
 14744  
 14745      var ranCoreAni = stepOne(cy, true); // notify renderer
 14746  
 14747      if (ranEleAni || ranCoreAni) {
 14748        if (eles.length > 0) {
 14749          cy.notify('draw', eles);
 14750        } else {
 14751          cy.notify('draw');
 14752        }
 14753      } // remove elements from list of currently animating if its queues are empty
 14754  
 14755  
 14756      eles.unmerge(doneEles);
 14757      cy.emit('step');
 14758    } // stepAll
 14759  
 14760    var corefn$1 = {
 14761      // pull in animation functions
 14762      animate: define$3.animate(),
 14763      animation: define$3.animation(),
 14764      animated: define$3.animated(),
 14765      clearQueue: define$3.clearQueue(),
 14766      delay: define$3.delay(),
 14767      delayAnimation: define$3.delayAnimation(),
 14768      stop: define$3.stop(),
 14769      addToAnimationPool: function addToAnimationPool(eles) {
 14770        var cy = this;
 14771  
 14772        if (!cy.styleEnabled()) {
 14773          return;
 14774        } // save cycles when no style used
 14775  
 14776  
 14777        cy._private.aniEles.merge(eles);
 14778      },
 14779      stopAnimationLoop: function stopAnimationLoop() {
 14780        this._private.animationsRunning = false;
 14781      },
 14782      startAnimationLoop: function startAnimationLoop() {
 14783        var cy = this;
 14784        cy._private.animationsRunning = true;
 14785  
 14786        if (!cy.styleEnabled()) {
 14787          return;
 14788        } // save cycles when no style used
 14789        // NB the animation loop will exec in headless environments if style enabled
 14790        // and explicit cy.destroy() is necessary to stop the loop
 14791  
 14792  
 14793        function headlessStep() {
 14794          if (!cy._private.animationsRunning) {
 14795            return;
 14796          }
 14797  
 14798          requestAnimationFrame(function animationStep(now) {
 14799            stepAll(now, cy);
 14800            headlessStep();
 14801          });
 14802        }
 14803  
 14804        var renderer = cy.renderer();
 14805  
 14806        if (renderer && renderer.beforeRender) {
 14807          // let the renderer schedule animations
 14808          renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
 14809            stepAll(now, cy);
 14810          }, renderer.beforeRenderPriorities.animations);
 14811        } else {
 14812          // manage the animation loop ourselves
 14813          headlessStep(); // first call
 14814        }
 14815      }
 14816    };
 14817  
 14818    var emitterOptions$1 = {
 14819      qualifierCompare: function qualifierCompare(selector1, selector2) {
 14820        if (selector1 == null || selector2 == null) {
 14821          return selector1 == null && selector2 == null;
 14822        } else {
 14823          return selector1.sameText(selector2);
 14824        }
 14825      },
 14826      eventMatches: function eventMatches(cy, listener, eventObj) {
 14827        var selector = listener.qualifier;
 14828  
 14829        if (selector != null) {
 14830          return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
 14831        }
 14832  
 14833        return true;
 14834      },
 14835      addEventFields: function addEventFields(cy, evt) {
 14836        evt.cy = cy;
 14837        evt.target = cy;
 14838      },
 14839      callbackContext: function callbackContext(cy, listener, eventObj) {
 14840        return listener.qualifier != null ? eventObj.target : cy;
 14841      }
 14842    };
 14843  
 14844    var argSelector$1 = function argSelector(arg) {
 14845      if (string(arg)) {
 14846        return new Selector(arg);
 14847      } else {
 14848        return arg;
 14849      }
 14850    };
 14851  
 14852    var elesfn$v = {
 14853      createEmitter: function createEmitter() {
 14854        var _p = this._private;
 14855  
 14856        if (!_p.emitter) {
 14857          _p.emitter = new Emitter(emitterOptions$1, this);
 14858        }
 14859  
 14860        return this;
 14861      },
 14862      emitter: function emitter() {
 14863        return this._private.emitter;
 14864      },
 14865      on: function on(events, selector, callback) {
 14866        this.emitter().on(events, argSelector$1(selector), callback);
 14867        return this;
 14868      },
 14869      removeListener: function removeListener(events, selector, callback) {
 14870        this.emitter().removeListener(events, argSelector$1(selector), callback);
 14871        return this;
 14872      },
 14873      removeAllListeners: function removeAllListeners() {
 14874        this.emitter().removeAllListeners();
 14875        return this;
 14876      },
 14877      one: function one(events, selector, callback) {
 14878        this.emitter().one(events, argSelector$1(selector), callback);
 14879        return this;
 14880      },
 14881      once: function once(events, selector, callback) {
 14882        this.emitter().one(events, argSelector$1(selector), callback);
 14883        return this;
 14884      },
 14885      emit: function emit(events, extraParams) {
 14886        this.emitter().emit(events, extraParams);
 14887        return this;
 14888      },
 14889      emitAndNotify: function emitAndNotify(event, eles) {
 14890        this.emit(event);
 14891        this.notify(event, eles);
 14892        return this;
 14893      }
 14894    };
 14895    define$3.eventAliasesOn(elesfn$v);
 14896  
 14897    var corefn$2 = {
 14898      png: function png(options) {
 14899        var renderer = this._private.renderer;
 14900        options = options || {};
 14901        return renderer.png(options);
 14902      },
 14903      jpg: function jpg(options) {
 14904        var renderer = this._private.renderer;
 14905        options = options || {};
 14906        options.bg = options.bg || '#fff';
 14907        return renderer.jpg(options);
 14908      }
 14909    };
 14910    corefn$2.jpeg = corefn$2.jpg;
 14911  
 14912    var corefn$3 = {
 14913      layout: function layout(options) {
 14914        var cy = this;
 14915  
 14916        if (options == null) {
 14917          error('Layout options must be specified to make a layout');
 14918          return;
 14919        }
 14920  
 14921        if (options.name == null) {
 14922          error('A `name` must be specified to make a layout');
 14923          return;
 14924        }
 14925  
 14926        var name = options.name;
 14927        var Layout = cy.extension('layout', name);
 14928  
 14929        if (Layout == null) {
 14930          error('No such layout `' + name + '` found.  Did you forget to import it and `cytoscape.use()` it?');
 14931          return;
 14932        }
 14933  
 14934        var eles;
 14935  
 14936        if (string(options.eles)) {
 14937          eles = cy.$(options.eles);
 14938        } else {
 14939          eles = options.eles != null ? options.eles : cy.$();
 14940        }
 14941  
 14942        var layout = new Layout(extend({}, options, {
 14943          cy: cy,
 14944          eles: eles
 14945        }));
 14946        return layout;
 14947      }
 14948    };
 14949    corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
 14950  
 14951    var corefn$4 = {
 14952      notify: function notify(eventName, eventEles) {
 14953        var _p = this._private;
 14954  
 14955        if (this.batching()) {
 14956          _p.batchNotifications = _p.batchNotifications || {};
 14957          var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
 14958  
 14959          if (eventEles != null) {
 14960            eles.merge(eventEles);
 14961          }
 14962  
 14963          return; // notifications are disabled during batching
 14964        }
 14965  
 14966        if (!_p.notificationsEnabled) {
 14967          return;
 14968        } // exit on disabled
 14969  
 14970  
 14971        var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
 14972  
 14973        if (this.destroyed() || !renderer) {
 14974          return;
 14975        }
 14976  
 14977        renderer.notify(eventName, eventEles);
 14978      },
 14979      notifications: function notifications(bool) {
 14980        var p = this._private;
 14981  
 14982        if (bool === undefined) {
 14983          return p.notificationsEnabled;
 14984        } else {
 14985          p.notificationsEnabled = bool ? true : false;
 14986        }
 14987  
 14988        return this;
 14989      },
 14990      noNotifications: function noNotifications(callback) {
 14991        this.notifications(false);
 14992        callback();
 14993        this.notifications(true);
 14994      },
 14995      batching: function batching() {
 14996        return this._private.batchCount > 0;
 14997      },
 14998      startBatch: function startBatch() {
 14999        var _p = this._private;
 15000  
 15001        if (_p.batchCount == null) {
 15002          _p.batchCount = 0;
 15003        }
 15004  
 15005        if (_p.batchCount === 0) {
 15006          _p.batchStyleEles = this.collection();
 15007          _p.batchNotifications = {};
 15008        }
 15009  
 15010        _p.batchCount++;
 15011        return this;
 15012      },
 15013      endBatch: function endBatch() {
 15014        var _p = this._private;
 15015  
 15016        if (_p.batchCount === 0) {
 15017          return this;
 15018        }
 15019  
 15020        _p.batchCount--;
 15021  
 15022        if (_p.batchCount === 0) {
 15023          // update style for dirty eles
 15024          _p.batchStyleEles.updateStyle();
 15025  
 15026          var renderer = this.renderer(); // notify the renderer of queued eles and event types
 15027  
 15028          Object.keys(_p.batchNotifications).forEach(function (eventName) {
 15029            var eles = _p.batchNotifications[eventName];
 15030  
 15031            if (eles.empty()) {
 15032              renderer.notify(eventName);
 15033            } else {
 15034              renderer.notify(eventName, eles);
 15035            }
 15036          });
 15037        }
 15038  
 15039        return this;
 15040      },
 15041      batch: function batch(callback) {
 15042        this.startBatch();
 15043        callback();
 15044        this.endBatch();
 15045        return this;
 15046      },
 15047      // for backwards compatibility
 15048      batchData: function batchData(map) {
 15049        var cy = this;
 15050        return this.batch(function () {
 15051          var ids = Object.keys(map);
 15052  
 15053          for (var i = 0; i < ids.length; i++) {
 15054            var id = ids[i];
 15055            var data = map[id];
 15056            var ele = cy.getElementById(id);
 15057            ele.data(data);
 15058          }
 15059        });
 15060      }
 15061    };
 15062  
 15063    var rendererDefaults = defaults({
 15064      hideEdgesOnViewport: false,
 15065      textureOnViewport: false,
 15066      motionBlur: false,
 15067      motionBlurOpacity: 0.05,
 15068      pixelRatio: undefined,
 15069      desktopTapThreshold: 4,
 15070      touchTapThreshold: 8,
 15071      wheelSensitivity: 1,
 15072      debug: false,
 15073      showFps: false
 15074    });
 15075    var corefn$5 = {
 15076      renderTo: function renderTo(context, zoom, pan, pxRatio) {
 15077        var r = this._private.renderer;
 15078        r.renderTo(context, zoom, pan, pxRatio);
 15079        return this;
 15080      },
 15081      renderer: function renderer() {
 15082        return this._private.renderer;
 15083      },
 15084      forceRender: function forceRender() {
 15085        this.notify('draw');
 15086        return this;
 15087      },
 15088      resize: function resize() {
 15089        this.invalidateSize();
 15090        this.emitAndNotify('resize');
 15091        return this;
 15092      },
 15093      initRenderer: function initRenderer(options) {
 15094        var cy = this;
 15095        var RendererProto = cy.extension('renderer', options.name);
 15096  
 15097        if (RendererProto == null) {
 15098          error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
 15099          return;
 15100        }
 15101  
 15102        if (options.wheelSensitivity !== undefined) {
 15103          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.");
 15104        }
 15105  
 15106        var rOpts = rendererDefaults(options);
 15107        rOpts.cy = cy;
 15108        cy._private.renderer = new RendererProto(rOpts);
 15109        this.notify('init');
 15110      },
 15111      destroyRenderer: function destroyRenderer() {
 15112        var cy = this;
 15113        cy.notify('destroy'); // destroy the renderer
 15114  
 15115        var domEle = cy.container();
 15116  
 15117        if (domEle) {
 15118          domEle._cyreg = null;
 15119  
 15120          while (domEle.childNodes.length > 0) {
 15121            domEle.removeChild(domEle.childNodes[0]);
 15122          }
 15123        }
 15124  
 15125        cy._private.renderer = null; // to be extra safe, remove the ref
 15126  
 15127        cy.mutableElements().forEach(function (ele) {
 15128          var _p = ele._private;
 15129          _p.rscratch = {};
 15130          _p.rstyle = {};
 15131          _p.animation.current = [];
 15132          _p.animation.queue = [];
 15133        });
 15134      },
 15135      onRender: function onRender(fn) {
 15136        return this.on('render', fn);
 15137      },
 15138      offRender: function offRender(fn) {
 15139        return this.off('render', fn);
 15140      }
 15141    };
 15142    corefn$5.invalidateDimensions = corefn$5.resize;
 15143  
 15144    var corefn$6 = {
 15145      // get a collection
 15146      // - empty collection on no args
 15147      // - collection of elements in the graph on selector arg
 15148      // - guarantee a returned collection when elements or collection specified
 15149      collection: function collection(eles, opts) {
 15150        if (string(eles)) {
 15151          return this.$(eles);
 15152        } else if (elementOrCollection(eles)) {
 15153          return eles.collection();
 15154        } else if (array(eles)) {
 15155          return new Collection(this, eles, opts);
 15156        }
 15157  
 15158        return new Collection(this);
 15159      },
 15160      nodes: function nodes(selector) {
 15161        var nodes = this.$(function (ele) {
 15162          return ele.isNode();
 15163        });
 15164  
 15165        if (selector) {
 15166          return nodes.filter(selector);
 15167        }
 15168  
 15169        return nodes;
 15170      },
 15171      edges: function edges(selector) {
 15172        var edges = this.$(function (ele) {
 15173          return ele.isEdge();
 15174        });
 15175  
 15176        if (selector) {
 15177          return edges.filter(selector);
 15178        }
 15179  
 15180        return edges;
 15181      },
 15182      // search the graph like jQuery
 15183      $: function $(selector) {
 15184        var eles = this._private.elements;
 15185  
 15186        if (selector) {
 15187          return eles.filter(selector);
 15188        } else {
 15189          return eles.spawnSelf();
 15190        }
 15191      },
 15192      mutableElements: function mutableElements() {
 15193        return this._private.elements;
 15194      }
 15195    }; // aliases
 15196  
 15197    corefn$6.elements = corefn$6.filter = corefn$6.$;
 15198  
 15199    var styfn = {}; // keys for style blocks, e.g. ttfftt
 15200  
 15201    var TRUE = 't';
 15202    var FALSE = 'f'; // (potentially expensive calculation)
 15203    // apply the style to the element based on
 15204    // - its bypass
 15205    // - what selectors match it
 15206  
 15207    styfn.apply = function (eles) {
 15208      var self = this;
 15209      var _p = self._private;
 15210      var cy = _p.cy;
 15211      var updatedEles = cy.collection();
 15212  
 15213      if (_p.newStyle) {
 15214        // clear style caches
 15215        _p.contextStyles = {};
 15216        _p.propDiffs = {};
 15217        self.cleanElements(eles, true);
 15218      }
 15219  
 15220      for (var ie = 0; ie < eles.length; ie++) {
 15221        var ele = eles[ie];
 15222        var cxtMeta = self.getContextMeta(ele);
 15223  
 15224        if (cxtMeta.empty) {
 15225          continue;
 15226        }
 15227  
 15228        var cxtStyle = self.getContextStyle(cxtMeta);
 15229        var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
 15230  
 15231        if (!_p.newStyle) {
 15232          self.updateTransitions(ele, app.diffProps);
 15233        }
 15234  
 15235        var hintsDiff = self.updateStyleHints(ele);
 15236  
 15237        if (hintsDiff) {
 15238          updatedEles.merge(ele);
 15239        }
 15240      } // for elements
 15241  
 15242  
 15243      _p.newStyle = false;
 15244      return updatedEles;
 15245    };
 15246  
 15247    styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
 15248      var self = this;
 15249      var cache = self._private.propDiffs = self._private.propDiffs || {};
 15250      var dualCxtKey = oldCxtKey + '-' + newCxtKey;
 15251      var cachedVal = cache[dualCxtKey];
 15252  
 15253      if (cachedVal) {
 15254        return cachedVal;
 15255      }
 15256  
 15257      var diffProps = [];
 15258      var addedProp = {};
 15259  
 15260      for (var i = 0; i < self.length; i++) {
 15261        var cxt = self[i];
 15262        var oldHasCxt = oldCxtKey[i] === TRUE;
 15263        var newHasCxt = newCxtKey[i] === TRUE;
 15264        var cxtHasDiffed = oldHasCxt !== newHasCxt;
 15265        var cxtHasMappedProps = cxt.mappedProperties.length > 0;
 15266  
 15267        if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
 15268          var props = void 0;
 15269  
 15270          if (cxtHasDiffed && cxtHasMappedProps) {
 15271            props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
 15272          } else if (cxtHasDiffed) {
 15273            props = cxt.properties; // need to check them all
 15274          } else if (cxtHasMappedProps) {
 15275            props = cxt.mappedProperties; // only need to check mapped
 15276          }
 15277  
 15278          for (var j = 0; j < props.length; j++) {
 15279            var prop = props[j];
 15280            var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
 15281            // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
 15282            // is cached)
 15283  
 15284            var laterCxtOverrides = false;
 15285  
 15286            for (var k = i + 1; k < self.length; k++) {
 15287              var laterCxt = self[k];
 15288              var hasLaterCxt = newCxtKey[k] === TRUE;
 15289  
 15290              if (!hasLaterCxt) {
 15291                continue;
 15292              } // can't override unless the context is active
 15293  
 15294  
 15295              laterCxtOverrides = laterCxt.properties[prop.name] != null;
 15296  
 15297              if (laterCxtOverrides) {
 15298                break;
 15299              } // exit early as long as one later context overrides
 15300  
 15301            }
 15302  
 15303            if (!addedProp[name] && !laterCxtOverrides) {
 15304              addedProp[name] = true;
 15305              diffProps.push(name);
 15306            }
 15307          } // for props
 15308  
 15309        } // if
 15310  
 15311      } // for contexts
 15312  
 15313  
 15314      cache[dualCxtKey] = diffProps;
 15315      return diffProps;
 15316    };
 15317  
 15318    styfn.getContextMeta = function (ele) {
 15319      var self = this;
 15320      var cxtKey = '';
 15321      var diffProps;
 15322      var prevKey = ele._private.styleCxtKey || '';
 15323  
 15324      if (self._private.newStyle) {
 15325        prevKey = ''; // since we need to apply all style if a fresh stylesheet
 15326      } // get the cxt key
 15327  
 15328  
 15329      for (var i = 0; i < self.length; i++) {
 15330        var context = self[i];
 15331        var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
 15332  
 15333        if (contextSelectorMatches) {
 15334          cxtKey += TRUE;
 15335        } else {
 15336          cxtKey += FALSE;
 15337        }
 15338      } // for context
 15339  
 15340  
 15341      diffProps = self.getPropertiesDiff(prevKey, cxtKey);
 15342      ele._private.styleCxtKey = cxtKey;
 15343      return {
 15344        key: cxtKey,
 15345        diffPropNames: diffProps,
 15346        empty: diffProps.length === 0
 15347      };
 15348    }; // gets a computed ele style object based on matched contexts
 15349  
 15350  
 15351    styfn.getContextStyle = function (cxtMeta) {
 15352      var cxtKey = cxtMeta.key;
 15353      var self = this;
 15354      var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
 15355  
 15356      if (cxtStyles[cxtKey]) {
 15357        return cxtStyles[cxtKey];
 15358      }
 15359  
 15360      var style = {
 15361        _private: {
 15362          key: cxtKey
 15363        }
 15364      };
 15365  
 15366      for (var i = 0; i < self.length; i++) {
 15367        var cxt = self[i];
 15368        var hasCxt = cxtKey[i] === TRUE;
 15369  
 15370        if (!hasCxt) {
 15371          continue;
 15372        }
 15373  
 15374        for (var j = 0; j < cxt.properties.length; j++) {
 15375          var prop = cxt.properties[j];
 15376          style[prop.name] = prop;
 15377        }
 15378      }
 15379  
 15380      cxtStyles[cxtKey] = style;
 15381      return style;
 15382    };
 15383  
 15384    styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
 15385      var self = this;
 15386      var diffProps = cxtMeta.diffPropNames;
 15387      var retDiffProps = {};
 15388      var types = self.types;
 15389  
 15390      for (var i = 0; i < diffProps.length; i++) {
 15391        var diffPropName = diffProps[i];
 15392        var cxtProp = cxtStyle[diffPropName];
 15393        var eleProp = ele.pstyle(diffPropName);
 15394  
 15395        if (!cxtProp) {
 15396          // no context prop means delete
 15397          if (!eleProp) {
 15398            continue; // no existing prop means nothing needs to be removed
 15399            // nb affects initial application on mapped values like control-point-distances
 15400          } else if (eleProp.bypass) {
 15401            cxtProp = {
 15402              name: diffPropName,
 15403              deleteBypassed: true
 15404            };
 15405          } else {
 15406            cxtProp = {
 15407              name: diffPropName,
 15408              "delete": true
 15409            };
 15410          }
 15411        } // save cycles when the context prop doesn't need to be applied
 15412  
 15413  
 15414        if (eleProp === cxtProp) {
 15415          continue;
 15416        } // save cycles when a mapped context prop doesn't need to be applied
 15417  
 15418  
 15419        if (cxtProp.mapped === types.fn // context prop is function mapper
 15420        && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
 15421        && eleProp.mapping != null // ele prop is a concrete value from from a mapper
 15422        && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
 15423        ) {
 15424            // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
 15425            var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
 15426  
 15427            var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
 15428  
 15429            if (fnValue === mapping.prevFnValue) {
 15430              continue;
 15431            }
 15432          }
 15433  
 15434        var retDiffProp = retDiffProps[diffPropName] = {
 15435          prev: eleProp
 15436        };
 15437        self.applyParsedProperty(ele, cxtProp);
 15438        retDiffProp.next = ele.pstyle(diffPropName);
 15439  
 15440        if (retDiffProp.next && retDiffProp.next.bypass) {
 15441          retDiffProp.next = retDiffProp.next.bypassed;
 15442        }
 15443      }
 15444  
 15445      return {
 15446        diffProps: retDiffProps
 15447      };
 15448    };
 15449  
 15450    styfn.updateStyleHints = function (ele) {
 15451      var _p = ele._private;
 15452      var self = this;
 15453      var propNames = self.propertyGroupNames;
 15454      var propGrKeys = self.propertyGroupKeys;
 15455  
 15456      var propHash = function propHash(ele, propNames, seedKey) {
 15457        return self.getPropertiesHash(ele, propNames, seedKey);
 15458      };
 15459  
 15460      var oldStyleKey = _p.styleKey;
 15461  
 15462      if (ele.removed()) {
 15463        return false;
 15464      }
 15465  
 15466      var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
 15467      // but lazily -- only use non-default prop values to reduce the number of hashes
 15468      //
 15469  
 15470      var overriddenStyles = ele._private.style;
 15471      propNames = Object.keys(overriddenStyles);
 15472  
 15473      for (var i = 0; i < propGrKeys.length; i++) {
 15474        var grKey = propGrKeys[i];
 15475        _p.styleKeys[grKey] = 0;
 15476      }
 15477  
 15478      var updateGrKey = function updateGrKey(val, grKey) {
 15479        return _p.styleKeys[grKey] = hashInt(val, _p.styleKeys[grKey]);
 15480      };
 15481  
 15482      var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
 15483        for (var j = 0; j < strVal.length; j++) {
 15484          updateGrKey(strVal.charCodeAt(j), grKey);
 15485        }
 15486      }; // - hashing works on 32 bit ints b/c we use bitwise ops
 15487      // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
 15488      // - raise up small numbers so more significant digits are seen by hashing
 15489      // - make small numbers larger than a normal value to avoid collisions
 15490      // - works in practice and it's relatively cheap
 15491  
 15492  
 15493      var N = 2000000000;
 15494  
 15495      var cleanNum = function cleanNum(val) {
 15496        return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
 15497      };
 15498  
 15499      for (var _i = 0; _i < propNames.length; _i++) {
 15500        var name = propNames[_i];
 15501        var parsedProp = overriddenStyles[name];
 15502  
 15503        if (parsedProp == null) {
 15504          continue;
 15505        }
 15506  
 15507        var propInfo = this.properties[name];
 15508        var type = propInfo.type;
 15509        var _grKey = propInfo.groupKey;
 15510        var normalizedNumberVal = void 0;
 15511  
 15512        if (propInfo.hashOverride != null) {
 15513          normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
 15514        } else if (parsedProp.pfValue != null) {
 15515          normalizedNumberVal = parsedProp.pfValue;
 15516        } // might not be a number if it allows enums
 15517  
 15518  
 15519        var numberVal = propInfo.enums == null ? parsedProp.value : null;
 15520        var haveNormNum = normalizedNumberVal != null;
 15521        var haveUnitedNum = numberVal != null;
 15522        var haveNum = haveNormNum || haveUnitedNum;
 15523        var units = parsedProp.units; // numbers are cheaper to hash than strings
 15524        // 1 hash op vs n hash ops (for length n string)
 15525  
 15526        if (type.number && haveNum) {
 15527          var v = haveNormNum ? normalizedNumberVal : numberVal;
 15528  
 15529          if (type.multiple) {
 15530            for (var _i2 = 0; _i2 < v.length; _i2++) {
 15531              updateGrKey(cleanNum(v[_i2]), _grKey);
 15532            }
 15533          } else {
 15534            updateGrKey(cleanNum(v), _grKey);
 15535          }
 15536  
 15537          if (!haveNormNum && units != null) {
 15538            updateGrKeyWStr(units, _grKey);
 15539          }
 15540        } else {
 15541          updateGrKeyWStr(parsedProp.strValue, _grKey);
 15542        }
 15543      } // overall style key
 15544      //
 15545  
 15546  
 15547      var hash = 0;
 15548  
 15549      for (var _i3 = 0; _i3 < propGrKeys.length; _i3++) {
 15550        var _grKey2 = propGrKeys[_i3];
 15551        var grHash = _p.styleKeys[_grKey2];
 15552        hash = hashInt(grHash, hash);
 15553      }
 15554  
 15555      _p.styleKey = hash; // label dims
 15556      //
 15557  
 15558      var labelDimsKey = _p.labelDimsKey = _p.styleKeys.labelDimensions;
 15559      _p.labelKey = propHash(ele, ['label'], labelDimsKey);
 15560      _p.labelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.labelKey);
 15561  
 15562      if (!isNode) {
 15563        _p.sourceLabelKey = propHash(ele, ['source-label'], labelDimsKey);
 15564        _p.sourceLabelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.sourceLabelKey);
 15565        _p.targetLabelKey = propHash(ele, ['target-label'], labelDimsKey);
 15566        _p.targetLabelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.targetLabelKey);
 15567      } // node
 15568      //
 15569  
 15570  
 15571      if (isNode) {
 15572        var _p$styleKeys = _p.styleKeys,
 15573            nodeBody = _p$styleKeys.nodeBody,
 15574            nodeBorder = _p$styleKeys.nodeBorder,
 15575            backgroundImage = _p$styleKeys.backgroundImage,
 15576            compound = _p$styleKeys.compound,
 15577            pie = _p$styleKeys.pie;
 15578        _p.nodeKey = hashIntsArray([nodeBorder, backgroundImage, compound, pie], nodeBody);
 15579        _p.hasPie = pie != 0;
 15580      }
 15581  
 15582      return oldStyleKey !== _p.styleKey;
 15583    };
 15584  
 15585    styfn.clearStyleHints = function (ele) {
 15586      var _p = ele._private;
 15587      _p.styleKeys = {};
 15588      _p.styleKey = null;
 15589      _p.labelKey = null;
 15590      _p.labelStyleKey = null;
 15591      _p.sourceLabelKey = null;
 15592      _p.sourceLabelStyleKey = null;
 15593      _p.targetLabelKey = null;
 15594      _p.targetLabelStyleKey = null;
 15595      _p.nodeKey = null;
 15596      _p.hasPie = null;
 15597    }; // apply a property to the style (for internal use)
 15598    // returns whether application was successful
 15599    //
 15600    // now, this function flattens the property, and here's how:
 15601    //
 15602    // for parsedProp:{ bypass: true, deleteBypass: true }
 15603    // no property is generated, instead the bypass property in the
 15604    // element's style is replaced by what's pointed to by the `bypassed`
 15605    // field in the bypass property (i.e. restoring the property the
 15606    // bypass was overriding)
 15607    //
 15608    // for parsedProp:{ mapped: truthy }
 15609    // the generated flattenedProp:{ mapping: prop }
 15610    //
 15611    // for parsedProp:{ bypass: true }
 15612    // the generated flattenedProp:{ bypassed: parsedProp }
 15613  
 15614  
 15615    styfn.applyParsedProperty = function (ele, parsedProp) {
 15616      var self = this;
 15617      var prop = parsedProp;
 15618      var style = ele._private.style;
 15619      var flatProp;
 15620      var types = self.types;
 15621      var type = self.properties[prop.name].type;
 15622      var propIsBypass = prop.bypass;
 15623      var origProp = style[prop.name];
 15624      var origPropIsBypass = origProp && origProp.bypass;
 15625      var _p = ele._private;
 15626      var flatPropMapping = 'mapping';
 15627  
 15628      var getVal = function getVal(p) {
 15629        if (p == null) {
 15630          return null;
 15631        } else if (p.pfValue != null) {
 15632          return p.pfValue;
 15633        } else {
 15634          return p.value;
 15635        }
 15636      };
 15637  
 15638      var checkTriggers = function checkTriggers() {
 15639        var fromVal = getVal(origProp);
 15640        var toVal = getVal(prop);
 15641        self.checkTriggers(ele, prop.name, fromVal, toVal);
 15642      }; // edge sanity checks to prevent the client from making serious mistakes
 15643  
 15644  
 15645      if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
 15646      parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
 15647      parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
 15648        prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
 15649      }
 15650  
 15651      if (prop["delete"]) {
 15652        // delete the property and use the default value on falsey value
 15653        style[prop.name] = undefined;
 15654        checkTriggers();
 15655        return true;
 15656      }
 15657  
 15658      if (prop.deleteBypassed) {
 15659        // delete the property that the
 15660        if (!origProp) {
 15661          checkTriggers();
 15662          return true; // can't delete if no prop
 15663        } else if (origProp.bypass) {
 15664          // delete bypassed
 15665          origProp.bypassed = undefined;
 15666          checkTriggers();
 15667          return true;
 15668        } else {
 15669          return false; // we're unsuccessful deleting the bypassed
 15670        }
 15671      } // check if we need to delete the current bypass
 15672  
 15673  
 15674      if (prop.deleteBypass) {
 15675        // then this property is just here to indicate we need to delete
 15676        if (!origProp) {
 15677          checkTriggers();
 15678          return true; // property is already not defined
 15679        } else if (origProp.bypass) {
 15680          // then replace the bypass property with the original
 15681          // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
 15682          style[prop.name] = origProp.bypassed;
 15683          checkTriggers();
 15684          return true;
 15685        } else {
 15686          return false; // we're unsuccessful deleting the bypass
 15687        }
 15688      }
 15689  
 15690      var printMappingErr = function printMappingErr() {
 15691        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');
 15692      }; // put the property in the style objects
 15693  
 15694  
 15695      switch (prop.mapped) {
 15696        // flatten the property if mapped
 15697        case types.mapData:
 15698          {
 15699            // flatten the field (e.g. data.foo.bar)
 15700            var fields = prop.field.split('.');
 15701            var fieldVal = _p.data;
 15702  
 15703            for (var i = 0; i < fields.length && fieldVal; i++) {
 15704              var field = fields[i];
 15705              fieldVal = fieldVal[field];
 15706            }
 15707  
 15708            if (fieldVal == null) {
 15709              printMappingErr();
 15710              return false;
 15711            }
 15712  
 15713            var percent;
 15714  
 15715            if (!number(fieldVal)) {
 15716              // then don't apply and fall back on the existing style
 15717              warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
 15718              return false;
 15719            } else {
 15720              var fieldWidth = prop.fieldMax - prop.fieldMin;
 15721  
 15722              if (fieldWidth === 0) {
 15723                // safety check -- not strictly necessary as no props of zero range should be passed here
 15724                percent = 0;
 15725              } else {
 15726                percent = (fieldVal - prop.fieldMin) / fieldWidth;
 15727              }
 15728            } // make sure to bound percent value
 15729  
 15730  
 15731            if (percent < 0) {
 15732              percent = 0;
 15733            } else if (percent > 1) {
 15734              percent = 1;
 15735            }
 15736  
 15737            if (type.color) {
 15738              var r1 = prop.valueMin[0];
 15739              var r2 = prop.valueMax[0];
 15740              var g1 = prop.valueMin[1];
 15741              var g2 = prop.valueMax[1];
 15742              var b1 = prop.valueMin[2];
 15743              var b2 = prop.valueMax[2];
 15744              var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
 15745              var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
 15746              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)];
 15747              flatProp = {
 15748                // colours are simple, so just create the flat property instead of expensive string parsing
 15749                bypass: prop.bypass,
 15750                // we're a bypass if the mapping property is a bypass
 15751                name: prop.name,
 15752                value: clr,
 15753                strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
 15754              };
 15755            } else if (type.number) {
 15756              var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
 15757              flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
 15758            } else {
 15759              return false; // can only map to colours and numbers
 15760            }
 15761  
 15762            if (!flatProp) {
 15763              // if we can't flatten the property, then don't apply the property and fall back on the existing style
 15764              printMappingErr();
 15765              return false;
 15766            }
 15767  
 15768            flatProp.mapping = prop; // keep a reference to the mapping
 15769  
 15770            prop = flatProp; // the flattened (mapped) property is the one we want
 15771  
 15772            break;
 15773          }
 15774        // direct mapping
 15775  
 15776        case types.data:
 15777          {
 15778            // flatten the field (e.g. data.foo.bar)
 15779            var _fields = prop.field.split('.');
 15780  
 15781            var _fieldVal = _p.data;
 15782  
 15783            for (var _i4 = 0; _i4 < _fields.length && _fieldVal; _i4++) {
 15784              var _field = _fields[_i4];
 15785              _fieldVal = _fieldVal[_field];
 15786            }
 15787  
 15788            if (_fieldVal != null) {
 15789              flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
 15790            }
 15791  
 15792            if (!flatProp) {
 15793              // if we can't flatten the property, then don't apply and fall back on the existing style
 15794              printMappingErr();
 15795              return false;
 15796            }
 15797  
 15798            flatProp.mapping = prop; // keep a reference to the mapping
 15799  
 15800            prop = flatProp; // the flattened (mapped) property is the one we want
 15801  
 15802            break;
 15803          }
 15804  
 15805        case types.fn:
 15806          {
 15807            var fn = prop.value;
 15808            var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
 15809  
 15810            prop.prevFnValue = fnRetVal;
 15811  
 15812            if (fnRetVal == null) {
 15813              warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
 15814              return false;
 15815            }
 15816  
 15817            flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
 15818  
 15819            if (!flatProp) {
 15820              warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
 15821              return false;
 15822            }
 15823  
 15824            flatProp.mapping = copy(prop); // keep a reference to the mapping
 15825  
 15826            prop = flatProp; // the flattened (mapped) property is the one we want
 15827  
 15828            break;
 15829          }
 15830  
 15831        case undefined:
 15832          break;
 15833        // just set the property
 15834  
 15835        default:
 15836          return false;
 15837        // not a valid mapping
 15838      } // if the property is a bypass property, then link the resultant property to the original one
 15839  
 15840  
 15841      if (propIsBypass) {
 15842        if (origPropIsBypass) {
 15843          // then this bypass overrides the existing one
 15844          prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
 15845        } else {
 15846          // then link the orig prop to the new bypass
 15847          prop.bypassed = origProp;
 15848        }
 15849  
 15850        style[prop.name] = prop; // and set
 15851      } else {
 15852        // prop is not bypass
 15853        if (origPropIsBypass) {
 15854          // then keep the orig prop (since it's a bypass) and link to the new prop
 15855          origProp.bypassed = prop;
 15856        } else {
 15857          // then just replace the old prop with the new one
 15858          style[prop.name] = prop;
 15859        }
 15860      }
 15861  
 15862      checkTriggers();
 15863      return true;
 15864    };
 15865  
 15866    styfn.cleanElements = function (eles, keepBypasses) {
 15867      for (var i = 0; i < eles.length; i++) {
 15868        var ele = eles[i];
 15869        this.clearStyleHints(ele);
 15870        ele.dirtyCompoundBoundsCache();
 15871        ele.dirtyBoundingBoxCache();
 15872  
 15873        if (!keepBypasses) {
 15874          ele._private.style = {};
 15875        } else {
 15876          var style = ele._private.style;
 15877          var propNames = Object.keys(style);
 15878  
 15879          for (var j = 0; j < propNames.length; j++) {
 15880            var propName = propNames[j];
 15881            var eleProp = style[propName];
 15882  
 15883            if (eleProp != null) {
 15884              if (eleProp.bypass) {
 15885                eleProp.bypassed = null;
 15886              } else {
 15887                style[propName] = null;
 15888              }
 15889            }
 15890          }
 15891        }
 15892      }
 15893    }; // updates the visual style for all elements (useful for manual style modification after init)
 15894  
 15895  
 15896    styfn.update = function () {
 15897      var cy = this._private.cy;
 15898      var eles = cy.mutableElements();
 15899      eles.updateStyle();
 15900    }; // diffProps : { name => { prev, next } }
 15901  
 15902  
 15903    styfn.updateTransitions = function (ele, diffProps) {
 15904      var self = this;
 15905      var _p = ele._private;
 15906      var props = ele.pstyle('transition-property').value;
 15907      var duration = ele.pstyle('transition-duration').pfValue;
 15908      var delay = ele.pstyle('transition-delay').pfValue;
 15909  
 15910      if (props.length > 0 && duration > 0) {
 15911        var style = {}; // build up the style to animate towards
 15912  
 15913        var anyPrev = false;
 15914  
 15915        for (var i = 0; i < props.length; i++) {
 15916          var prop = props[i];
 15917          var styProp = ele.pstyle(prop);
 15918          var diffProp = diffProps[prop];
 15919  
 15920          if (!diffProp) {
 15921            continue;
 15922          }
 15923  
 15924          var prevProp = diffProp.prev;
 15925          var fromProp = prevProp;
 15926          var toProp = diffProp.next != null ? diffProp.next : styProp;
 15927          var diff = false;
 15928          var initVal = void 0;
 15929          var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
 15930  
 15931          if (!fromProp) {
 15932            continue;
 15933          } // consider px values
 15934  
 15935  
 15936          if (number(fromProp.pfValue) && number(toProp.pfValue)) {
 15937            diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
 15938  
 15939            initVal = fromProp.pfValue + initDt * diff; // consider numerical values
 15940          } else if (number(fromProp.value) && number(toProp.value)) {
 15941            diff = toProp.value - fromProp.value; // nonzero is truthy
 15942  
 15943            initVal = fromProp.value + initDt * diff; // consider colour values
 15944          } else if (array(fromProp.value) && array(toProp.value)) {
 15945            diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
 15946            initVal = fromProp.strValue;
 15947          } // the previous value is good for an animation only if it's different
 15948  
 15949  
 15950          if (diff) {
 15951            style[prop] = toProp.strValue; // to val
 15952  
 15953            this.applyBypass(ele, prop, initVal); // from val
 15954  
 15955            anyPrev = true;
 15956          }
 15957        } // end if props allow ani
 15958        // can't transition if there's nothing previous to transition from
 15959  
 15960  
 15961        if (!anyPrev) {
 15962          return;
 15963        }
 15964  
 15965        _p.transitioning = true;
 15966        new Promise$1(function (resolve) {
 15967          if (delay > 0) {
 15968            ele.delayAnimation(delay).play().promise().then(resolve);
 15969          } else {
 15970            resolve();
 15971          }
 15972        }).then(function () {
 15973          return ele.animation({
 15974            style: style,
 15975            duration: duration,
 15976            easing: ele.pstyle('transition-timing-function').value,
 15977            queue: false
 15978          }).play().promise();
 15979        }).then(function () {
 15980          // if( !isBypass ){
 15981          self.removeBypasses(ele, props);
 15982          ele.emitAndNotify('style'); // }
 15983  
 15984          _p.transitioning = false;
 15985        });
 15986      } else if (_p.transitioning) {
 15987        this.removeBypasses(ele, props);
 15988        ele.emitAndNotify('style');
 15989        _p.transitioning = false;
 15990      }
 15991    };
 15992  
 15993    styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
 15994      var prop = this.properties[name];
 15995      var triggerCheck = getTrigger(prop);
 15996  
 15997      if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
 15998        onTrigger(prop);
 15999      }
 16000    };
 16001  
 16002    styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
 16003      var _this = this;
 16004  
 16005      this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
 16006        return prop.triggersZOrder;
 16007      }, function () {
 16008        _this._private.cy.notify('zorder', ele);
 16009      });
 16010    };
 16011  
 16012    styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
 16013      this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
 16014        return prop.triggersBounds;
 16015      }, function (prop) {
 16016        ele.dirtyCompoundBoundsCache();
 16017        ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
 16018        // then dirty the pll edge bb cache as well
 16019  
 16020        if ( // only for beziers -- so performance of other edges isn't affected
 16021        (ele.pstyle('curve-style').value === 'bezier' // already a bezier
 16022        // was just now changed to or from a bezier:
 16023        || name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier')) && prop.triggersBoundsOfParallelBeziers) {
 16024          ele.parallelEdges().forEach(function (pllEdge) {
 16025            if (pllEdge.isBundledBezier()) {
 16026              pllEdge.dirtyBoundingBoxCache();
 16027            }
 16028          });
 16029        }
 16030      });
 16031    };
 16032  
 16033    styfn.checkTriggers = function (ele, name, fromValue, toValue) {
 16034      ele.dirtyStyleCache();
 16035      this.checkZOrderTrigger(ele, name, fromValue, toValue);
 16036      this.checkBoundsTrigger(ele, name, fromValue, toValue);
 16037    };
 16038  
 16039    var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
 16040    // returns true iff application was successful for at least 1 specified property
 16041  
 16042    styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
 16043      var self = this;
 16044      var props = [];
 16045      var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
 16046  
 16047      if (name === '*' || name === '**') {
 16048        // apply to all property names
 16049        if (value !== undefined) {
 16050          for (var i = 0; i < self.properties.length; i++) {
 16051            var prop = self.properties[i];
 16052            var _name = prop.name;
 16053            var parsedProp = this.parse(_name, value, true);
 16054  
 16055            if (parsedProp) {
 16056              props.push(parsedProp);
 16057            }
 16058          }
 16059        }
 16060      } else if (string(name)) {
 16061        // then parse the single property
 16062        var _parsedProp = this.parse(name, value, true);
 16063  
 16064        if (_parsedProp) {
 16065          props.push(_parsedProp);
 16066        }
 16067      } else if (plainObject(name)) {
 16068        // then parse each property
 16069        var specifiedProps = name;
 16070        updateTransitions = value;
 16071        var names = Object.keys(specifiedProps);
 16072  
 16073        for (var _i = 0; _i < names.length; _i++) {
 16074          var _name2 = names[_i];
 16075          var _value = specifiedProps[_name2];
 16076  
 16077          if (_value === undefined) {
 16078            // try camel case name too
 16079            _value = specifiedProps[dash2camel(_name2)];
 16080          }
 16081  
 16082          if (_value !== undefined) {
 16083            var _parsedProp2 = this.parse(_name2, _value, true);
 16084  
 16085            if (_parsedProp2) {
 16086              props.push(_parsedProp2);
 16087            }
 16088          }
 16089        }
 16090      } else {
 16091        // can't do anything without well defined properties
 16092        return false;
 16093      } // we've failed if there are no valid properties
 16094  
 16095  
 16096      if (props.length === 0) {
 16097        return false;
 16098      } // now, apply the bypass properties on the elements
 16099  
 16100  
 16101      var ret = false; // return true if at least one succesful bypass applied
 16102  
 16103      for (var _i2 = 0; _i2 < eles.length; _i2++) {
 16104        // for each ele
 16105        var ele = eles[_i2];
 16106        var diffProps = {};
 16107        var diffProp = void 0;
 16108  
 16109        for (var j = 0; j < props.length; j++) {
 16110          // for each prop
 16111          var _prop = props[j];
 16112  
 16113          if (updateTransitions) {
 16114            var prevProp = ele.pstyle(_prop.name);
 16115            diffProp = diffProps[_prop.name] = {
 16116              prev: prevProp
 16117            };
 16118          }
 16119  
 16120          ret = this.applyParsedProperty(ele, _prop) || ret;
 16121  
 16122          if (updateTransitions) {
 16123            diffProp.next = ele.pstyle(_prop.name);
 16124          }
 16125        } // for props
 16126  
 16127  
 16128        if (ret) {
 16129          this.updateStyleHints(ele);
 16130        }
 16131  
 16132        if (updateTransitions) {
 16133          this.updateTransitions(ele, diffProps, isBypass);
 16134        }
 16135      } // for eles
 16136  
 16137  
 16138      return ret;
 16139    }; // only useful in specific cases like animation
 16140  
 16141  
 16142    styfn$1.overrideBypass = function (eles, name, value) {
 16143      name = camel2dash(name);
 16144  
 16145      for (var i = 0; i < eles.length; i++) {
 16146        var ele = eles[i];
 16147        var prop = ele._private.style[name];
 16148        var type = this.properties[name].type;
 16149        var isColor = type.color;
 16150        var isMulti = type.mutiple;
 16151        var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
 16152  
 16153        if (!prop || !prop.bypass) {
 16154          // need a bypass if one doesn't exist
 16155          this.applyBypass(ele, name, value);
 16156        } else {
 16157          prop.value = value;
 16158  
 16159          if (prop.pfValue != null) {
 16160            prop.pfValue = value;
 16161          }
 16162  
 16163          if (isColor) {
 16164            prop.strValue = 'rgb(' + value.join(',') + ')';
 16165          } else if (isMulti) {
 16166            prop.strValue = value.join(' ');
 16167          } else {
 16168            prop.strValue = '' + value;
 16169          }
 16170  
 16171          this.updateStyleHints(ele);
 16172        }
 16173  
 16174        this.checkTriggers(ele, name, oldValue, value);
 16175      }
 16176    };
 16177  
 16178    styfn$1.removeAllBypasses = function (eles, updateTransitions) {
 16179      return this.removeBypasses(eles, this.propertyNames, updateTransitions);
 16180    };
 16181  
 16182    styfn$1.removeBypasses = function (eles, props, updateTransitions) {
 16183      var isBypass = true;
 16184  
 16185      for (var j = 0; j < eles.length; j++) {
 16186        var ele = eles[j];
 16187        var diffProps = {};
 16188  
 16189        for (var i = 0; i < props.length; i++) {
 16190          var name = props[i];
 16191          var prop = this.properties[name];
 16192          var prevProp = ele.pstyle(prop.name);
 16193  
 16194          if (!prevProp || !prevProp.bypass) {
 16195            // if a bypass doesn't exist for the prop, nothing needs to be removed
 16196            continue;
 16197          }
 16198  
 16199          var value = ''; // empty => remove bypass
 16200  
 16201          var parsedProp = this.parse(name, value, true);
 16202          var diffProp = diffProps[prop.name] = {
 16203            prev: prevProp
 16204          };
 16205          this.applyParsedProperty(ele, parsedProp);
 16206          diffProp.next = ele.pstyle(prop.name);
 16207        } // for props
 16208  
 16209  
 16210        this.updateStyleHints(ele);
 16211  
 16212        if (updateTransitions) {
 16213          this.updateTransitions(ele, diffProps, isBypass);
 16214        }
 16215      } // for eles
 16216  
 16217    };
 16218  
 16219    var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
 16220  
 16221    styfn$2.getEmSizeInPixels = function () {
 16222      var px = this.containerCss('font-size');
 16223  
 16224      if (px != null) {
 16225        return parseFloat(px);
 16226      } else {
 16227        return 1; // for headless
 16228      }
 16229    }; // gets css property from the core container
 16230  
 16231  
 16232    styfn$2.containerCss = function (propName) {
 16233      var cy = this._private.cy;
 16234      var domElement = cy.container();
 16235  
 16236      if (window$1 && domElement && window$1.getComputedStyle) {
 16237        return window$1.getComputedStyle(domElement).getPropertyValue(propName);
 16238      }
 16239    };
 16240  
 16241    var styfn$3 = {}; // gets the rendered style for an element
 16242  
 16243    styfn$3.getRenderedStyle = function (ele, prop) {
 16244      if (prop) {
 16245        return this.getStylePropertyValue(ele, prop, true);
 16246      } else {
 16247        return this.getRawStyle(ele, true);
 16248      }
 16249    }; // gets the raw style for an element
 16250  
 16251  
 16252    styfn$3.getRawStyle = function (ele, isRenderedVal) {
 16253      var self = this;
 16254      ele = ele[0]; // insure it's an element
 16255  
 16256      if (ele) {
 16257        var rstyle = {};
 16258  
 16259        for (var i = 0; i < self.properties.length; i++) {
 16260          var prop = self.properties[i];
 16261          var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
 16262  
 16263          if (val != null) {
 16264            rstyle[prop.name] = val;
 16265            rstyle[dash2camel(prop.name)] = val;
 16266          }
 16267        }
 16268  
 16269        return rstyle;
 16270      }
 16271    };
 16272  
 16273    styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
 16274      var pstyle = ele.pstyle(property)[subproperty][index];
 16275      return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
 16276    };
 16277  
 16278    styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
 16279      var self = this;
 16280      ele = ele[0]; // insure it's an element
 16281  
 16282      if (ele) {
 16283        var prop = self.properties[propName];
 16284  
 16285        if (prop.alias) {
 16286          prop = prop.pointsTo;
 16287        }
 16288  
 16289        var type = prop.type;
 16290        var styleProp = ele.pstyle(prop.name);
 16291  
 16292        if (styleProp) {
 16293          var value = styleProp.value,
 16294              units = styleProp.units,
 16295              strValue = styleProp.strValue;
 16296  
 16297          if (isRenderedVal && type.number && value != null && number(value)) {
 16298            var zoom = ele.cy().zoom();
 16299  
 16300            var getRenderedValue = function getRenderedValue(val) {
 16301              return val * zoom;
 16302            };
 16303  
 16304            var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
 16305              return getRenderedValue(val) + units;
 16306            };
 16307  
 16308            var isArrayValue = array(value);
 16309            var haveUnits = isArrayValue ? units.every(function (u) {
 16310              return u != null;
 16311            }) : units != null;
 16312  
 16313            if (haveUnits) {
 16314              if (isArrayValue) {
 16315                return value.map(function (v, i) {
 16316                  return getValueStringWithUnits(v, units[i]);
 16317                }).join(' ');
 16318              } else {
 16319                return getValueStringWithUnits(value, units);
 16320              }
 16321            } else {
 16322              if (isArrayValue) {
 16323                return value.map(function (v) {
 16324                  return string(v) ? v : '' + getRenderedValue(v);
 16325                }).join(' ');
 16326              } else {
 16327                return '' + getRenderedValue(value);
 16328              }
 16329            }
 16330          } else if (strValue != null) {
 16331            return strValue;
 16332          }
 16333        }
 16334  
 16335        return null;
 16336      }
 16337    };
 16338  
 16339    styfn$3.getAnimationStartStyle = function (ele, aniProps) {
 16340      var rstyle = {};
 16341  
 16342      for (var i = 0; i < aniProps.length; i++) {
 16343        var aniProp = aniProps[i];
 16344        var name = aniProp.name;
 16345        var styleProp = ele.pstyle(name);
 16346  
 16347        if (styleProp !== undefined) {
 16348          // then make a prop of it
 16349          if (plainObject(styleProp)) {
 16350            styleProp = this.parse(name, styleProp.strValue);
 16351          } else {
 16352            styleProp = this.parse(name, styleProp);
 16353          }
 16354        }
 16355  
 16356        if (styleProp) {
 16357          rstyle[name] = styleProp;
 16358        }
 16359      }
 16360  
 16361      return rstyle;
 16362    };
 16363  
 16364    styfn$3.getPropsList = function (propsObj) {
 16365      var self = this;
 16366      var rstyle = [];
 16367      var style = propsObj;
 16368      var props = self.properties;
 16369  
 16370      if (style) {
 16371        var names = Object.keys(style);
 16372  
 16373        for (var i = 0; i < names.length; i++) {
 16374          var name = names[i];
 16375          var val = style[name];
 16376          var prop = props[name] || props[camel2dash(name)];
 16377          var styleProp = this.parse(prop.name, val);
 16378  
 16379          if (styleProp) {
 16380            rstyle.push(styleProp);
 16381          }
 16382        }
 16383      }
 16384  
 16385      return rstyle;
 16386    };
 16387  
 16388    styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
 16389      var hash = seed;
 16390      var name, val, strVal, chVal;
 16391      var i, j;
 16392  
 16393      for (i = 0; i < propNames.length; i++) {
 16394        name = propNames[i];
 16395        val = ele.pstyle(name, false);
 16396  
 16397        if (val == null) {
 16398          continue;
 16399        } else if (val.pfValue != null) {
 16400          hash = hashInt(chVal, hash);
 16401        } else {
 16402          strVal = val.strValue;
 16403  
 16404          for (j = 0; j < strVal.length; j++) {
 16405            chVal = strVal.charCodeAt(j);
 16406            hash = hashInt(chVal, hash);
 16407          }
 16408        }
 16409      }
 16410  
 16411      return hash;
 16412    };
 16413  
 16414    styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
 16415  
 16416    var styfn$4 = {};
 16417  
 16418    styfn$4.appendFromJson = function (json) {
 16419      var style = this;
 16420  
 16421      for (var i = 0; i < json.length; i++) {
 16422        var context = json[i];
 16423        var selector = context.selector;
 16424        var props = context.style || context.css;
 16425        var names = Object.keys(props);
 16426        style.selector(selector); // apply selector
 16427  
 16428        for (var j = 0; j < names.length; j++) {
 16429          var name = names[j];
 16430          var value = props[name];
 16431          style.css(name, value); // apply property
 16432        }
 16433      }
 16434  
 16435      return style;
 16436    }; // accessible cy.style() function
 16437  
 16438  
 16439    styfn$4.fromJson = function (json) {
 16440      var style = this;
 16441      style.resetToDefault();
 16442      style.appendFromJson(json);
 16443      return style;
 16444    }; // get json from cy.style() api
 16445  
 16446  
 16447    styfn$4.json = function () {
 16448      var json = [];
 16449  
 16450      for (var i = this.defaultLength; i < this.length; i++) {
 16451        var cxt = this[i];
 16452        var selector = cxt.selector;
 16453        var props = cxt.properties;
 16454        var css = {};
 16455  
 16456        for (var j = 0; j < props.length; j++) {
 16457          var prop = props[j];
 16458          css[prop.name] = prop.strValue;
 16459        }
 16460  
 16461        json.push({
 16462          selector: !selector ? 'core' : selector.toString(),
 16463          style: css
 16464        });
 16465      }
 16466  
 16467      return json;
 16468    };
 16469  
 16470    var styfn$5 = {};
 16471  
 16472    styfn$5.appendFromString = function (string) {
 16473      var self = this;
 16474      var style = this;
 16475      var remaining = '' + string;
 16476      var selAndBlockStr;
 16477      var blockRem;
 16478      var propAndValStr; // remove comments from the style string
 16479  
 16480      remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
 16481  
 16482      function removeSelAndBlockFromRemaining() {
 16483        // remove the parsed selector and block from the remaining text to parse
 16484        if (remaining.length > selAndBlockStr.length) {
 16485          remaining = remaining.substr(selAndBlockStr.length);
 16486        } else {
 16487          remaining = '';
 16488        }
 16489      }
 16490  
 16491      function removePropAndValFromRem() {
 16492        // remove the parsed property and value from the remaining block text to parse
 16493        if (blockRem.length > propAndValStr.length) {
 16494          blockRem = blockRem.substr(propAndValStr.length);
 16495        } else {
 16496          blockRem = '';
 16497        }
 16498      }
 16499  
 16500      for (;;) {
 16501        var nothingLeftToParse = remaining.match(/^\s*$/);
 16502  
 16503        if (nothingLeftToParse) {
 16504          break;
 16505        }
 16506  
 16507        var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
 16508  
 16509        if (!selAndBlock) {
 16510          warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
 16511          break;
 16512        }
 16513  
 16514        selAndBlockStr = selAndBlock[0]; // parse the selector
 16515  
 16516        var selectorStr = selAndBlock[1];
 16517  
 16518        if (selectorStr !== 'core') {
 16519          var selector = new Selector(selectorStr);
 16520  
 16521          if (selector.invalid) {
 16522            warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
 16523  
 16524            removeSelAndBlockFromRemaining();
 16525            continue;
 16526          }
 16527        } // parse the block of properties and values
 16528  
 16529  
 16530        var blockStr = selAndBlock[2];
 16531        var invalidBlock = false;
 16532        blockRem = blockStr;
 16533        var props = [];
 16534  
 16535        for (;;) {
 16536          var _nothingLeftToParse = blockRem.match(/^\s*$/);
 16537  
 16538          if (_nothingLeftToParse) {
 16539            break;
 16540          }
 16541  
 16542          var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
 16543  
 16544          if (!propAndVal) {
 16545            warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
 16546            invalidBlock = true;
 16547            break;
 16548          }
 16549  
 16550          propAndValStr = propAndVal[0];
 16551          var propStr = propAndVal[1];
 16552          var valStr = propAndVal[2];
 16553          var prop = self.properties[propStr];
 16554  
 16555          if (!prop) {
 16556            warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
 16557  
 16558            removePropAndValFromRem();
 16559            continue;
 16560          }
 16561  
 16562          var parsedProp = style.parse(propStr, valStr);
 16563  
 16564          if (!parsedProp) {
 16565            warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
 16566  
 16567            removePropAndValFromRem();
 16568            continue;
 16569          }
 16570  
 16571          props.push({
 16572            name: propStr,
 16573            val: valStr
 16574          });
 16575          removePropAndValFromRem();
 16576        }
 16577  
 16578        if (invalidBlock) {
 16579          removeSelAndBlockFromRemaining();
 16580          break;
 16581        } // put the parsed block in the style
 16582  
 16583  
 16584        style.selector(selectorStr);
 16585  
 16586        for (var i = 0; i < props.length; i++) {
 16587          var _prop = props[i];
 16588          style.css(_prop.name, _prop.val);
 16589        }
 16590  
 16591        removeSelAndBlockFromRemaining();
 16592      }
 16593  
 16594      return style;
 16595    };
 16596  
 16597    styfn$5.fromString = function (string) {
 16598      var style = this;
 16599      style.resetToDefault();
 16600      style.appendFromString(string);
 16601      return style;
 16602    };
 16603  
 16604    var styfn$6 = {};
 16605  
 16606    (function () {
 16607      var number = number$1;
 16608      var rgba = rgbaNoBackRefs;
 16609      var hsla = hslaNoBackRefs;
 16610      var hex3$1 = hex3;
 16611      var hex6$1 = hex6;
 16612  
 16613      var data = function data(prefix) {
 16614        return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
 16615      };
 16616  
 16617      var mapData = function mapData(prefix) {
 16618        var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
 16619        return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
 16620      };
 16621  
 16622      var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
 16623  
 16624      styfn$6.types = {
 16625        time: {
 16626          number: true,
 16627          min: 0,
 16628          units: 's|ms',
 16629          implicitUnits: 'ms'
 16630        },
 16631        percent: {
 16632          number: true,
 16633          min: 0,
 16634          max: 100,
 16635          units: '%',
 16636          implicitUnits: '%'
 16637        },
 16638        percentages: {
 16639          number: true,
 16640          min: 0,
 16641          max: 100,
 16642          units: '%',
 16643          implicitUnits: '%',
 16644          multiple: true
 16645        },
 16646        zeroOneNumber: {
 16647          number: true,
 16648          min: 0,
 16649          max: 1,
 16650          unitless: true
 16651        },
 16652        zeroOneNumbers: {
 16653          number: true,
 16654          min: 0,
 16655          max: 1,
 16656          unitless: true,
 16657          multiple: true
 16658        },
 16659        nOneOneNumber: {
 16660          number: true,
 16661          min: -1,
 16662          max: 1,
 16663          unitless: true
 16664        },
 16665        nonNegativeInt: {
 16666          number: true,
 16667          min: 0,
 16668          integer: true,
 16669          unitless: true
 16670        },
 16671        position: {
 16672          enums: ['parent', 'origin']
 16673        },
 16674        nodeSize: {
 16675          number: true,
 16676          min: 0,
 16677          enums: ['label']
 16678        },
 16679        number: {
 16680          number: true,
 16681          unitless: true
 16682        },
 16683        numbers: {
 16684          number: true,
 16685          unitless: true,
 16686          multiple: true
 16687        },
 16688        positiveNumber: {
 16689          number: true,
 16690          unitless: true,
 16691          min: 0,
 16692          strictMin: true
 16693        },
 16694        size: {
 16695          number: true,
 16696          min: 0
 16697        },
 16698        bidirectionalSize: {
 16699          number: true
 16700        },
 16701        // allows negative
 16702        bidirectionalSizes: {
 16703          number: true,
 16704          multiple: true
 16705        },
 16706        // allows negative
 16707        sizeMaybePercent: {
 16708          number: true,
 16709          min: 0,
 16710          allowPercent: true
 16711        },
 16712        axisDirection: {
 16713          enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
 16714        },
 16715        paddingRelativeTo: {
 16716          enums: ['width', 'height', 'average', 'min', 'max']
 16717        },
 16718        bgWH: {
 16719          number: true,
 16720          min: 0,
 16721          allowPercent: true,
 16722          enums: ['auto'],
 16723          multiple: true
 16724        },
 16725        bgPos: {
 16726          number: true,
 16727          allowPercent: true,
 16728          multiple: true
 16729        },
 16730        bgRelativeTo: {
 16731          enums: ['inner', 'include-padding'],
 16732          multiple: true
 16733        },
 16734        bgRepeat: {
 16735          enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
 16736          multiple: true
 16737        },
 16738        bgFit: {
 16739          enums: ['none', 'contain', 'cover'],
 16740          multiple: true
 16741        },
 16742        bgCrossOrigin: {
 16743          enums: ['anonymous', 'use-credentials'],
 16744          multiple: true
 16745        },
 16746        bgClip: {
 16747          enums: ['none', 'node'],
 16748          multiple: true
 16749        },
 16750        color: {
 16751          color: true
 16752        },
 16753        colors: {
 16754          color: true,
 16755          multiple: true
 16756        },
 16757        fill: {
 16758          enums: ['solid', 'linear-gradient', 'radial-gradient']
 16759        },
 16760        bool: {
 16761          enums: ['yes', 'no']
 16762        },
 16763        lineStyle: {
 16764          enums: ['solid', 'dotted', 'dashed']
 16765        },
 16766        lineCap: {
 16767          enums: ['butt', 'round', 'square']
 16768        },
 16769        borderStyle: {
 16770          enums: ['solid', 'dotted', 'dashed', 'double']
 16771        },
 16772        curveStyle: {
 16773          enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'taxi']
 16774        },
 16775        fontFamily: {
 16776          regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
 16777        },
 16778        fontStyle: {
 16779          enums: ['italic', 'normal', 'oblique']
 16780        },
 16781        fontWeight: {
 16782          enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
 16783        },
 16784        textDecoration: {
 16785          enums: ['none', 'underline', 'overline', 'line-through']
 16786        },
 16787        textTransform: {
 16788          enums: ['none', 'uppercase', 'lowercase']
 16789        },
 16790        textWrap: {
 16791          enums: ['none', 'wrap', 'ellipsis']
 16792        },
 16793        textOverflowWrap: {
 16794          enums: ['whitespace', 'anywhere']
 16795        },
 16796        textBackgroundShape: {
 16797          enums: ['rectangle', 'roundrectangle', 'round-rectangle']
 16798        },
 16799        nodeShape: {
 16800          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']
 16801        },
 16802        compoundIncludeLabels: {
 16803          enums: ['include', 'exclude']
 16804        },
 16805        arrowShape: {
 16806          enums: ['tee', 'triangle', 'triangle-tee', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
 16807        },
 16808        arrowFill: {
 16809          enums: ['filled', 'hollow']
 16810        },
 16811        display: {
 16812          enums: ['element', 'none']
 16813        },
 16814        visibility: {
 16815          enums: ['hidden', 'visible']
 16816        },
 16817        zCompoundDepth: {
 16818          enums: ['bottom', 'orphan', 'auto', 'top']
 16819        },
 16820        zIndexCompare: {
 16821          enums: ['auto', 'manual']
 16822        },
 16823        valign: {
 16824          enums: ['top', 'center', 'bottom']
 16825        },
 16826        halign: {
 16827          enums: ['left', 'center', 'right']
 16828        },
 16829        justification: {
 16830          enums: ['left', 'center', 'right', 'auto']
 16831        },
 16832        text: {
 16833          string: true
 16834        },
 16835        data: {
 16836          mapping: true,
 16837          regex: data('data')
 16838        },
 16839        layoutData: {
 16840          mapping: true,
 16841          regex: data('layoutData')
 16842        },
 16843        scratch: {
 16844          mapping: true,
 16845          regex: data('scratch')
 16846        },
 16847        mapData: {
 16848          mapping: true,
 16849          regex: mapData('mapData')
 16850        },
 16851        mapLayoutData: {
 16852          mapping: true,
 16853          regex: mapData('mapLayoutData')
 16854        },
 16855        mapScratch: {
 16856          mapping: true,
 16857          regex: mapData('mapScratch')
 16858        },
 16859        fn: {
 16860          mapping: true,
 16861          fn: true
 16862        },
 16863        url: {
 16864          regexes: urlRegexes,
 16865          singleRegexMatchValue: true
 16866        },
 16867        urls: {
 16868          regexes: urlRegexes,
 16869          singleRegexMatchValue: true,
 16870          multiple: true
 16871        },
 16872        propList: {
 16873          propList: true
 16874        },
 16875        angle: {
 16876          number: true,
 16877          units: 'deg|rad',
 16878          implicitUnits: 'rad'
 16879        },
 16880        textRotation: {
 16881          number: true,
 16882          units: 'deg|rad',
 16883          implicitUnits: 'rad',
 16884          enums: ['none', 'autorotate']
 16885        },
 16886        polygonPointList: {
 16887          number: true,
 16888          multiple: true,
 16889          evenMultiple: true,
 16890          min: -1,
 16891          max: 1,
 16892          unitless: true
 16893        },
 16894        edgeDistances: {
 16895          enums: ['intersection', 'node-position']
 16896        },
 16897        edgeEndpoint: {
 16898          number: true,
 16899          multiple: true,
 16900          units: '%|px|em|deg|rad',
 16901          implicitUnits: 'px',
 16902          enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
 16903          singleEnum: true,
 16904          validate: function validate(valArr, unitsArr) {
 16905            switch (valArr.length) {
 16906              case 2:
 16907                // can be % or px only
 16908                return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
 16909  
 16910              case 1:
 16911                // can be enum, deg, or rad only
 16912                return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
 16913  
 16914              default:
 16915                return false;
 16916            }
 16917          }
 16918        },
 16919        easing: {
 16920          regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
 16921          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']
 16922        },
 16923        gradientDirection: {
 16924          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']
 16925        },
 16926        boundsExpansion: {
 16927          number: true,
 16928          multiple: true,
 16929          min: 0,
 16930          validate: function validate(valArr) {
 16931            var length = valArr.length;
 16932            return length === 1 || length === 2 || length === 4;
 16933          }
 16934        }
 16935      };
 16936      var diff = {
 16937        zeroNonZero: function zeroNonZero(val1, val2) {
 16938          if ((val1 == null || val2 == null) && val1 !== val2) {
 16939            return true; // null cases could represent any value
 16940          }
 16941  
 16942          if (val1 == 0 && val2 != 0) {
 16943            return true;
 16944          } else if (val1 != 0 && val2 == 0) {
 16945            return true;
 16946          } else {
 16947            return false;
 16948          }
 16949        },
 16950        any: function any(val1, val2) {
 16951          return val1 != val2;
 16952        }
 16953      }; // define visual style properties
 16954      //
 16955      // - n.b. adding a new group of props may require updates to updateStyleHints()
 16956      // - adding new props to an existing group gets handled automatically
 16957  
 16958      var t = styfn$6.types;
 16959      var mainLabel = [{
 16960        name: 'label',
 16961        type: t.text,
 16962        triggersBounds: diff.any
 16963      }, {
 16964        name: 'text-rotation',
 16965        type: t.textRotation,
 16966        triggersBounds: diff.any
 16967      }, {
 16968        name: 'text-margin-x',
 16969        type: t.bidirectionalSize,
 16970        triggersBounds: diff.any
 16971      }, {
 16972        name: 'text-margin-y',
 16973        type: t.bidirectionalSize,
 16974        triggersBounds: diff.any
 16975      }];
 16976      var sourceLabel = [{
 16977        name: 'source-label',
 16978        type: t.text,
 16979        triggersBounds: diff.any
 16980      }, {
 16981        name: 'source-text-rotation',
 16982        type: t.textRotation,
 16983        triggersBounds: diff.any
 16984      }, {
 16985        name: 'source-text-margin-x',
 16986        type: t.bidirectionalSize,
 16987        triggersBounds: diff.any
 16988      }, {
 16989        name: 'source-text-margin-y',
 16990        type: t.bidirectionalSize,
 16991        triggersBounds: diff.any
 16992      }, {
 16993        name: 'source-text-offset',
 16994        type: t.size,
 16995        triggersBounds: diff.any
 16996      }];
 16997      var targetLabel = [{
 16998        name: 'target-label',
 16999        type: t.text,
 17000        triggersBounds: diff.any
 17001      }, {
 17002        name: 'target-text-rotation',
 17003        type: t.textRotation,
 17004        triggersBounds: diff.any
 17005      }, {
 17006        name: 'target-text-margin-x',
 17007        type: t.bidirectionalSize,
 17008        triggersBounds: diff.any
 17009      }, {
 17010        name: 'target-text-margin-y',
 17011        type: t.bidirectionalSize,
 17012        triggersBounds: diff.any
 17013      }, {
 17014        name: 'target-text-offset',
 17015        type: t.size,
 17016        triggersBounds: diff.any
 17017      }];
 17018      var labelDimensions = [{
 17019        name: 'font-family',
 17020        type: t.fontFamily,
 17021        triggersBounds: diff.any
 17022      }, {
 17023        name: 'font-style',
 17024        type: t.fontStyle,
 17025        triggersBounds: diff.any
 17026      }, {
 17027        name: 'font-weight',
 17028        type: t.fontWeight,
 17029        triggersBounds: diff.any
 17030      }, {
 17031        name: 'font-size',
 17032        type: t.size,
 17033        triggersBounds: diff.any
 17034      }, {
 17035        name: 'text-transform',
 17036        type: t.textTransform,
 17037        triggersBounds: diff.any
 17038      }, {
 17039        name: 'text-wrap',
 17040        type: t.textWrap,
 17041        triggersBounds: diff.any
 17042      }, {
 17043        name: 'text-overflow-wrap',
 17044        type: t.textOverflowWrap,
 17045        triggersBounds: diff.any
 17046      }, {
 17047        name: 'text-max-width',
 17048        type: t.size,
 17049        triggersBounds: diff.any
 17050      }, {
 17051        name: 'text-outline-width',
 17052        type: t.size,
 17053        triggersBounds: diff.any
 17054      }, {
 17055        name: 'line-height',
 17056        type: t.positiveNumber,
 17057        triggersBounds: diff.any
 17058      }];
 17059      var commonLabel = [{
 17060        name: 'text-valign',
 17061        type: t.valign,
 17062        triggersBounds: diff.any
 17063      }, {
 17064        name: 'text-halign',
 17065        type: t.halign,
 17066        triggersBounds: diff.any
 17067      }, {
 17068        name: 'color',
 17069        type: t.color
 17070      }, {
 17071        name: 'text-outline-color',
 17072        type: t.color
 17073      }, {
 17074        name: 'text-outline-opacity',
 17075        type: t.zeroOneNumber
 17076      }, {
 17077        name: 'text-background-color',
 17078        type: t.color
 17079      }, {
 17080        name: 'text-background-opacity',
 17081        type: t.zeroOneNumber
 17082      }, {
 17083        name: 'text-background-padding',
 17084        type: t.size,
 17085        triggersBounds: diff.any
 17086      }, {
 17087        name: 'text-border-opacity',
 17088        type: t.zeroOneNumber
 17089      }, {
 17090        name: 'text-border-color',
 17091        type: t.color
 17092      }, {
 17093        name: 'text-border-width',
 17094        type: t.size,
 17095        triggersBounds: diff.any
 17096      }, {
 17097        name: 'text-border-style',
 17098        type: t.borderStyle,
 17099        triggersBounds: diff.any
 17100      }, {
 17101        name: 'text-background-shape',
 17102        type: t.textBackgroundShape,
 17103        triggersBounds: diff.any
 17104      }, {
 17105        name: 'text-justification',
 17106        type: t.justification
 17107      }];
 17108      var behavior = [{
 17109        name: 'events',
 17110        type: t.bool
 17111      }, {
 17112        name: 'text-events',
 17113        type: t.bool
 17114      }];
 17115      var visibility = [{
 17116        name: 'display',
 17117        type: t.display,
 17118        triggersZOrder: diff.any,
 17119        triggersBounds: diff.any,
 17120        triggersBoundsOfParallelBeziers: true
 17121      }, {
 17122        name: 'visibility',
 17123        type: t.visibility,
 17124        triggersZOrder: diff.any
 17125      }, {
 17126        name: 'opacity',
 17127        type: t.zeroOneNumber,
 17128        triggersZOrder: diff.zeroNonZero
 17129      }, {
 17130        name: 'text-opacity',
 17131        type: t.zeroOneNumber
 17132      }, {
 17133        name: 'min-zoomed-font-size',
 17134        type: t.size
 17135      }, {
 17136        name: 'z-compound-depth',
 17137        type: t.zCompoundDepth,
 17138        triggersZOrder: diff.any
 17139      }, {
 17140        name: 'z-index-compare',
 17141        type: t.zIndexCompare,
 17142        triggersZOrder: diff.any
 17143      }, {
 17144        name: 'z-index',
 17145        type: t.nonNegativeInt,
 17146        triggersZOrder: diff.any
 17147      }];
 17148      var overlay = [{
 17149        name: 'overlay-padding',
 17150        type: t.size,
 17151        triggersBounds: diff.any
 17152      }, {
 17153        name: 'overlay-color',
 17154        type: t.color
 17155      }, {
 17156        name: 'overlay-opacity',
 17157        type: t.zeroOneNumber,
 17158        triggersBounds: diff.zeroNonZero
 17159      }];
 17160      var transition = [{
 17161        name: 'transition-property',
 17162        type: t.propList
 17163      }, {
 17164        name: 'transition-duration',
 17165        type: t.time
 17166      }, {
 17167        name: 'transition-delay',
 17168        type: t.time
 17169      }, {
 17170        name: 'transition-timing-function',
 17171        type: t.easing
 17172      }];
 17173  
 17174      var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
 17175        if (parsedProp.value === 'label') {
 17176          return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
 17177        } else {
 17178          return parsedProp.pfValue;
 17179        }
 17180      };
 17181  
 17182      var nodeBody = [{
 17183        name: 'height',
 17184        type: t.nodeSize,
 17185        triggersBounds: diff.any,
 17186        hashOverride: nodeSizeHashOverride
 17187      }, {
 17188        name: 'width',
 17189        type: t.nodeSize,
 17190        triggersBounds: diff.any,
 17191        hashOverride: nodeSizeHashOverride
 17192      }, {
 17193        name: 'shape',
 17194        type: t.nodeShape,
 17195        triggersBounds: diff.any
 17196      }, {
 17197        name: 'shape-polygon-points',
 17198        type: t.polygonPointList,
 17199        triggersBounds: diff.any
 17200      }, {
 17201        name: 'background-color',
 17202        type: t.color
 17203      }, {
 17204        name: 'background-fill',
 17205        type: t.fill
 17206      }, {
 17207        name: 'background-opacity',
 17208        type: t.zeroOneNumber
 17209      }, {
 17210        name: 'background-blacken',
 17211        type: t.nOneOneNumber
 17212      }, {
 17213        name: 'background-gradient-stop-colors',
 17214        type: t.colors
 17215      }, {
 17216        name: 'background-gradient-stop-positions',
 17217        type: t.percentages
 17218      }, {
 17219        name: 'background-gradient-direction',
 17220        type: t.gradientDirection
 17221      }, {
 17222        name: 'padding',
 17223        type: t.sizeMaybePercent,
 17224        triggersBounds: diff.any
 17225      }, {
 17226        name: 'padding-relative-to',
 17227        type: t.paddingRelativeTo,
 17228        triggersBounds: diff.any
 17229      }, {
 17230        name: 'bounds-expansion',
 17231        type: t.boundsExpansion,
 17232        triggersBounds: diff.any
 17233      }];
 17234      var nodeBorder = [{
 17235        name: 'border-color',
 17236        type: t.color
 17237      }, {
 17238        name: 'border-opacity',
 17239        type: t.zeroOneNumber
 17240      }, {
 17241        name: 'border-width',
 17242        type: t.size,
 17243        triggersBounds: diff.any
 17244      }, {
 17245        name: 'border-style',
 17246        type: t.borderStyle
 17247      }];
 17248      var backgroundImage = [{
 17249        name: 'background-image',
 17250        type: t.urls
 17251      }, {
 17252        name: 'background-image-crossorigin',
 17253        type: t.bgCrossOrigin
 17254      }, {
 17255        name: 'background-image-opacity',
 17256        type: t.zeroOneNumbers
 17257      }, {
 17258        name: 'background-position-x',
 17259        type: t.bgPos
 17260      }, {
 17261        name: 'background-position-y',
 17262        type: t.bgPos
 17263      }, {
 17264        name: 'background-width-relative-to',
 17265        type: t.bgRelativeTo
 17266      }, {
 17267        name: 'background-height-relative-to',
 17268        type: t.bgRelativeTo
 17269      }, {
 17270        name: 'background-repeat',
 17271        type: t.bgRepeat
 17272      }, {
 17273        name: 'background-fit',
 17274        type: t.bgFit
 17275      }, {
 17276        name: 'background-clip',
 17277        type: t.bgClip
 17278      }, {
 17279        name: 'background-width',
 17280        type: t.bgWH
 17281      }, {
 17282        name: 'background-height',
 17283        type: t.bgWH
 17284      }, {
 17285        name: 'background-offset-x',
 17286        type: t.bgPos
 17287      }, {
 17288        name: 'background-offset-y',
 17289        type: t.bgPos
 17290      }];
 17291      var compound = [{
 17292        name: 'position',
 17293        type: t.position,
 17294        triggersBounds: diff.any
 17295      }, {
 17296        name: 'compound-sizing-wrt-labels',
 17297        type: t.compoundIncludeLabels,
 17298        triggersBounds: diff.any
 17299      }, {
 17300        name: 'min-width',
 17301        type: t.size,
 17302        triggersBounds: diff.any
 17303      }, {
 17304        name: 'min-width-bias-left',
 17305        type: t.sizeMaybePercent,
 17306        triggersBounds: diff.any
 17307      }, {
 17308        name: 'min-width-bias-right',
 17309        type: t.sizeMaybePercent,
 17310        triggersBounds: diff.any
 17311      }, {
 17312        name: 'min-height',
 17313        type: t.size,
 17314        triggersBounds: diff.any
 17315      }, {
 17316        name: 'min-height-bias-top',
 17317        type: t.sizeMaybePercent,
 17318        triggersBounds: diff.any
 17319      }, {
 17320        name: 'min-height-bias-bottom',
 17321        type: t.sizeMaybePercent,
 17322        triggersBounds: diff.any
 17323      }];
 17324      var edgeLine = [{
 17325        name: 'line-style',
 17326        type: t.lineStyle
 17327      }, {
 17328        name: 'line-color',
 17329        type: t.color
 17330      }, {
 17331        name: 'line-fill',
 17332        type: t.fill
 17333      }, {
 17334        name: 'line-cap',
 17335        type: t.lineCap
 17336      }, {
 17337        name: 'line-dash-pattern',
 17338        type: t.numbers
 17339      }, {
 17340        name: 'line-dash-offset',
 17341        type: t.number
 17342      }, {
 17343        name: 'line-gradient-stop-colors',
 17344        type: t.colors
 17345      }, {
 17346        name: 'line-gradient-stop-positions',
 17347        type: t.percentages
 17348      }, {
 17349        name: 'curve-style',
 17350        type: t.curveStyle,
 17351        triggersBounds: diff.any,
 17352        triggersBoundsOfParallelBeziers: true
 17353      }, {
 17354        name: 'haystack-radius',
 17355        type: t.zeroOneNumber,
 17356        triggersBounds: diff.any
 17357      }, {
 17358        name: 'source-endpoint',
 17359        type: t.edgeEndpoint,
 17360        triggersBounds: diff.any
 17361      }, {
 17362        name: 'target-endpoint',
 17363        type: t.edgeEndpoint,
 17364        triggersBounds: diff.any
 17365      }, {
 17366        name: 'control-point-step-size',
 17367        type: t.size,
 17368        triggersBounds: diff.any
 17369      }, {
 17370        name: 'control-point-distances',
 17371        type: t.bidirectionalSizes,
 17372        triggersBounds: diff.any
 17373      }, {
 17374        name: 'control-point-weights',
 17375        type: t.numbers,
 17376        triggersBounds: diff.any
 17377      }, {
 17378        name: 'segment-distances',
 17379        type: t.bidirectionalSizes,
 17380        triggersBounds: diff.any
 17381      }, {
 17382        name: 'segment-weights',
 17383        type: t.numbers,
 17384        triggersBounds: diff.any
 17385      }, {
 17386        name: 'taxi-turn',
 17387        type: t.sizeMaybePercent,
 17388        triggersBounds: diff.any
 17389      }, {
 17390        name: 'taxi-turn-min-distance',
 17391        type: t.size,
 17392        triggersBounds: diff.any
 17393      }, {
 17394        name: 'taxi-direction',
 17395        type: t.axisDirection,
 17396        triggersBounds: diff.any
 17397      }, {
 17398        name: 'edge-distances',
 17399        type: t.edgeDistances,
 17400        triggersBounds: diff.any
 17401      }, {
 17402        name: 'arrow-scale',
 17403        type: t.positiveNumber,
 17404        triggersBounds: diff.any
 17405      }, {
 17406        name: 'loop-direction',
 17407        type: t.angle,
 17408        triggersBounds: diff.any
 17409      }, {
 17410        name: 'loop-sweep',
 17411        type: t.angle,
 17412        triggersBounds: diff.any
 17413      }, {
 17414        name: 'source-distance-from-node',
 17415        type: t.size,
 17416        triggersBounds: diff.any
 17417      }, {
 17418        name: 'target-distance-from-node',
 17419        type: t.size,
 17420        triggersBounds: diff.any
 17421      }];
 17422      var ghost = [{
 17423        name: 'ghost',
 17424        type: t.bool,
 17425        triggersBounds: diff.any
 17426      }, {
 17427        name: 'ghost-offset-x',
 17428        type: t.bidirectionalSize,
 17429        triggersBounds: diff.any
 17430      }, {
 17431        name: 'ghost-offset-y',
 17432        type: t.bidirectionalSize,
 17433        triggersBounds: diff.any
 17434      }, {
 17435        name: 'ghost-opacity',
 17436        type: t.zeroOneNumber
 17437      }];
 17438      var core = [{
 17439        name: 'selection-box-color',
 17440        type: t.color
 17441      }, {
 17442        name: 'selection-box-opacity',
 17443        type: t.zeroOneNumber
 17444      }, {
 17445        name: 'selection-box-border-color',
 17446        type: t.color
 17447      }, {
 17448        name: 'selection-box-border-width',
 17449        type: t.size
 17450      }, {
 17451        name: 'active-bg-color',
 17452        type: t.color
 17453      }, {
 17454        name: 'active-bg-opacity',
 17455        type: t.zeroOneNumber
 17456      }, {
 17457        name: 'active-bg-size',
 17458        type: t.size
 17459      }, {
 17460        name: 'outside-texture-bg-color',
 17461        type: t.color
 17462      }, {
 17463        name: 'outside-texture-bg-opacity',
 17464        type: t.zeroOneNumber
 17465      }]; // pie backgrounds for nodes
 17466  
 17467      var pie = [];
 17468      styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
 17469  
 17470      pie.push({
 17471        name: 'pie-size',
 17472        type: t.sizeMaybePercent
 17473      });
 17474  
 17475      for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
 17476        pie.push({
 17477          name: 'pie-' + i + '-background-color',
 17478          type: t.color
 17479        });
 17480        pie.push({
 17481          name: 'pie-' + i + '-background-size',
 17482          type: t.percent
 17483        });
 17484        pie.push({
 17485          name: 'pie-' + i + '-background-opacity',
 17486          type: t.zeroOneNumber
 17487        });
 17488      } // edge arrows
 17489  
 17490  
 17491      var edgeArrow = [];
 17492      var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
 17493      [{
 17494        name: 'arrow-shape',
 17495        type: t.arrowShape,
 17496        triggersBounds: diff.any
 17497      }, {
 17498        name: 'arrow-color',
 17499        type: t.color
 17500      }, {
 17501        name: 'arrow-fill',
 17502        type: t.arrowFill
 17503      }].forEach(function (prop) {
 17504        arrowPrefixes.forEach(function (prefix) {
 17505          var name = prefix + '-' + prop.name;
 17506          var type = prop.type,
 17507              triggersBounds = prop.triggersBounds;
 17508          edgeArrow.push({
 17509            name: name,
 17510            type: type,
 17511            triggersBounds: triggersBounds
 17512          });
 17513        });
 17514      }, {});
 17515      var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
 17516      var propGroups = styfn$6.propertyGroups = {
 17517        // common to all eles
 17518        behavior: behavior,
 17519        transition: transition,
 17520        visibility: visibility,
 17521        overlay: overlay,
 17522        ghost: ghost,
 17523        // labels
 17524        commonLabel: commonLabel,
 17525        labelDimensions: labelDimensions,
 17526        mainLabel: mainLabel,
 17527        sourceLabel: sourceLabel,
 17528        targetLabel: targetLabel,
 17529        // node props
 17530        nodeBody: nodeBody,
 17531        nodeBorder: nodeBorder,
 17532        backgroundImage: backgroundImage,
 17533        pie: pie,
 17534        compound: compound,
 17535        // edge props
 17536        edgeLine: edgeLine,
 17537        edgeArrow: edgeArrow,
 17538        core: core
 17539      };
 17540      var propGroupNames = styfn$6.propertyGroupNames = {};
 17541      var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
 17542      propGroupKeys.forEach(function (key) {
 17543        propGroupNames[key] = propGroups[key].map(function (prop) {
 17544          return prop.name;
 17545        });
 17546        propGroups[key].forEach(function (prop) {
 17547          return prop.groupKey = key;
 17548        });
 17549      }); // define aliases
 17550  
 17551      var aliases = styfn$6.aliases = [{
 17552        name: 'content',
 17553        pointsTo: 'label'
 17554      }, {
 17555        name: 'control-point-distance',
 17556        pointsTo: 'control-point-distances'
 17557      }, {
 17558        name: 'control-point-weight',
 17559        pointsTo: 'control-point-weights'
 17560      }, {
 17561        name: 'edge-text-rotation',
 17562        pointsTo: 'text-rotation'
 17563      }, {
 17564        name: 'padding-left',
 17565        pointsTo: 'padding'
 17566      }, {
 17567        name: 'padding-right',
 17568        pointsTo: 'padding'
 17569      }, {
 17570        name: 'padding-top',
 17571        pointsTo: 'padding'
 17572      }, {
 17573        name: 'padding-bottom',
 17574        pointsTo: 'padding'
 17575      }]; // list of property names
 17576  
 17577      styfn$6.propertyNames = props.map(function (p) {
 17578        return p.name;
 17579      }); // allow access of properties by name ( e.g. style.properties.height )
 17580  
 17581      for (var _i = 0; _i < props.length; _i++) {
 17582        var prop = props[_i];
 17583        props[prop.name] = prop; // allow lookup by name
 17584      } // map aliases
 17585  
 17586  
 17587      for (var _i2 = 0; _i2 < aliases.length; _i2++) {
 17588        var alias = aliases[_i2];
 17589        var pointsToProp = props[alias.pointsTo];
 17590        var aliasProp = {
 17591          name: alias.name,
 17592          alias: true,
 17593          pointsTo: pointsToProp
 17594        }; // add alias prop for parsing
 17595  
 17596        props.push(aliasProp);
 17597        props[alias.name] = aliasProp; // allow lookup by name
 17598      }
 17599    })();
 17600  
 17601    styfn$6.getDefaultProperty = function (name) {
 17602      return this.getDefaultProperties()[name];
 17603    };
 17604  
 17605    styfn$6.getDefaultProperties = function () {
 17606      var _p = this._private;
 17607  
 17608      if (_p.defaultProperties != null) {
 17609        return _p.defaultProperties;
 17610      }
 17611  
 17612      var rawProps = extend({
 17613        // core props
 17614        'selection-box-color': '#ddd',
 17615        'selection-box-opacity': 0.65,
 17616        'selection-box-border-color': '#aaa',
 17617        'selection-box-border-width': 1,
 17618        'active-bg-color': 'black',
 17619        'active-bg-opacity': 0.15,
 17620        'active-bg-size': 30,
 17621        'outside-texture-bg-color': '#000',
 17622        'outside-texture-bg-opacity': 0.125,
 17623        // common node/edge props
 17624        'events': 'yes',
 17625        'text-events': 'no',
 17626        'text-valign': 'top',
 17627        'text-halign': 'center',
 17628        'text-justification': 'auto',
 17629        'line-height': 1,
 17630        'color': '#000',
 17631        'text-outline-color': '#000',
 17632        'text-outline-width': 0,
 17633        'text-outline-opacity': 1,
 17634        'text-opacity': 1,
 17635        'text-decoration': 'none',
 17636        'text-transform': 'none',
 17637        'text-wrap': 'none',
 17638        'text-overflow-wrap': 'whitespace',
 17639        'text-max-width': 9999,
 17640        'text-background-color': '#000',
 17641        'text-background-opacity': 0,
 17642        'text-background-shape': 'rectangle',
 17643        'text-background-padding': 0,
 17644        'text-border-opacity': 0,
 17645        'text-border-width': 0,
 17646        'text-border-style': 'solid',
 17647        'text-border-color': '#000',
 17648        'font-family': 'Helvetica Neue, Helvetica, sans-serif',
 17649        'font-style': 'normal',
 17650        'font-weight': 'normal',
 17651        'font-size': 16,
 17652        'min-zoomed-font-size': 0,
 17653        'text-rotation': 'none',
 17654        'source-text-rotation': 'none',
 17655        'target-text-rotation': 'none',
 17656        'visibility': 'visible',
 17657        'display': 'element',
 17658        'opacity': 1,
 17659        'z-compound-depth': 'auto',
 17660        'z-index-compare': 'auto',
 17661        'z-index': 0,
 17662        'label': '',
 17663        'text-margin-x': 0,
 17664        'text-margin-y': 0,
 17665        'source-label': '',
 17666        'source-text-offset': 0,
 17667        'source-text-margin-x': 0,
 17668        'source-text-margin-y': 0,
 17669        'target-label': '',
 17670        'target-text-offset': 0,
 17671        'target-text-margin-x': 0,
 17672        'target-text-margin-y': 0,
 17673        'overlay-opacity': 0,
 17674        'overlay-color': '#000',
 17675        'overlay-padding': 10,
 17676        'transition-property': 'none',
 17677        'transition-duration': 0,
 17678        'transition-delay': 0,
 17679        'transition-timing-function': 'linear',
 17680        // node props
 17681        'background-blacken': 0,
 17682        'background-color': '#999',
 17683        'background-fill': 'solid',
 17684        'background-opacity': 1,
 17685        'background-image': 'none',
 17686        'background-image-crossorigin': 'anonymous',
 17687        'background-image-opacity': 1,
 17688        'background-position-x': '50%',
 17689        'background-position-y': '50%',
 17690        'background-offset-x': 0,
 17691        'background-offset-y': 0,
 17692        'background-width-relative-to': 'include-padding',
 17693        'background-height-relative-to': 'include-padding',
 17694        'background-repeat': 'no-repeat',
 17695        'background-fit': 'none',
 17696        'background-clip': 'node',
 17697        'background-width': 'auto',
 17698        'background-height': 'auto',
 17699        'border-color': '#000',
 17700        'border-opacity': 1,
 17701        'border-width': 0,
 17702        'border-style': 'solid',
 17703        'height': 30,
 17704        'width': 30,
 17705        'shape': 'ellipse',
 17706        'shape-polygon-points': '-1, -1,   1, -1,   1, 1,   -1, 1',
 17707        'bounds-expansion': 0,
 17708        // node gradient
 17709        'background-gradient-direction': 'to-bottom',
 17710        'background-gradient-stop-colors': '#999',
 17711        'background-gradient-stop-positions': '0%',
 17712        // ghost props
 17713        'ghost': 'no',
 17714        'ghost-offset-y': 0,
 17715        'ghost-offset-x': 0,
 17716        'ghost-opacity': 0,
 17717        // compound props
 17718        'padding': 0,
 17719        'padding-relative-to': 'width',
 17720        'position': 'origin',
 17721        'compound-sizing-wrt-labels': 'include',
 17722        'min-width': 0,
 17723        'min-width-bias-left': 0,
 17724        'min-width-bias-right': 0,
 17725        'min-height': 0,
 17726        'min-height-bias-top': 0,
 17727        'min-height-bias-bottom': 0
 17728      }, {
 17729        // node pie bg
 17730        'pie-size': '100%'
 17731      }, [{
 17732        name: 'pie-{{i}}-background-color',
 17733        value: 'black'
 17734      }, {
 17735        name: 'pie-{{i}}-background-size',
 17736        value: '0%'
 17737      }, {
 17738        name: 'pie-{{i}}-background-opacity',
 17739        value: 1
 17740      }].reduce(function (css, prop) {
 17741        for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
 17742          var name = prop.name.replace('{{i}}', i);
 17743          var val = prop.value;
 17744          css[name] = val;
 17745        }
 17746  
 17747        return css;
 17748      }, {}), {
 17749        // edge props
 17750        'line-style': 'solid',
 17751        'line-color': '#999',
 17752        'line-fill': 'solid',
 17753        'line-cap': 'butt',
 17754        'line-gradient-stop-colors': '#999',
 17755        'line-gradient-stop-positions': '0%',
 17756        'control-point-step-size': 40,
 17757        'control-point-weights': 0.5,
 17758        'segment-weights': 0.5,
 17759        'segment-distances': 20,
 17760        'taxi-turn': '50%',
 17761        'taxi-turn-min-distance': 10,
 17762        'taxi-direction': 'auto',
 17763        'edge-distances': 'intersection',
 17764        'curve-style': 'haystack',
 17765        'haystack-radius': 0,
 17766        'arrow-scale': 1,
 17767        'loop-direction': '-45deg',
 17768        'loop-sweep': '-90deg',
 17769        'source-distance-from-node': 0,
 17770        'target-distance-from-node': 0,
 17771        'source-endpoint': 'outside-to-node',
 17772        'target-endpoint': 'outside-to-node',
 17773        'line-dash-pattern': [6, 3],
 17774        'line-dash-offset': 0
 17775      }, [{
 17776        name: 'arrow-shape',
 17777        value: 'none'
 17778      }, {
 17779        name: 'arrow-color',
 17780        value: '#999'
 17781      }, {
 17782        name: 'arrow-fill',
 17783        value: 'filled'
 17784      }].reduce(function (css, prop) {
 17785        styfn$6.arrowPrefixes.forEach(function (prefix) {
 17786          var name = prefix + '-' + prop.name;
 17787          var val = prop.value;
 17788          css[name] = val;
 17789        });
 17790        return css;
 17791      }, {}));
 17792      var parsedProps = {};
 17793  
 17794      for (var i = 0; i < this.properties.length; i++) {
 17795        var prop = this.properties[i];
 17796  
 17797        if (prop.pointsTo) {
 17798          continue;
 17799        }
 17800  
 17801        var name = prop.name;
 17802        var val = rawProps[name];
 17803        var parsedProp = this.parse(name, val);
 17804        parsedProps[name] = parsedProp;
 17805      }
 17806  
 17807      _p.defaultProperties = parsedProps;
 17808      return _p.defaultProperties;
 17809    };
 17810  
 17811    styfn$6.addDefaultStylesheet = function () {
 17812      this.selector(':parent').css({
 17813        'shape': 'rectangle',
 17814        'padding': 10,
 17815        'background-color': '#eee',
 17816        'border-color': '#ccc',
 17817        'border-width': 1
 17818      }).selector('edge').css({
 17819        'width': 3
 17820      }).selector(':loop').css({
 17821        'curve-style': 'bezier'
 17822      }).selector('edge:compound').css({
 17823        'curve-style': 'bezier',
 17824        'source-endpoint': 'outside-to-line',
 17825        'target-endpoint': 'outside-to-line'
 17826      }).selector(':selected').css({
 17827        'background-color': '#0169D9',
 17828        'line-color': '#0169D9',
 17829        'source-arrow-color': '#0169D9',
 17830        'target-arrow-color': '#0169D9',
 17831        'mid-source-arrow-color': '#0169D9',
 17832        'mid-target-arrow-color': '#0169D9'
 17833      }).selector(':parent:selected').css({
 17834        'background-color': '#CCE1F9',
 17835        'border-color': '#aec8e5'
 17836      }).selector(':active').css({
 17837        'overlay-color': 'black',
 17838        'overlay-padding': 10,
 17839        'overlay-opacity': 0.25
 17840      });
 17841      this.defaultLength = this.length;
 17842    };
 17843  
 17844    var styfn$7 = {}; // a caching layer for property parsing
 17845  
 17846    styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
 17847      var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
 17848  
 17849      if (fn(value)) {
 17850        return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
 17851      }
 17852  
 17853      var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
 17854      var bypassKey = propIsBypass ? 't' : 'f';
 17855      var valueKey = '' + value;
 17856      var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
 17857      var propCache = self.propCache = self.propCache || [];
 17858      var ret;
 17859  
 17860      if (!(ret = propCache[argHash])) {
 17861        ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
 17862      } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
 17863      // - mappings can't be shared b/c mappings are per-element
 17864  
 17865  
 17866      if (propIsBypass || propIsFlat === 'mapping') {
 17867        // need a copy since props are mutated later in their lifecycles
 17868        ret = copy(ret);
 17869  
 17870        if (ret) {
 17871          ret.value = copy(ret.value); // because it could be an array, e.g. colour
 17872        }
 17873      }
 17874  
 17875      return ret;
 17876    };
 17877  
 17878    styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
 17879      var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
 17880  
 17881      if (!prop && value != null) {
 17882        warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
 17883      }
 17884  
 17885      return prop;
 17886    }; // parse a property; return null on invalid; return parsed property otherwise
 17887    // fields :
 17888    // - name : the name of the property
 17889    // - value : the parsed, native-typed value of the property
 17890    // - strValue : a string value that represents the property value in valid css
 17891    // - bypass : true iff the property is a bypass property
 17892  
 17893  
 17894    styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
 17895      var self = this;
 17896      name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
 17897  
 17898      var property = self.properties[name];
 17899      var passedValue = value;
 17900      var types = self.types;
 17901  
 17902      if (!property) {
 17903        return null;
 17904      } // return null on property of unknown name
 17905  
 17906  
 17907      if (value === undefined) {
 17908        return null;
 17909      } // can't assign undefined
 17910      // the property may be an alias
 17911  
 17912  
 17913      if (property.alias) {
 17914        property = property.pointsTo;
 17915        name = property.name;
 17916      }
 17917  
 17918      var valueIsString = string(value);
 17919  
 17920      if (valueIsString) {
 17921        // trim the value to make parsing easier
 17922        value = value.trim();
 17923      }
 17924  
 17925      var type = property.type;
 17926  
 17927      if (!type) {
 17928        return null;
 17929      } // no type, no luck
 17930      // check if bypass is null or empty string (i.e. indication to delete bypass property)
 17931  
 17932  
 17933      if (propIsBypass && (value === '' || value === null)) {
 17934        return {
 17935          name: name,
 17936          value: value,
 17937          bypass: true,
 17938          deleteBypass: true
 17939        };
 17940      } // check if value is a function used as a mapper
 17941  
 17942  
 17943      if (fn(value)) {
 17944        return {
 17945          name: name,
 17946          value: value,
 17947          strValue: 'fn',
 17948          mapped: types.fn,
 17949          bypass: propIsBypass
 17950        };
 17951      } // check if value is mapped
 17952  
 17953  
 17954      var data, mapData;
 17955  
 17956      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))) {
 17957        if (propIsBypass) {
 17958          return false;
 17959        } // mappers not allowed in bypass
 17960  
 17961  
 17962        var mapped = types.data;
 17963        return {
 17964          name: name,
 17965          value: data,
 17966          strValue: '' + value,
 17967          mapped: mapped,
 17968          field: data[1],
 17969          bypass: propIsBypass
 17970        };
 17971      } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
 17972        if (propIsBypass) {
 17973          return false;
 17974        } // mappers not allowed in bypass
 17975  
 17976  
 17977        if (type.multiple) {
 17978          return false;
 17979        } // impossible to map to num
 17980  
 17981  
 17982        var _mapped = types.mapData; // we can map only if the type is a colour or a number
 17983  
 17984        if (!(type.color || type.number)) {
 17985          return false;
 17986        }
 17987  
 17988        var valueMin = this.parse(name, mapData[4]); // parse to validate
 17989  
 17990        if (!valueMin || valueMin.mapped) {
 17991          return false;
 17992        } // can't be invalid or mapped
 17993  
 17994  
 17995        var valueMax = this.parse(name, mapData[5]); // parse to validate
 17996  
 17997        if (!valueMax || valueMax.mapped) {
 17998          return false;
 17999        } // can't be invalid or mapped
 18000        // check if valueMin and valueMax are the same
 18001  
 18002  
 18003        if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
 18004          warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
 18005          return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
 18006        } else if (type.color) {
 18007          var c1 = valueMin.value;
 18008          var c2 = valueMax.value;
 18009          var same = c1[0] === c2[0] // red
 18010          && c1[1] === c2[1] // green
 18011          && c1[2] === c2[2] // blue
 18012          && ( // optional alpha
 18013          c1[3] === c2[3] // same alpha outright
 18014          || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
 18015          c2[3] == null || c2[3] === 1) // full opacity for colour 2?
 18016          );
 18017  
 18018          if (same) {
 18019            return false;
 18020          } // can't make a mapper without a range
 18021  
 18022        }
 18023  
 18024        return {
 18025          name: name,
 18026          value: mapData,
 18027          strValue: '' + value,
 18028          mapped: _mapped,
 18029          field: mapData[1],
 18030          fieldMin: parseFloat(mapData[2]),
 18031          // min & max are numeric
 18032          fieldMax: parseFloat(mapData[3]),
 18033          valueMin: valueMin.value,
 18034          valueMax: valueMax.value,
 18035          bypass: propIsBypass
 18036        };
 18037      }
 18038  
 18039      if (type.multiple && propIsFlat !== 'multiple') {
 18040        var vals;
 18041  
 18042        if (valueIsString) {
 18043          vals = value.split(/\s+/);
 18044        } else if (array(value)) {
 18045          vals = value;
 18046        } else {
 18047          vals = [value];
 18048        }
 18049  
 18050        if (type.evenMultiple && vals.length % 2 !== 0) {
 18051          return null;
 18052        }
 18053  
 18054        var valArr = [];
 18055        var unitsArr = [];
 18056        var pfValArr = [];
 18057        var strVal = '';
 18058        var hasEnum = false;
 18059  
 18060        for (var i = 0; i < vals.length; i++) {
 18061          var p = self.parse(name, vals[i], propIsBypass, 'multiple');
 18062          hasEnum = hasEnum || string(p.value);
 18063          valArr.push(p.value);
 18064          pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
 18065          unitsArr.push(p.units);
 18066          strVal += (i > 0 ? ' ' : '') + p.strValue;
 18067        }
 18068  
 18069        if (type.validate && !type.validate(valArr, unitsArr)) {
 18070          return null;
 18071        }
 18072  
 18073        if (type.singleEnum && hasEnum) {
 18074          if (valArr.length === 1 && string(valArr[0])) {
 18075            return {
 18076              name: name,
 18077              value: valArr[0],
 18078              strValue: valArr[0],
 18079              bypass: propIsBypass
 18080            };
 18081          } else {
 18082            return null;
 18083          }
 18084        }
 18085  
 18086        return {
 18087          name: name,
 18088          value: valArr,
 18089          pfValue: pfValArr,
 18090          strValue: strVal,
 18091          bypass: propIsBypass,
 18092          units: unitsArr
 18093        };
 18094      } // several types also allow enums
 18095  
 18096  
 18097      var checkEnums = function checkEnums() {
 18098        for (var _i = 0; _i < type.enums.length; _i++) {
 18099          var en = type.enums[_i];
 18100  
 18101          if (en === value) {
 18102            return {
 18103              name: name,
 18104              value: value,
 18105              strValue: '' + value,
 18106              bypass: propIsBypass
 18107            };
 18108          }
 18109        }
 18110  
 18111        return null;
 18112      }; // check the type and return the appropriate object
 18113  
 18114  
 18115      if (type.number) {
 18116        var units;
 18117        var implicitUnits = 'px'; // not set => px
 18118  
 18119        if (type.units) {
 18120          // use specified units if set
 18121          units = type.units;
 18122        }
 18123  
 18124        if (type.implicitUnits) {
 18125          implicitUnits = type.implicitUnits;
 18126        }
 18127  
 18128        if (!type.unitless) {
 18129          if (valueIsString) {
 18130            var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
 18131  
 18132            if (units) {
 18133              unitsRegex = units;
 18134            } // only allow explicit units if so set
 18135  
 18136  
 18137            var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
 18138  
 18139            if (match) {
 18140              value = match[1];
 18141              units = match[2] || implicitUnits;
 18142            }
 18143          } else if (!units || type.implicitUnits) {
 18144            units = implicitUnits; // implicitly px if unspecified
 18145          }
 18146        }
 18147  
 18148        value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
 18149  
 18150        if (isNaN(value) && type.enums === undefined) {
 18151          return null;
 18152        } // check if this number type also accepts special keywords in place of numbers
 18153        // (i.e. `left`, `auto`, etc)
 18154  
 18155  
 18156        if (isNaN(value) && type.enums !== undefined) {
 18157          value = passedValue;
 18158          return checkEnums();
 18159        } // check if value must be an integer
 18160  
 18161  
 18162        if (type.integer && !integer(value)) {
 18163          return null;
 18164        } // check value is within range
 18165  
 18166  
 18167        if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
 18168          return null;
 18169        }
 18170  
 18171        var ret = {
 18172          name: name,
 18173          value: value,
 18174          strValue: '' + value + (units ? units : ''),
 18175          units: units,
 18176          bypass: propIsBypass
 18177        }; // normalise value in pixels
 18178  
 18179        if (type.unitless || units !== 'px' && units !== 'em') {
 18180          ret.pfValue = value;
 18181        } else {
 18182          ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
 18183        } // normalise value in ms
 18184  
 18185  
 18186        if (units === 'ms' || units === 's') {
 18187          ret.pfValue = units === 'ms' ? value : 1000 * value;
 18188        } // normalise value in rad
 18189  
 18190  
 18191        if (units === 'deg' || units === 'rad') {
 18192          ret.pfValue = units === 'rad' ? value : deg2rad(value);
 18193        } // normalize value in %
 18194  
 18195  
 18196        if (units === '%') {
 18197          ret.pfValue = value / 100;
 18198        }
 18199  
 18200        return ret;
 18201      } else if (type.propList) {
 18202        var props = [];
 18203        var propsStr = '' + value;
 18204  
 18205        if (propsStr === 'none') ; else {
 18206          // go over each prop
 18207          var propsSplit = propsStr.split(/\s*,\s*|\s+/);
 18208  
 18209          for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
 18210            var propName = propsSplit[_i2].trim();
 18211  
 18212            if (self.properties[propName]) {
 18213              props.push(propName);
 18214            } else {
 18215              warn('`' + propName + '` is not a valid property name');
 18216            }
 18217          }
 18218  
 18219          if (props.length === 0) {
 18220            return null;
 18221          }
 18222        }
 18223  
 18224        return {
 18225          name: name,
 18226          value: props,
 18227          strValue: props.length === 0 ? 'none' : props.join(' '),
 18228          bypass: propIsBypass
 18229        };
 18230      } else if (type.color) {
 18231        var tuple = color2tuple(value);
 18232  
 18233        if (!tuple) {
 18234          return null;
 18235        }
 18236  
 18237        return {
 18238          name: name,
 18239          value: tuple,
 18240          pfValue: tuple,
 18241          strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
 18242          // n.b. no spaces b/c of multiple support
 18243          bypass: propIsBypass
 18244        };
 18245      } else if (type.regex || type.regexes) {
 18246        // first check enums
 18247        if (type.enums) {
 18248          var enumProp = checkEnums();
 18249  
 18250          if (enumProp) {
 18251            return enumProp;
 18252          }
 18253        }
 18254  
 18255        var regexes = type.regexes ? type.regexes : [type.regex];
 18256  
 18257        for (var _i3 = 0; _i3 < regexes.length; _i3++) {
 18258          var regex = new RegExp(regexes[_i3]); // make a regex from the type string
 18259  
 18260          var m = regex.exec(value);
 18261  
 18262          if (m) {
 18263            // regex matches
 18264            return {
 18265              name: name,
 18266              value: type.singleRegexMatchValue ? m[1] : m,
 18267              strValue: '' + value,
 18268              bypass: propIsBypass
 18269            };
 18270          }
 18271        }
 18272  
 18273        return null; // didn't match any
 18274      } else if (type.string) {
 18275        // just return
 18276        return {
 18277          name: name,
 18278          value: '' + value,
 18279          strValue: '' + value,
 18280          bypass: propIsBypass
 18281        };
 18282      } else if (type.enums) {
 18283        // check enums last because it's a combo type in others
 18284        return checkEnums();
 18285      } else {
 18286        return null; // not a type we can handle
 18287      }
 18288    };
 18289  
 18290    var Style = function Style(cy) {
 18291      if (!(this instanceof Style)) {
 18292        return new Style(cy);
 18293      }
 18294  
 18295      if (!core(cy)) {
 18296        error('A style must have a core reference');
 18297        return;
 18298      }
 18299  
 18300      this._private = {
 18301        cy: cy,
 18302        coreStyle: {}
 18303      };
 18304      this.length = 0;
 18305      this.resetToDefault();
 18306    };
 18307  
 18308    var styfn$8 = Style.prototype;
 18309  
 18310    styfn$8.instanceString = function () {
 18311      return 'style';
 18312    }; // remove all contexts
 18313  
 18314  
 18315    styfn$8.clear = function () {
 18316      for (var i = 0; i < this.length; i++) {
 18317        this[i] = undefined;
 18318      }
 18319  
 18320      this.length = 0;
 18321      var _p = this._private;
 18322      _p.newStyle = true;
 18323      return this; // chaining
 18324    };
 18325  
 18326    styfn$8.resetToDefault = function () {
 18327      this.clear();
 18328      this.addDefaultStylesheet();
 18329      return this;
 18330    }; // builds a style object for the 'core' selector
 18331  
 18332  
 18333    styfn$8.core = function (propName) {
 18334      return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
 18335    }; // create a new context from the specified selector string and switch to that context
 18336  
 18337  
 18338    styfn$8.selector = function (selectorStr) {
 18339      // 'core' is a special case and does not need a selector
 18340      var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
 18341      var i = this.length++; // new context means new index
 18342  
 18343      this[i] = {
 18344        selector: selector,
 18345        properties: [],
 18346        mappedProperties: [],
 18347        index: i
 18348      };
 18349      return this; // chaining
 18350    }; // add one or many css rules to the current context
 18351  
 18352  
 18353    styfn$8.css = function () {
 18354      var self = this;
 18355      var args = arguments;
 18356  
 18357      if (args.length === 1) {
 18358        var map = args[0];
 18359  
 18360        for (var i = 0; i < self.properties.length; i++) {
 18361          var prop = self.properties[i];
 18362          var mapVal = map[prop.name];
 18363  
 18364          if (mapVal === undefined) {
 18365            mapVal = map[dash2camel(prop.name)];
 18366          }
 18367  
 18368          if (mapVal !== undefined) {
 18369            this.cssRule(prop.name, mapVal);
 18370          }
 18371        }
 18372      } else if (args.length === 2) {
 18373        this.cssRule(args[0], args[1]);
 18374      } // do nothing if args are invalid
 18375  
 18376  
 18377      return this; // chaining
 18378    };
 18379  
 18380    styfn$8.style = styfn$8.css; // add a single css rule to the current context
 18381  
 18382    styfn$8.cssRule = function (name, value) {
 18383      // name-value pair
 18384      var property = this.parse(name, value); // add property to current context if valid
 18385  
 18386      if (property) {
 18387        var i = this.length - 1;
 18388        this[i].properties.push(property);
 18389        this[i].properties[property.name] = property; // allow access by name as well
 18390  
 18391        if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
 18392          this._private.hasPie = true;
 18393        }
 18394  
 18395        if (property.mapped) {
 18396          this[i].mappedProperties.push(property);
 18397        } // add to core style if necessary
 18398  
 18399  
 18400        var currentSelectorIsCore = !this[i].selector;
 18401  
 18402        if (currentSelectorIsCore) {
 18403          this._private.coreStyle[property.name] = property;
 18404        }
 18405      }
 18406  
 18407      return this; // chaining
 18408    };
 18409  
 18410    styfn$8.append = function (style) {
 18411      if (stylesheet(style)) {
 18412        style.appendToStyle(this);
 18413      } else if (array(style)) {
 18414        this.appendFromJson(style);
 18415      } else if (string(style)) {
 18416        this.appendFromString(style);
 18417      } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
 18418  
 18419  
 18420      return this;
 18421    }; // static function
 18422  
 18423  
 18424    Style.fromJson = function (cy, json) {
 18425      var style = new Style(cy);
 18426      style.fromJson(json);
 18427      return style;
 18428    };
 18429  
 18430    Style.fromString = function (cy, string) {
 18431      return new Style(cy).fromString(string);
 18432    };
 18433  
 18434    [styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
 18435      extend(styfn$8, props);
 18436    });
 18437    Style.types = styfn$8.types;
 18438    Style.properties = styfn$8.properties;
 18439    Style.propertyGroups = styfn$8.propertyGroups;
 18440    Style.propertyGroupNames = styfn$8.propertyGroupNames;
 18441    Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
 18442  
 18443    var corefn$7 = {
 18444      style: function style(newStyle) {
 18445        if (newStyle) {
 18446          var s = this.setStyle(newStyle);
 18447          s.update();
 18448        }
 18449  
 18450        return this._private.style;
 18451      },
 18452      setStyle: function setStyle(style) {
 18453        var _p = this._private;
 18454  
 18455        if (stylesheet(style)) {
 18456          _p.style = style.generateStyle(this);
 18457        } else if (array(style)) {
 18458          _p.style = Style.fromJson(this, style);
 18459        } else if (string(style)) {
 18460          _p.style = Style.fromString(this, style);
 18461        } else {
 18462          _p.style = Style(this);
 18463        }
 18464  
 18465        return _p.style;
 18466      }
 18467    };
 18468  
 18469    var defaultSelectionType = 'single';
 18470    var corefn$8 = {
 18471      autolock: function autolock(bool) {
 18472        if (bool !== undefined) {
 18473          this._private.autolock = bool ? true : false;
 18474        } else {
 18475          return this._private.autolock;
 18476        }
 18477  
 18478        return this; // chaining
 18479      },
 18480      autoungrabify: function autoungrabify(bool) {
 18481        if (bool !== undefined) {
 18482          this._private.autoungrabify = bool ? true : false;
 18483        } else {
 18484          return this._private.autoungrabify;
 18485        }
 18486  
 18487        return this; // chaining
 18488      },
 18489      autounselectify: function autounselectify(bool) {
 18490        if (bool !== undefined) {
 18491          this._private.autounselectify = bool ? true : false;
 18492        } else {
 18493          return this._private.autounselectify;
 18494        }
 18495  
 18496        return this; // chaining
 18497      },
 18498      selectionType: function selectionType(selType) {
 18499        var _p = this._private;
 18500  
 18501        if (_p.selectionType == null) {
 18502          _p.selectionType = defaultSelectionType;
 18503        }
 18504  
 18505        if (selType !== undefined) {
 18506          if (selType === 'additive' || selType === 'single') {
 18507            _p.selectionType = selType;
 18508          }
 18509        } else {
 18510          return _p.selectionType;
 18511        }
 18512  
 18513        return this;
 18514      },
 18515      panningEnabled: function panningEnabled(bool) {
 18516        if (bool !== undefined) {
 18517          this._private.panningEnabled = bool ? true : false;
 18518        } else {
 18519          return this._private.panningEnabled;
 18520        }
 18521  
 18522        return this; // chaining
 18523      },
 18524      userPanningEnabled: function userPanningEnabled(bool) {
 18525        if (bool !== undefined) {
 18526          this._private.userPanningEnabled = bool ? true : false;
 18527        } else {
 18528          return this._private.userPanningEnabled;
 18529        }
 18530  
 18531        return this; // chaining
 18532      },
 18533      zoomingEnabled: function zoomingEnabled(bool) {
 18534        if (bool !== undefined) {
 18535          this._private.zoomingEnabled = bool ? true : false;
 18536        } else {
 18537          return this._private.zoomingEnabled;
 18538        }
 18539  
 18540        return this; // chaining
 18541      },
 18542      userZoomingEnabled: function userZoomingEnabled(bool) {
 18543        if (bool !== undefined) {
 18544          this._private.userZoomingEnabled = bool ? true : false;
 18545        } else {
 18546          return this._private.userZoomingEnabled;
 18547        }
 18548  
 18549        return this; // chaining
 18550      },
 18551      boxSelectionEnabled: function boxSelectionEnabled(bool) {
 18552        if (bool !== undefined) {
 18553          this._private.boxSelectionEnabled = bool ? true : false;
 18554        } else {
 18555          return this._private.boxSelectionEnabled;
 18556        }
 18557  
 18558        return this; // chaining
 18559      },
 18560      pan: function pan() {
 18561        var args = arguments;
 18562        var pan = this._private.pan;
 18563        var dim, val, dims, x, y;
 18564  
 18565        switch (args.length) {
 18566          case 0:
 18567            // .pan()
 18568            return pan;
 18569  
 18570          case 1:
 18571            if (string(args[0])) {
 18572              // .pan('x')
 18573              dim = args[0];
 18574              return pan[dim];
 18575            } else if (plainObject(args[0])) {
 18576              // .pan({ x: 0, y: 100 })
 18577              if (!this._private.panningEnabled) {
 18578                return this;
 18579              }
 18580  
 18581              dims = args[0];
 18582              x = dims.x;
 18583              y = dims.y;
 18584  
 18585              if (number(x)) {
 18586                pan.x = x;
 18587              }
 18588  
 18589              if (number(y)) {
 18590                pan.y = y;
 18591              }
 18592  
 18593              this.emit('pan viewport');
 18594            }
 18595  
 18596            break;
 18597  
 18598          case 2:
 18599            // .pan('x', 100)
 18600            if (!this._private.panningEnabled) {
 18601              return this;
 18602            }
 18603  
 18604            dim = args[0];
 18605            val = args[1];
 18606  
 18607            if ((dim === 'x' || dim === 'y') && number(val)) {
 18608              pan[dim] = val;
 18609            }
 18610  
 18611            this.emit('pan viewport');
 18612            break;
 18613          // invalid
 18614        }
 18615  
 18616        this.notify('viewport');
 18617        return this; // chaining
 18618      },
 18619      panBy: function panBy(arg0, arg1) {
 18620        var args = arguments;
 18621        var pan = this._private.pan;
 18622        var dim, val, dims, x, y;
 18623  
 18624        if (!this._private.panningEnabled) {
 18625          return this;
 18626        }
 18627  
 18628        switch (args.length) {
 18629          case 1:
 18630            if (plainObject(arg0)) {
 18631              // .panBy({ x: 0, y: 100 })
 18632              dims = args[0];
 18633              x = dims.x;
 18634              y = dims.y;
 18635  
 18636              if (number(x)) {
 18637                pan.x += x;
 18638              }
 18639  
 18640              if (number(y)) {
 18641                pan.y += y;
 18642              }
 18643  
 18644              this.emit('pan viewport');
 18645            }
 18646  
 18647            break;
 18648  
 18649          case 2:
 18650            // .panBy('x', 100)
 18651            dim = arg0;
 18652            val = arg1;
 18653  
 18654            if ((dim === 'x' || dim === 'y') && number(val)) {
 18655              pan[dim] += val;
 18656            }
 18657  
 18658            this.emit('pan viewport');
 18659            break;
 18660          // invalid
 18661        }
 18662  
 18663        this.notify('viewport');
 18664        return this; // chaining
 18665      },
 18666      fit: function fit(elements, padding) {
 18667        var viewportState = this.getFitViewport(elements, padding);
 18668  
 18669        if (viewportState) {
 18670          var _p = this._private;
 18671          _p.zoom = viewportState.zoom;
 18672          _p.pan = viewportState.pan;
 18673          this.emit('pan zoom viewport');
 18674          this.notify('viewport');
 18675        }
 18676  
 18677        return this; // chaining
 18678      },
 18679      getFitViewport: function getFitViewport(elements, padding) {
 18680        if (number(elements) && padding === undefined) {
 18681          // elements is optional
 18682          padding = elements;
 18683          elements = undefined;
 18684        }
 18685  
 18686        if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
 18687          return;
 18688        }
 18689  
 18690        var bb;
 18691  
 18692        if (string(elements)) {
 18693          var sel = elements;
 18694          elements = this.$(sel);
 18695        } else if (boundingBox(elements)) {
 18696          // assume bb
 18697          var bbe = elements;
 18698          bb = {
 18699            x1: bbe.x1,
 18700            y1: bbe.y1,
 18701            x2: bbe.x2,
 18702            y2: bbe.y2
 18703          };
 18704          bb.w = bb.x2 - bb.x1;
 18705          bb.h = bb.y2 - bb.y1;
 18706        } else if (!elementOrCollection(elements)) {
 18707          elements = this.mutableElements();
 18708        }
 18709  
 18710        if (elementOrCollection(elements) && elements.empty()) {
 18711          return;
 18712        } // can't fit to nothing
 18713  
 18714  
 18715        bb = bb || elements.boundingBox();
 18716        var w = this.width();
 18717        var h = this.height();
 18718        var zoom;
 18719        padding = number(padding) ? padding : 0;
 18720  
 18721        if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
 18722          zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
 18723  
 18724          zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
 18725          zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
 18726          var pan = {
 18727            // now pan to middle
 18728            x: (w - zoom * (bb.x1 + bb.x2)) / 2,
 18729            y: (h - zoom * (bb.y1 + bb.y2)) / 2
 18730          };
 18731          return {
 18732            zoom: zoom,
 18733            pan: pan
 18734          };
 18735        }
 18736  
 18737        return;
 18738      },
 18739      zoomRange: function zoomRange(min, max) {
 18740        var _p = this._private;
 18741  
 18742        if (max == null) {
 18743          var opts = min;
 18744          min = opts.min;
 18745          max = opts.max;
 18746        }
 18747  
 18748        if (number(min) && number(max) && min <= max) {
 18749          _p.minZoom = min;
 18750          _p.maxZoom = max;
 18751        } else if (number(min) && max === undefined && min <= _p.maxZoom) {
 18752          _p.minZoom = min;
 18753        } else if (number(max) && min === undefined && max >= _p.minZoom) {
 18754          _p.maxZoom = max;
 18755        }
 18756  
 18757        return this;
 18758      },
 18759      minZoom: function minZoom(zoom) {
 18760        if (zoom === undefined) {
 18761          return this._private.minZoom;
 18762        } else {
 18763          return this.zoomRange({
 18764            min: zoom
 18765          });
 18766        }
 18767      },
 18768      maxZoom: function maxZoom(zoom) {
 18769        if (zoom === undefined) {
 18770          return this._private.maxZoom;
 18771        } else {
 18772          return this.zoomRange({
 18773            max: zoom
 18774          });
 18775        }
 18776      },
 18777      getZoomedViewport: function getZoomedViewport(params) {
 18778        var _p = this._private;
 18779        var currentPan = _p.pan;
 18780        var currentZoom = _p.zoom;
 18781        var pos; // in rendered px
 18782  
 18783        var zoom;
 18784        var bail = false;
 18785  
 18786        if (!_p.zoomingEnabled) {
 18787          // zooming disabled
 18788          bail = true;
 18789        }
 18790  
 18791        if (number(params)) {
 18792          // then set the zoom
 18793          zoom = params;
 18794        } else if (plainObject(params)) {
 18795          // then zoom about a point
 18796          zoom = params.level;
 18797  
 18798          if (params.position != null) {
 18799            pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
 18800          } else if (params.renderedPosition != null) {
 18801            pos = params.renderedPosition;
 18802          }
 18803  
 18804          if (pos != null && !_p.panningEnabled) {
 18805            // panning disabled
 18806            bail = true;
 18807          }
 18808        } // crop zoom
 18809  
 18810  
 18811        zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
 18812        zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
 18813  
 18814        if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
 18815          return null;
 18816        }
 18817  
 18818        if (pos != null) {
 18819          // set zoom about position
 18820          var pan1 = currentPan;
 18821          var zoom1 = currentZoom;
 18822          var zoom2 = zoom;
 18823          var pan2 = {
 18824            x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
 18825            y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
 18826          };
 18827          return {
 18828            zoomed: true,
 18829            panned: true,
 18830            zoom: zoom2,
 18831            pan: pan2
 18832          };
 18833        } else {
 18834          // just set the zoom
 18835          return {
 18836            zoomed: true,
 18837            panned: false,
 18838            zoom: zoom,
 18839            pan: currentPan
 18840          };
 18841        }
 18842      },
 18843      zoom: function zoom(params) {
 18844        if (params === undefined) {
 18845          // get
 18846          return this._private.zoom;
 18847        } else {
 18848          // set
 18849          var vp = this.getZoomedViewport(params);
 18850          var _p = this._private;
 18851  
 18852          if (vp == null || !vp.zoomed) {
 18853            return this;
 18854          }
 18855  
 18856          _p.zoom = vp.zoom;
 18857  
 18858          if (vp.panned) {
 18859            _p.pan.x = vp.pan.x;
 18860            _p.pan.y = vp.pan.y;
 18861          }
 18862  
 18863          this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
 18864          this.notify('viewport');
 18865          return this; // chaining
 18866        }
 18867      },
 18868      viewport: function viewport(opts) {
 18869        var _p = this._private;
 18870        var zoomDefd = true;
 18871        var panDefd = true;
 18872        var events = []; // to trigger
 18873  
 18874        var zoomFailed = false;
 18875        var panFailed = false;
 18876  
 18877        if (!opts) {
 18878          return this;
 18879        }
 18880  
 18881        if (!number(opts.zoom)) {
 18882          zoomDefd = false;
 18883        }
 18884  
 18885        if (!plainObject(opts.pan)) {
 18886          panDefd = false;
 18887        }
 18888  
 18889        if (!zoomDefd && !panDefd) {
 18890          return this;
 18891        }
 18892  
 18893        if (zoomDefd) {
 18894          var z = opts.zoom;
 18895  
 18896          if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
 18897            zoomFailed = true;
 18898          } else {
 18899            _p.zoom = z;
 18900            events.push('zoom');
 18901          }
 18902        }
 18903  
 18904        if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
 18905          var p = opts.pan;
 18906  
 18907          if (number(p.x)) {
 18908            _p.pan.x = p.x;
 18909            panFailed = false;
 18910          }
 18911  
 18912          if (number(p.y)) {
 18913            _p.pan.y = p.y;
 18914            panFailed = false;
 18915          }
 18916  
 18917          if (!panFailed) {
 18918            events.push('pan');
 18919          }
 18920        }
 18921  
 18922        if (events.length > 0) {
 18923          events.push('viewport');
 18924          this.emit(events.join(' '));
 18925          this.notify('viewport');
 18926        }
 18927  
 18928        return this; // chaining
 18929      },
 18930      center: function center(elements) {
 18931        var pan = this.getCenterPan(elements);
 18932  
 18933        if (pan) {
 18934          this._private.pan = pan;
 18935          this.emit('pan viewport');
 18936          this.notify('viewport');
 18937        }
 18938  
 18939        return this; // chaining
 18940      },
 18941      getCenterPan: function getCenterPan(elements, zoom) {
 18942        if (!this._private.panningEnabled) {
 18943          return;
 18944        }
 18945  
 18946        if (string(elements)) {
 18947          var selector = elements;
 18948          elements = this.mutableElements().filter(selector);
 18949        } else if (!elementOrCollection(elements)) {
 18950          elements = this.mutableElements();
 18951        }
 18952  
 18953        if (elements.length === 0) {
 18954          return;
 18955        } // can't centre pan to nothing
 18956  
 18957  
 18958        var bb = elements.boundingBox();
 18959        var w = this.width();
 18960        var h = this.height();
 18961        zoom = zoom === undefined ? this._private.zoom : zoom;
 18962        var pan = {
 18963          // middle
 18964          x: (w - zoom * (bb.x1 + bb.x2)) / 2,
 18965          y: (h - zoom * (bb.y1 + bb.y2)) / 2
 18966        };
 18967        return pan;
 18968      },
 18969      reset: function reset() {
 18970        if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
 18971          return this;
 18972        }
 18973  
 18974        this.viewport({
 18975          pan: {
 18976            x: 0,
 18977            y: 0
 18978          },
 18979          zoom: 1
 18980        });
 18981        return this; // chaining
 18982      },
 18983      invalidateSize: function invalidateSize() {
 18984        this._private.sizeCache = null;
 18985      },
 18986      size: function size() {
 18987        var _p = this._private;
 18988        var container = _p.container;
 18989        return _p.sizeCache = _p.sizeCache || (container ? function () {
 18990          var style = window$1.getComputedStyle(container);
 18991  
 18992          var val = function val(name) {
 18993            return parseFloat(style.getPropertyValue(name));
 18994          };
 18995  
 18996          return {
 18997            width: container.clientWidth - val('padding-left') - val('padding-right'),
 18998            height: container.clientHeight - val('padding-top') - val('padding-bottom')
 18999          };
 19000        }() : {
 19001          // fallback if no container (not 0 b/c can be used for dividing etc)
 19002          width: 1,
 19003          height: 1
 19004        });
 19005      },
 19006      width: function width() {
 19007        return this.size().width;
 19008      },
 19009      height: function height() {
 19010        return this.size().height;
 19011      },
 19012      extent: function extent() {
 19013        var pan = this._private.pan;
 19014        var zoom = this._private.zoom;
 19015        var rb = this.renderedExtent();
 19016        var b = {
 19017          x1: (rb.x1 - pan.x) / zoom,
 19018          x2: (rb.x2 - pan.x) / zoom,
 19019          y1: (rb.y1 - pan.y) / zoom,
 19020          y2: (rb.y2 - pan.y) / zoom
 19021        };
 19022        b.w = b.x2 - b.x1;
 19023        b.h = b.y2 - b.y1;
 19024        return b;
 19025      },
 19026      renderedExtent: function renderedExtent() {
 19027        var width = this.width();
 19028        var height = this.height();
 19029        return {
 19030          x1: 0,
 19031          y1: 0,
 19032          x2: width,
 19033          y2: height,
 19034          w: width,
 19035          h: height
 19036        };
 19037      }
 19038    }; // aliases
 19039  
 19040    corefn$8.centre = corefn$8.center; // backwards compatibility
 19041  
 19042    corefn$8.autolockNodes = corefn$8.autolock;
 19043    corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
 19044  
 19045    var fn$6 = {
 19046      data: define$3.data({
 19047        field: 'data',
 19048        bindingEvent: 'data',
 19049        allowBinding: true,
 19050        allowSetting: true,
 19051        settingEvent: 'data',
 19052        settingTriggersEvent: true,
 19053        triggerFnName: 'trigger',
 19054        allowGetting: true
 19055      }),
 19056      removeData: define$3.removeData({
 19057        field: 'data',
 19058        event: 'data',
 19059        triggerFnName: 'trigger',
 19060        triggerEvent: true
 19061      }),
 19062      scratch: define$3.data({
 19063        field: 'scratch',
 19064        bindingEvent: 'scratch',
 19065        allowBinding: true,
 19066        allowSetting: true,
 19067        settingEvent: 'scratch',
 19068        settingTriggersEvent: true,
 19069        triggerFnName: 'trigger',
 19070        allowGetting: true
 19071      }),
 19072      removeScratch: define$3.removeData({
 19073        field: 'scratch',
 19074        event: 'scratch',
 19075        triggerFnName: 'trigger',
 19076        triggerEvent: true
 19077      })
 19078    }; // aliases
 19079  
 19080    fn$6.attr = fn$6.data;
 19081    fn$6.removeAttr = fn$6.removeData;
 19082  
 19083    var Core = function Core(opts) {
 19084      var cy = this;
 19085      opts = extend({}, opts);
 19086      var container = opts.container; // allow for passing a wrapped jquery object
 19087      // e.g. cytoscape({ container: $('#cy') })
 19088  
 19089      if (container && !htmlElement(container) && htmlElement(container[0])) {
 19090        container = container[0];
 19091      }
 19092  
 19093      var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
 19094  
 19095      reg = reg || {};
 19096  
 19097      if (reg && reg.cy) {
 19098        reg.cy.destroy();
 19099        reg = {}; // old instance => replace reg completely
 19100      }
 19101  
 19102      var readies = reg.readies = reg.readies || [];
 19103  
 19104      if (container) {
 19105        container._cyreg = reg;
 19106      } // make sure container assoc'd reg points to this cy
 19107  
 19108  
 19109      reg.cy = cy;
 19110      var head = window$1 !== undefined && container !== undefined && !opts.headless;
 19111      var options = opts;
 19112      options.layout = extend({
 19113        name: head ? 'grid' : 'null'
 19114      }, options.layout);
 19115      options.renderer = extend({
 19116        name: head ? 'canvas' : 'null'
 19117      }, options.renderer);
 19118  
 19119      var defVal = function defVal(def, val, altVal) {
 19120        if (val !== undefined) {
 19121          return val;
 19122        } else if (altVal !== undefined) {
 19123          return altVal;
 19124        } else {
 19125          return def;
 19126        }
 19127      };
 19128  
 19129      var _p = this._private = {
 19130        container: container,
 19131        // html dom ele container
 19132        ready: false,
 19133        // whether ready has been triggered
 19134        options: options,
 19135        // cached options
 19136        elements: new Collection(this),
 19137        // elements in the graph
 19138        listeners: [],
 19139        // list of listeners
 19140        aniEles: new Collection(this),
 19141        // elements being animated
 19142        data: {},
 19143        // data for the core
 19144        scratch: {},
 19145        // scratch object for core
 19146        layout: null,
 19147        renderer: null,
 19148        destroyed: false,
 19149        // whether destroy was called
 19150        notificationsEnabled: true,
 19151        // whether notifications are sent to the renderer
 19152        minZoom: 1e-50,
 19153        maxZoom: 1e50,
 19154        zoomingEnabled: defVal(true, options.zoomingEnabled),
 19155        userZoomingEnabled: defVal(true, options.userZoomingEnabled),
 19156        panningEnabled: defVal(true, options.panningEnabled),
 19157        userPanningEnabled: defVal(true, options.userPanningEnabled),
 19158        boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
 19159        autolock: defVal(false, options.autolock, options.autolockNodes),
 19160        autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
 19161        autounselectify: defVal(false, options.autounselectify),
 19162        styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
 19163        zoom: number(options.zoom) ? options.zoom : 1,
 19164        pan: {
 19165          x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
 19166          y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
 19167        },
 19168        animation: {
 19169          // object for currently-running animations
 19170          current: [],
 19171          queue: []
 19172        },
 19173        hasCompoundNodes: false
 19174      };
 19175  
 19176      this.createEmitter(); // set selection type
 19177  
 19178      this.selectionType(options.selectionType); // init zoom bounds
 19179  
 19180      this.zoomRange({
 19181        min: options.minZoom,
 19182        max: options.maxZoom
 19183      });
 19184  
 19185      var loadExtData = function loadExtData(extData, next) {
 19186        var anyIsPromise = extData.some(promise);
 19187  
 19188        if (anyIsPromise) {
 19189          return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
 19190        } else {
 19191          next(extData); // exec synchronously for convenience
 19192        }
 19193      }; // start with the default stylesheet so we have something before loading an external stylesheet
 19194  
 19195  
 19196      if (_p.styleEnabled) {
 19197        cy.setStyle([]);
 19198      } // create the renderer
 19199  
 19200  
 19201      var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
 19202  
 19203      cy.initRenderer(rendererOptions);
 19204  
 19205      var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
 19206        cy.notifications(false); // remove old elements
 19207  
 19208        var oldEles = cy.mutableElements();
 19209  
 19210        if (oldEles.length > 0) {
 19211          oldEles.remove();
 19212        }
 19213  
 19214        if (elements != null) {
 19215          if (plainObject(elements) || array(elements)) {
 19216            cy.add(elements);
 19217          }
 19218        }
 19219  
 19220        cy.one('layoutready', function (e) {
 19221          cy.notifications(true);
 19222          cy.emit(e); // we missed this event by turning notifications off, so pass it on
 19223  
 19224          cy.one('load', onload);
 19225          cy.emitAndNotify('load');
 19226        }).one('layoutstop', function () {
 19227          cy.one('done', ondone);
 19228          cy.emit('done');
 19229        });
 19230        var layoutOpts = extend({}, cy._private.options.layout);
 19231        layoutOpts.eles = cy.elements();
 19232        cy.layout(layoutOpts).run();
 19233      };
 19234  
 19235      loadExtData([options.style, options.elements], function (thens) {
 19236        var initStyle = thens[0];
 19237        var initEles = thens[1]; // init style
 19238  
 19239        if (_p.styleEnabled) {
 19240          cy.style().append(initStyle);
 19241        } // initial load
 19242  
 19243  
 19244        setElesAndLayout(initEles, function () {
 19245          // onready
 19246          cy.startAnimationLoop();
 19247          _p.ready = true; // if a ready callback is specified as an option, the bind it
 19248  
 19249          if (fn(options.ready)) {
 19250            cy.on('ready', options.ready);
 19251          } // bind all the ready handlers registered before creating this instance
 19252  
 19253  
 19254          for (var i = 0; i < readies.length; i++) {
 19255            var fn$1 = readies[i];
 19256            cy.on('ready', fn$1);
 19257          }
 19258  
 19259          if (reg) {
 19260            reg.readies = [];
 19261          } // 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
 19262  
 19263  
 19264          cy.emit('ready');
 19265        }, options.done);
 19266      });
 19267    };
 19268  
 19269    var corefn$9 = Core.prototype; // short alias
 19270  
 19271    extend(corefn$9, {
 19272      instanceString: function instanceString() {
 19273        return 'core';
 19274      },
 19275      isReady: function isReady() {
 19276        return this._private.ready;
 19277      },
 19278      destroyed: function destroyed() {
 19279        return this._private.destroyed;
 19280      },
 19281      ready: function ready(fn) {
 19282        if (this.isReady()) {
 19283          this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
 19284        } else {
 19285          this.on('ready', fn);
 19286        }
 19287  
 19288        return this;
 19289      },
 19290      destroy: function destroy() {
 19291        var cy = this;
 19292        if (cy.destroyed()) return;
 19293        cy.stopAnimationLoop();
 19294        cy.destroyRenderer();
 19295        this.emit('destroy');
 19296        cy._private.destroyed = true;
 19297        return cy;
 19298      },
 19299      hasElementWithId: function hasElementWithId(id) {
 19300        return this._private.elements.hasElementWithId(id);
 19301      },
 19302      getElementById: function getElementById(id) {
 19303        return this._private.elements.getElementById(id);
 19304      },
 19305      hasCompoundNodes: function hasCompoundNodes() {
 19306        return this._private.hasCompoundNodes;
 19307      },
 19308      headless: function headless() {
 19309        return this._private.renderer.isHeadless();
 19310      },
 19311      styleEnabled: function styleEnabled() {
 19312        return this._private.styleEnabled;
 19313      },
 19314      addToPool: function addToPool(eles) {
 19315        this._private.elements.merge(eles);
 19316  
 19317        return this; // chaining
 19318      },
 19319      removeFromPool: function removeFromPool(eles) {
 19320        this._private.elements.unmerge(eles);
 19321  
 19322        return this;
 19323      },
 19324      container: function container() {
 19325        return this._private.container || null;
 19326      },
 19327      mount: function mount(container) {
 19328        if (container == null) {
 19329          return;
 19330        }
 19331  
 19332        var cy = this;
 19333        var _p = cy._private;
 19334        var options = _p.options;
 19335  
 19336        if (!htmlElement(container) && htmlElement(container[0])) {
 19337          container = container[0];
 19338        }
 19339  
 19340        cy.stopAnimationLoop();
 19341        cy.destroyRenderer();
 19342        _p.container = container;
 19343        _p.styleEnabled = true;
 19344        cy.invalidateSize();
 19345        cy.initRenderer(extend({}, options, options.renderer, {
 19346          // allow custom renderer name to be re-used, otherwise use canvas
 19347          name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
 19348        }));
 19349        cy.startAnimationLoop();
 19350        cy.style(options.style);
 19351        cy.emit('mount');
 19352        return cy;
 19353      },
 19354      unmount: function unmount() {
 19355        var cy = this;
 19356        cy.stopAnimationLoop();
 19357        cy.destroyRenderer();
 19358        cy.initRenderer({
 19359          name: 'null'
 19360        });
 19361        cy.emit('unmount');
 19362        return cy;
 19363      },
 19364      options: function options() {
 19365        return copy(this._private.options);
 19366      },
 19367      json: function json(obj) {
 19368        var cy = this;
 19369        var _p = cy._private;
 19370        var eles = cy.mutableElements();
 19371  
 19372        var getFreshRef = function getFreshRef(ele) {
 19373          return cy.getElementById(ele.id());
 19374        };
 19375  
 19376        if (plainObject(obj)) {
 19377          // set
 19378          cy.startBatch();
 19379  
 19380          if (obj.elements) {
 19381            var idInJson = {};
 19382  
 19383            var updateEles = function updateEles(jsons, gr) {
 19384              var toAdd = [];
 19385              var toMod = [];
 19386  
 19387              for (var i = 0; i < jsons.length; i++) {
 19388                var json = jsons[i];
 19389                var id = '' + json.data.id; // id must be string
 19390  
 19391                var ele = cy.getElementById(id);
 19392                idInJson[id] = true;
 19393  
 19394                if (ele.length !== 0) {
 19395                  // existing element should be updated
 19396                  toMod.push({
 19397                    ele: ele,
 19398                    json: json
 19399                  });
 19400                } else {
 19401                  // otherwise should be added
 19402                  if (gr) {
 19403                    json.group = gr;
 19404                    toAdd.push(json);
 19405                  } else {
 19406                    toAdd.push(json);
 19407                  }
 19408                }
 19409              }
 19410  
 19411              cy.add(toAdd);
 19412  
 19413              for (var _i = 0; _i < toMod.length; _i++) {
 19414                var _toMod$_i = toMod[_i],
 19415                    _ele = _toMod$_i.ele,
 19416                    _json = _toMod$_i.json;
 19417  
 19418                _ele.json(_json);
 19419              }
 19420            };
 19421  
 19422            if (array(obj.elements)) {
 19423              // elements: []
 19424              updateEles(obj.elements);
 19425            } else {
 19426              // elements: { nodes: [], edges: [] }
 19427              var grs = ['nodes', 'edges'];
 19428  
 19429              for (var i = 0; i < grs.length; i++) {
 19430                var gr = grs[i];
 19431                var elements = obj.elements[gr];
 19432  
 19433                if (array(elements)) {
 19434                  updateEles(elements, gr);
 19435                }
 19436              }
 19437            }
 19438  
 19439            var parentsToRemove = cy.collection();
 19440            eles.filter(function (ele) {
 19441              return !idInJson[ele.id()];
 19442            }).forEach(function (ele) {
 19443              if (ele.isParent()) {
 19444                parentsToRemove.merge(ele);
 19445              } else {
 19446                ele.remove();
 19447              }
 19448            }); // so that children are not removed w/parent
 19449  
 19450            parentsToRemove.forEach(function (ele) {
 19451              return ele.children().move({
 19452                parent: null
 19453              });
 19454            }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
 19455  
 19456            parentsToRemove.forEach(function (ele) {
 19457              return getFreshRef(ele).remove();
 19458            });
 19459          }
 19460  
 19461          if (obj.style) {
 19462            cy.style(obj.style);
 19463          }
 19464  
 19465          if (obj.zoom != null && obj.zoom !== _p.zoom) {
 19466            cy.zoom(obj.zoom);
 19467          }
 19468  
 19469          if (obj.pan) {
 19470            if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
 19471              cy.pan(obj.pan);
 19472            }
 19473          }
 19474  
 19475          if (obj.data) {
 19476            cy.data(obj.data);
 19477          }
 19478  
 19479          var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify'];
 19480  
 19481          for (var _i2 = 0; _i2 < fields.length; _i2++) {
 19482            var f = fields[_i2];
 19483  
 19484            if (obj[f] != null) {
 19485              cy[f](obj[f]);
 19486            }
 19487          }
 19488  
 19489          cy.endBatch();
 19490          return this; // chaining
 19491        } else {
 19492          // get
 19493          var flat = !!obj;
 19494          var json = {};
 19495  
 19496          if (flat) {
 19497            json.elements = this.elements().map(function (ele) {
 19498              return ele.json();
 19499            });
 19500          } else {
 19501            json.elements = {};
 19502            eles.forEach(function (ele) {
 19503              var group = ele.group();
 19504  
 19505              if (!json.elements[group]) {
 19506                json.elements[group] = [];
 19507              }
 19508  
 19509              json.elements[group].push(ele.json());
 19510            });
 19511          }
 19512  
 19513          if (this._private.styleEnabled) {
 19514            json.style = cy.style().json();
 19515          }
 19516  
 19517          json.data = copy(cy.data());
 19518          var options = _p.options;
 19519          json.zoomingEnabled = _p.zoomingEnabled;
 19520          json.userZoomingEnabled = _p.userZoomingEnabled;
 19521          json.zoom = _p.zoom;
 19522          json.minZoom = _p.minZoom;
 19523          json.maxZoom = _p.maxZoom;
 19524          json.panningEnabled = _p.panningEnabled;
 19525          json.userPanningEnabled = _p.userPanningEnabled;
 19526          json.pan = copy(_p.pan);
 19527          json.boxSelectionEnabled = _p.boxSelectionEnabled;
 19528          json.renderer = copy(options.renderer);
 19529          json.hideEdgesOnViewport = options.hideEdgesOnViewport;
 19530          json.textureOnViewport = options.textureOnViewport;
 19531          json.wheelSensitivity = options.wheelSensitivity;
 19532          json.motionBlur = options.motionBlur;
 19533          return json;
 19534        }
 19535      }
 19536    });
 19537    corefn$9.$id = corefn$9.getElementById;
 19538    [corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
 19539      extend(corefn$9, props);
 19540    });
 19541  
 19542    /* eslint-disable no-unused-vars */
 19543  
 19544    var defaults$9 = {
 19545      fit: true,
 19546      // whether to fit the viewport to the graph
 19547      directed: false,
 19548      // whether the tree is directed downwards (or edges can point in any direction if false)
 19549      padding: 30,
 19550      // padding on fit
 19551      circle: false,
 19552      // put depths in concentric circles if true, put depths top down if false
 19553      grid: false,
 19554      // whether to create an even grid into which the DAG is placed (circle:false only)
 19555      spacingFactor: 1.75,
 19556      // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
 19557      boundingBox: undefined,
 19558      // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 19559      avoidOverlap: true,
 19560      // prevents node overlap, may overflow boundingBox if not enough space
 19561      nodeDimensionsIncludeLabels: false,
 19562      // Excludes the label when calculating node bounding boxes for the layout algorithm
 19563      roots: undefined,
 19564      // the roots of the trees
 19565      maximal: false,
 19566      // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
 19567      animate: false,
 19568      // whether to transition the node positions
 19569      animationDuration: 500,
 19570      // duration of animation in ms if enabled
 19571      animationEasing: undefined,
 19572      // easing of animation if enabled,
 19573      animateFilter: function animateFilter(node, i) {
 19574        return true;
 19575      },
 19576      // 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
 19577      ready: undefined,
 19578      // callback on layoutready
 19579      stop: undefined,
 19580      // callback on layoutstop
 19581      transform: function transform(node, position) {
 19582        return position;
 19583      } // transform a given node position. Useful for changing flow direction in discrete layouts
 19584  
 19585    };
 19586    /* eslint-enable */
 19587  
 19588    var getInfo = function getInfo(ele) {
 19589      return ele.scratch('breadthfirst');
 19590    };
 19591  
 19592    var setInfo = function setInfo(ele, obj) {
 19593      return ele.scratch('breadthfirst', obj);
 19594    };
 19595  
 19596    function BreadthFirstLayout(options) {
 19597      this.options = extend({}, defaults$9, options);
 19598    }
 19599  
 19600    BreadthFirstLayout.prototype.run = function () {
 19601      var params = this.options;
 19602      var options = params;
 19603      var cy = params.cy;
 19604      var eles = options.eles;
 19605      var nodes = eles.nodes().filter(function (n) {
 19606        return !n.isParent();
 19607      });
 19608      var graph = eles;
 19609      var directed = options.directed;
 19610      var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
 19611  
 19612      var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
 19613        x1: 0,
 19614        y1: 0,
 19615        w: cy.width(),
 19616        h: cy.height()
 19617      });
 19618      var roots;
 19619  
 19620      if (elementOrCollection(options.roots)) {
 19621        roots = options.roots;
 19622      } else if (array(options.roots)) {
 19623        var rootsArray = [];
 19624  
 19625        for (var i = 0; i < options.roots.length; i++) {
 19626          var id = options.roots[i];
 19627          var ele = cy.getElementById(id);
 19628          rootsArray.push(ele);
 19629        }
 19630  
 19631        roots = cy.collection(rootsArray);
 19632      } else if (string(options.roots)) {
 19633        roots = cy.$(options.roots);
 19634      } else {
 19635        if (directed) {
 19636          roots = nodes.roots();
 19637        } else {
 19638          var components = eles.components();
 19639          roots = cy.collection();
 19640  
 19641          var _loop = function _loop(_i) {
 19642            var comp = components[_i];
 19643            var maxDegree = comp.maxDegree(false);
 19644            var compRoots = comp.filter(function (ele) {
 19645              return ele.degree(false) === maxDegree;
 19646            });
 19647            roots = roots.add(compRoots);
 19648          };
 19649  
 19650          for (var _i = 0; _i < components.length; _i++) {
 19651            _loop(_i);
 19652          }
 19653        }
 19654      }
 19655  
 19656      var depths = [];
 19657      var foundByBfs = {};
 19658  
 19659      var addToDepth = function addToDepth(ele, d) {
 19660        if (depths[d] == null) {
 19661          depths[d] = [];
 19662        }
 19663  
 19664        var i = depths[d].length;
 19665        depths[d].push(ele);
 19666        setInfo(ele, {
 19667          index: i,
 19668          depth: d
 19669        });
 19670      };
 19671  
 19672      var changeDepth = function changeDepth(ele, newDepth) {
 19673        var _getInfo = getInfo(ele),
 19674            depth = _getInfo.depth,
 19675            index = _getInfo.index;
 19676  
 19677        depths[depth][index] = null;
 19678        addToDepth(ele, newDepth);
 19679      }; // find the depths of the nodes
 19680  
 19681  
 19682      graph.bfs({
 19683        roots: roots,
 19684        directed: options.directed,
 19685        visit: function visit(node, edge, pNode, i, depth) {
 19686          var ele = node[0];
 19687          var id = ele.id();
 19688          addToDepth(ele, depth);
 19689          foundByBfs[id] = true;
 19690        }
 19691      }); // check for nodes not found by bfs
 19692  
 19693      var orphanNodes = [];
 19694  
 19695      for (var _i2 = 0; _i2 < nodes.length; _i2++) {
 19696        var _ele = nodes[_i2];
 19697  
 19698        if (foundByBfs[_ele.id()]) {
 19699          continue;
 19700        } else {
 19701          orphanNodes.push(_ele);
 19702        }
 19703      } // assign the nodes a depth and index
 19704  
 19705  
 19706      var assignDepthsAt = function assignDepthsAt(i) {
 19707        var eles = depths[i];
 19708  
 19709        for (var j = 0; j < eles.length; j++) {
 19710          var _ele2 = eles[j];
 19711  
 19712          if (_ele2 == null) {
 19713            eles.splice(j, 1);
 19714            j--;
 19715            continue;
 19716          }
 19717  
 19718          setInfo(_ele2, {
 19719            depth: i,
 19720            index: j
 19721          });
 19722        }
 19723      };
 19724  
 19725      var assignDepths = function assignDepths() {
 19726        for (var _i3 = 0; _i3 < depths.length; _i3++) {
 19727          assignDepthsAt(_i3);
 19728        }
 19729      };
 19730  
 19731      var adjustMaximally = function adjustMaximally(ele, shifted) {
 19732        var eInfo = getInfo(ele);
 19733        var incomers = ele.incomers().filter(function (el) {
 19734          return el.isNode() && eles.has(el);
 19735        });
 19736        var maxDepth = -1;
 19737        var id = ele.id();
 19738  
 19739        for (var k = 0; k < incomers.length; k++) {
 19740          var incmr = incomers[k];
 19741          var iInfo = getInfo(incmr);
 19742          maxDepth = Math.max(maxDepth, iInfo.depth);
 19743        }
 19744  
 19745        if (eInfo.depth <= maxDepth) {
 19746          if (shifted[id]) {
 19747            return null;
 19748          }
 19749  
 19750          changeDepth(ele, maxDepth + 1);
 19751          shifted[id] = true;
 19752          return true;
 19753        }
 19754  
 19755        return false;
 19756      }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
 19757  
 19758  
 19759      if (directed && maximal) {
 19760        var Q = [];
 19761        var shifted = {};
 19762  
 19763        var enqueue = function enqueue(n) {
 19764          return Q.push(n);
 19765        };
 19766  
 19767        var dequeue = function dequeue() {
 19768          return Q.shift();
 19769        };
 19770  
 19771        nodes.forEach(function (n) {
 19772          return Q.push(n);
 19773        });
 19774  
 19775        while (Q.length > 0) {
 19776          var _ele3 = dequeue();
 19777  
 19778          var didShift = adjustMaximally(_ele3, shifted);
 19779  
 19780          if (didShift) {
 19781            _ele3.outgoers().filter(function (el) {
 19782              return el.isNode() && eles.has(el);
 19783            }).forEach(enqueue);
 19784          } else if (didShift === null) {
 19785            warn('Detected double maximal shift for node `' + _ele3.id() + '`.  Bailing maximal adjustment due to cycle.  Use `options.maximal: true` only on DAGs.');
 19786            break; // exit on failure
 19787          }
 19788        }
 19789      }
 19790  
 19791      assignDepths(); // clear holes
 19792      // find min distance we need to leave between nodes
 19793  
 19794      var minDistance = 0;
 19795  
 19796      if (options.avoidOverlap) {
 19797        for (var _i4 = 0; _i4 < nodes.length; _i4++) {
 19798          var n = nodes[_i4];
 19799          var nbb = n.layoutDimensions(options);
 19800          var w = nbb.w;
 19801          var h = nbb.h;
 19802          minDistance = Math.max(minDistance, w, h);
 19803        }
 19804      } // get the weighted percent for an element based on its connectivity to other levels
 19805  
 19806  
 19807      var cachedWeightedPercent = {};
 19808  
 19809      var getWeightedPercent = function getWeightedPercent(ele) {
 19810        if (cachedWeightedPercent[ele.id()]) {
 19811          return cachedWeightedPercent[ele.id()];
 19812        }
 19813  
 19814        var eleDepth = getInfo(ele).depth;
 19815        var neighbors = ele.neighborhood();
 19816        var percent = 0;
 19817        var samples = 0;
 19818  
 19819        for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
 19820          var neighbor = neighbors[_i5];
 19821  
 19822          if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
 19823            continue;
 19824          }
 19825  
 19826          var bf = getInfo(neighbor);
 19827          var index = bf.index;
 19828          var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
 19829  
 19830          if (index == null || depth == null) {
 19831            continue;
 19832          }
 19833  
 19834          var nDepth = depths[depth].length;
 19835  
 19836          if (depth < eleDepth) {
 19837            // only get influenced by elements above
 19838            percent += index / nDepth;
 19839            samples++;
 19840          }
 19841        }
 19842  
 19843        samples = Math.max(1, samples);
 19844        percent = percent / samples;
 19845  
 19846        if (samples === 0) {
 19847          // put lone nodes at the start
 19848          percent = 0;
 19849        }
 19850  
 19851        cachedWeightedPercent[ele.id()] = percent;
 19852        return percent;
 19853      }; // rearrange the indices in each depth level based on connectivity
 19854  
 19855  
 19856      var sortFn = function sortFn(a, b) {
 19857        var apct = getWeightedPercent(a);
 19858        var bpct = getWeightedPercent(b);
 19859        var diff = apct - bpct;
 19860  
 19861        if (diff === 0) {
 19862          return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
 19863        } else {
 19864          return diff;
 19865        }
 19866      }; // sort each level to make connected nodes closer
 19867  
 19868  
 19869      for (var _i6 = 0; _i6 < depths.length; _i6++) {
 19870        depths[_i6].sort(sortFn);
 19871  
 19872        assignDepthsAt(_i6);
 19873      } // assign orphan nodes to a new top-level depth
 19874  
 19875  
 19876      var orphanDepth = [];
 19877  
 19878      for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
 19879        orphanDepth.push(orphanNodes[_i7]);
 19880      }
 19881  
 19882      depths.unshift(orphanDepth);
 19883      assignDepths();
 19884      var biggestDepthSize = 0;
 19885  
 19886      for (var _i8 = 0; _i8 < depths.length; _i8++) {
 19887        biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
 19888      }
 19889  
 19890      var center = {
 19891        x: bb.x1 + bb.w / 2,
 19892        y: bb.x1 + bb.h / 2
 19893      };
 19894      var maxDepthSize = depths.reduce(function (max, eles) {
 19895        return Math.max(max, eles.length);
 19896      }, 0);
 19897  
 19898      var getPosition = function getPosition(ele) {
 19899        var _getInfo2 = getInfo(ele),
 19900            depth = _getInfo2.depth,
 19901            index = _getInfo2.index;
 19902  
 19903        var depthSize = depths[depth].length;
 19904        var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
 19905        var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
 19906        var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
 19907        radiusStepSize = Math.max(radiusStepSize, minDistance);
 19908  
 19909        if (!options.circle) {
 19910          var epos = {
 19911            x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
 19912            y: (depth + 1) * distanceY
 19913          };
 19914          return epos;
 19915        } else {
 19916          var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
 19917          var theta = 2 * Math.PI / depths[depth].length * index;
 19918  
 19919          if (depth === 0 && depths[0].length === 1) {
 19920            radius = 1;
 19921          }
 19922  
 19923          return {
 19924            x: center.x + radius * Math.cos(theta),
 19925            y: center.y + radius * Math.sin(theta)
 19926          };
 19927        }
 19928      };
 19929  
 19930      nodes.layoutPositions(this, options, getPosition);
 19931      return this; // chaining
 19932    };
 19933  
 19934    var defaults$a = {
 19935      fit: true,
 19936      // whether to fit the viewport to the graph
 19937      padding: 30,
 19938      // the padding on fit
 19939      boundingBox: undefined,
 19940      // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 19941      avoidOverlap: true,
 19942      // prevents node overlap, may overflow boundingBox and radius if not enough space
 19943      nodeDimensionsIncludeLabels: false,
 19944      // Excludes the label when calculating node bounding boxes for the layout algorithm
 19945      spacingFactor: undefined,
 19946      // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
 19947      radius: undefined,
 19948      // the radius of the circle
 19949      startAngle: 3 / 2 * Math.PI,
 19950      // where nodes start in radians
 19951      sweep: undefined,
 19952      // how many radians should be between the first and last node (defaults to full circle)
 19953      clockwise: true,
 19954      // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
 19955      sort: undefined,
 19956      // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
 19957      animate: false,
 19958      // whether to transition the node positions
 19959      animationDuration: 500,
 19960      // duration of animation in ms if enabled
 19961      animationEasing: undefined,
 19962      // easing of animation if enabled
 19963      animateFilter: function animateFilter(node, i) {
 19964        return true;
 19965      },
 19966      // 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
 19967      ready: undefined,
 19968      // callback on layoutready
 19969      stop: undefined,
 19970      // callback on layoutstop
 19971      transform: function transform(node, position) {
 19972        return position;
 19973      } // transform a given node position. Useful for changing flow direction in discrete layouts 
 19974  
 19975    };
 19976  
 19977    function CircleLayout(options) {
 19978      this.options = extend({}, defaults$a, options);
 19979    }
 19980  
 19981    CircleLayout.prototype.run = function () {
 19982      var params = this.options;
 19983      var options = params;
 19984      var cy = params.cy;
 19985      var eles = options.eles;
 19986      var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
 19987      var nodes = eles.nodes().not(':parent');
 19988  
 19989      if (options.sort) {
 19990        nodes = nodes.sort(options.sort);
 19991      }
 19992  
 19993      var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
 19994        x1: 0,
 19995        y1: 0,
 19996        w: cy.width(),
 19997        h: cy.height()
 19998      });
 19999      var center = {
 20000        x: bb.x1 + bb.w / 2,
 20001        y: bb.y1 + bb.h / 2
 20002      };
 20003      var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
 20004      var dTheta = sweep / Math.max(1, nodes.length - 1);
 20005      var r;
 20006      var minDistance = 0;
 20007  
 20008      for (var i = 0; i < nodes.length; i++) {
 20009        var n = nodes[i];
 20010        var nbb = n.layoutDimensions(options);
 20011        var w = nbb.w;
 20012        var h = nbb.h;
 20013        minDistance = Math.max(minDistance, w, h);
 20014      }
 20015  
 20016      if (number(options.radius)) {
 20017        r = options.radius;
 20018      } else if (nodes.length <= 1) {
 20019        r = 0;
 20020      } else {
 20021        r = Math.min(bb.h, bb.w) / 2 - minDistance;
 20022      } // calculate the radius
 20023  
 20024  
 20025      if (nodes.length > 1 && options.avoidOverlap) {
 20026        // but only if more than one node (can't overlap)
 20027        minDistance *= 1.75; // just to have some nice spacing
 20028  
 20029        var dcos = Math.cos(dTheta) - Math.cos(0);
 20030        var dsin = Math.sin(dTheta) - Math.sin(0);
 20031        var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
 20032  
 20033        r = Math.max(rMin, r);
 20034      }
 20035  
 20036      var getPos = function getPos(ele, i) {
 20037        var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
 20038        var rx = r * Math.cos(theta);
 20039        var ry = r * Math.sin(theta);
 20040        var pos = {
 20041          x: center.x + rx,
 20042          y: center.y + ry
 20043        };
 20044        return pos;
 20045      };
 20046  
 20047      nodes.layoutPositions(this, options, getPos);
 20048      return this; // chaining
 20049    };
 20050  
 20051    var defaults$b = {
 20052      fit: true,
 20053      // whether to fit the viewport to the graph
 20054      padding: 30,
 20055      // the padding on fit
 20056      startAngle: 3 / 2 * Math.PI,
 20057      // where nodes start in radians
 20058      sweep: undefined,
 20059      // how many radians should be between the first and last node (defaults to full circle)
 20060      clockwise: true,
 20061      // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
 20062      equidistant: false,
 20063      // whether levels have an equal radial distance betwen them, may cause bounding box overflow
 20064      minNodeSpacing: 10,
 20065      // min spacing between outside of nodes (used for radius adjustment)
 20066      boundingBox: undefined,
 20067      // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 20068      avoidOverlap: true,
 20069      // prevents node overlap, may overflow boundingBox if not enough space
 20070      nodeDimensionsIncludeLabels: false,
 20071      // Excludes the label when calculating node bounding boxes for the layout algorithm
 20072      height: undefined,
 20073      // height of layout area (overrides container height)
 20074      width: undefined,
 20075      // width of layout area (overrides container width)
 20076      spacingFactor: undefined,
 20077      // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
 20078      concentric: function concentric(node) {
 20079        // returns numeric value for each node, placing higher nodes in levels towards the centre
 20080        return node.degree();
 20081      },
 20082      levelWidth: function levelWidth(nodes) {
 20083        // the letiation of concentric values in each level
 20084        return nodes.maxDegree() / 4;
 20085      },
 20086      animate: false,
 20087      // whether to transition the node positions
 20088      animationDuration: 500,
 20089      // duration of animation in ms if enabled
 20090      animationEasing: undefined,
 20091      // easing of animation if enabled
 20092      animateFilter: function animateFilter(node, i) {
 20093        return true;
 20094      },
 20095      // 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
 20096      ready: undefined,
 20097      // callback on layoutready
 20098      stop: undefined,
 20099      // callback on layoutstop
 20100      transform: function transform(node, position) {
 20101        return position;
 20102      } // transform a given node position. Useful for changing flow direction in discrete layouts
 20103  
 20104    };
 20105  
 20106    function ConcentricLayout(options) {
 20107      this.options = extend({}, defaults$b, options);
 20108    }
 20109  
 20110    ConcentricLayout.prototype.run = function () {
 20111      var params = this.options;
 20112      var options = params;
 20113      var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
 20114      var cy = params.cy;
 20115      var eles = options.eles;
 20116      var nodes = eles.nodes().not(':parent');
 20117      var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
 20118        x1: 0,
 20119        y1: 0,
 20120        w: cy.width(),
 20121        h: cy.height()
 20122      });
 20123      var center = {
 20124        x: bb.x1 + bb.w / 2,
 20125        y: bb.y1 + bb.h / 2
 20126      };
 20127      var nodeValues = []; // { node, value }
 20128  
 20129      var maxNodeSize = 0;
 20130  
 20131      for (var i = 0; i < nodes.length; i++) {
 20132        var node = nodes[i];
 20133        var value = void 0; // calculate the node value
 20134  
 20135        value = options.concentric(node);
 20136        nodeValues.push({
 20137          value: value,
 20138          node: node
 20139        }); // for style mapping
 20140  
 20141        node._private.scratch.concentric = value;
 20142      } // in case we used the `concentric` in style
 20143  
 20144  
 20145      nodes.updateStyle(); // calculate max size now based on potentially updated mappers
 20146  
 20147      for (var _i = 0; _i < nodes.length; _i++) {
 20148        var _node = nodes[_i];
 20149  
 20150        var nbb = _node.layoutDimensions(options);
 20151  
 20152        maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
 20153      } // sort node values in descreasing order
 20154  
 20155  
 20156      nodeValues.sort(function (a, b) {
 20157        return b.value - a.value;
 20158      });
 20159      var levelWidth = options.levelWidth(nodes); // put the values into levels
 20160  
 20161      var levels = [[]];
 20162      var currentLevel = levels[0];
 20163  
 20164      for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
 20165        var val = nodeValues[_i2];
 20166  
 20167        if (currentLevel.length > 0) {
 20168          var diff = Math.abs(currentLevel[0].value - val.value);
 20169  
 20170          if (diff >= levelWidth) {
 20171            currentLevel = [];
 20172            levels.push(currentLevel);
 20173          }
 20174        }
 20175  
 20176        currentLevel.push(val);
 20177      } // create positions from levels
 20178  
 20179  
 20180      var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
 20181  
 20182      if (!options.avoidOverlap) {
 20183        // then strictly constrain to bb
 20184        var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
 20185        var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
 20186        var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
 20187        minDist = Math.min(minDist, rStep);
 20188      } // find the metrics for each level
 20189  
 20190  
 20191      var r = 0;
 20192  
 20193      for (var _i3 = 0; _i3 < levels.length; _i3++) {
 20194        var level = levels[_i3];
 20195        var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
 20196        var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
 20197  
 20198        if (level.length > 1 && options.avoidOverlap) {
 20199          // but only if more than one node (can't overlap)
 20200          var dcos = Math.cos(dTheta) - Math.cos(0);
 20201          var dsin = Math.sin(dTheta) - Math.sin(0);
 20202          var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
 20203  
 20204          r = Math.max(rMin, r);
 20205        }
 20206  
 20207        level.r = r;
 20208        r += minDist;
 20209      }
 20210  
 20211      if (options.equidistant) {
 20212        var rDeltaMax = 0;
 20213        var _r = 0;
 20214  
 20215        for (var _i4 = 0; _i4 < levels.length; _i4++) {
 20216          var _level = levels[_i4];
 20217          var rDelta = _level.r - _r;
 20218          rDeltaMax = Math.max(rDeltaMax, rDelta);
 20219        }
 20220  
 20221        _r = 0;
 20222  
 20223        for (var _i5 = 0; _i5 < levels.length; _i5++) {
 20224          var _level2 = levels[_i5];
 20225  
 20226          if (_i5 === 0) {
 20227            _r = _level2.r;
 20228          }
 20229  
 20230          _level2.r = _r;
 20231          _r += rDeltaMax;
 20232        }
 20233      } // calculate the node positions
 20234  
 20235  
 20236      var pos = {}; // id => position
 20237  
 20238      for (var _i6 = 0; _i6 < levels.length; _i6++) {
 20239        var _level3 = levels[_i6];
 20240        var _dTheta = _level3.dTheta;
 20241        var _r2 = _level3.r;
 20242  
 20243        for (var j = 0; j < _level3.length; j++) {
 20244          var _val = _level3[j];
 20245          var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
 20246          var p = {
 20247            x: center.x + _r2 * Math.cos(theta),
 20248            y: center.y + _r2 * Math.sin(theta)
 20249          };
 20250          pos[_val.node.id()] = p;
 20251        }
 20252      } // position the nodes
 20253  
 20254  
 20255      nodes.layoutPositions(this, options, function (ele) {
 20256        var id = ele.id();
 20257        return pos[id];
 20258      });
 20259      return this; // chaining
 20260    };
 20261  
 20262    /*
 20263    The CoSE layout was written by Gerardo Huck.
 20264    https://www.linkedin.com/in/gerardohuck/
 20265  
 20266    Based on the following article:
 20267    http://dl.acm.org/citation.cfm?id=1498047
 20268  
 20269    Modifications tracked on Github.
 20270    */
 20271    var DEBUG;
 20272    /**
 20273     * @brief :  default layout options
 20274     */
 20275  
 20276    var defaults$c = {
 20277      // Called on `layoutready`
 20278      ready: function ready() {},
 20279      // Called on `layoutstop`
 20280      stop: function stop() {},
 20281      // Whether to animate while running the layout
 20282      // true : Animate continuously as the layout is running
 20283      // false : Just show the end result
 20284      // 'end' : Animate with the end result, from the initial positions to the end positions
 20285      animate: true,
 20286      // Easing of the animation for animate:'end'
 20287      animationEasing: undefined,
 20288      // The duration of the animation for animate:'end'
 20289      animationDuration: undefined,
 20290      // A function that determines whether the node should be animated
 20291      // All nodes animated by default on animate enabled
 20292      // Non-animated nodes are positioned immediately when the layout starts
 20293      animateFilter: function animateFilter(node, i) {
 20294        return true;
 20295      },
 20296      // The layout animates only after this many milliseconds for animate:true
 20297      // (prevents flashing on fast runs)
 20298      animationThreshold: 250,
 20299      // Number of iterations between consecutive screen positions update
 20300      refresh: 20,
 20301      // Whether to fit the network view after when done
 20302      fit: true,
 20303      // Padding on fit
 20304      padding: 30,
 20305      // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 20306      boundingBox: undefined,
 20307      // Excludes the label when calculating node bounding boxes for the layout algorithm
 20308      nodeDimensionsIncludeLabels: false,
 20309      // Randomize the initial positions of the nodes (true) or use existing positions (false)
 20310      randomize: false,
 20311      // Extra spacing between components in non-compound graphs
 20312      componentSpacing: 40,
 20313      // Node repulsion (non overlapping) multiplier
 20314      nodeRepulsion: function nodeRepulsion(node) {
 20315        return 2048;
 20316      },
 20317      // Node repulsion (overlapping) multiplier
 20318      nodeOverlap: 4,
 20319      // Ideal edge (non nested) length
 20320      idealEdgeLength: function idealEdgeLength(edge) {
 20321        return 32;
 20322      },
 20323      // Divisor to compute edge forces
 20324      edgeElasticity: function edgeElasticity(edge) {
 20325        return 32;
 20326      },
 20327      // Nesting factor (multiplier) to compute ideal edge length for nested edges
 20328      nestingFactor: 1.2,
 20329      // Gravity force (constant)
 20330      gravity: 1,
 20331      // Maximum number of iterations to perform
 20332      numIter: 1000,
 20333      // Initial temperature (maximum node displacement)
 20334      initialTemp: 1000,
 20335      // Cooling factor (how the temperature is reduced between consecutive iterations
 20336      coolingFactor: 0.99,
 20337      // Lower temperature threshold (below this point the layout will end)
 20338      minTemp: 1.0
 20339    };
 20340    /**
 20341     * @brief       : constructor
 20342     * @arg options : object containing layout options
 20343     */
 20344  
 20345    function CoseLayout(options) {
 20346      this.options = extend({}, defaults$c, options);
 20347      this.options.layout = this;
 20348    }
 20349    /**
 20350     * @brief : runs the layout
 20351     */
 20352  
 20353  
 20354    CoseLayout.prototype.run = function () {
 20355      var options = this.options;
 20356      var cy = options.cy;
 20357      var layout = this;
 20358      layout.stopped = false;
 20359  
 20360      if (options.animate === true || options.animate === false) {
 20361        layout.emit({
 20362          type: 'layoutstart',
 20363          layout: layout
 20364        });
 20365      } // Set DEBUG - Global variable
 20366  
 20367  
 20368      if (true === options.debug) {
 20369        DEBUG = true;
 20370      } else {
 20371        DEBUG = false;
 20372      } // Initialize layout info
 20373  
 20374  
 20375      var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
 20376  
 20377      if (DEBUG) {
 20378        printLayoutInfo(layoutInfo);
 20379      } // If required, randomize node positions
 20380  
 20381  
 20382      if (options.randomize) {
 20383        randomizePositions(layoutInfo);
 20384      }
 20385  
 20386      var startTime = performanceNow();
 20387  
 20388      var refresh = function refresh() {
 20389        refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
 20390  
 20391        if (true === options.fit) {
 20392          cy.fit(options.padding);
 20393        }
 20394      };
 20395  
 20396      var mainLoop = function mainLoop(i) {
 20397        if (layout.stopped || i >= options.numIter) {
 20398          // logDebug("Layout manually stopped. Stopping computation in step " + i);
 20399          return false;
 20400        } // Do one step in the phisical simulation
 20401  
 20402  
 20403        step$1(layoutInfo, options); // Update temperature
 20404  
 20405        layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
 20406  
 20407        if (layoutInfo.temperature < options.minTemp) {
 20408          // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
 20409          return false;
 20410        }
 20411  
 20412        return true;
 20413      };
 20414  
 20415      var done = function done() {
 20416        if (options.animate === true || options.animate === false) {
 20417          refresh(); // Layout has finished
 20418  
 20419          layout.one('layoutstop', options.stop);
 20420          layout.emit({
 20421            type: 'layoutstop',
 20422            layout: layout
 20423          });
 20424        } else {
 20425          var nodes = options.eles.nodes();
 20426          var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
 20427          nodes.layoutPositions(layout, options, getScaledPos);
 20428        }
 20429      };
 20430  
 20431      var i = 0;
 20432      var loopRet = true;
 20433  
 20434      if (options.animate === true) {
 20435        var frame = function frame() {
 20436          var f = 0;
 20437  
 20438          while (loopRet && f < options.refresh) {
 20439            loopRet = mainLoop(i);
 20440            i++;
 20441            f++;
 20442          }
 20443  
 20444          if (!loopRet) {
 20445            // it's done
 20446            separateComponents(layoutInfo, options);
 20447            done();
 20448          } else {
 20449            var now = performanceNow();
 20450  
 20451            if (now - startTime >= options.animationThreshold) {
 20452              refresh();
 20453            }
 20454  
 20455            requestAnimationFrame(frame);
 20456          }
 20457        };
 20458  
 20459        frame();
 20460      } else {
 20461        while (loopRet) {
 20462          loopRet = mainLoop(i);
 20463          i++;
 20464        }
 20465  
 20466        separateComponents(layoutInfo, options);
 20467        done();
 20468      }
 20469  
 20470      return this; // chaining
 20471    };
 20472    /**
 20473     * @brief : called on continuous layouts to stop them before they finish
 20474     */
 20475  
 20476  
 20477    CoseLayout.prototype.stop = function () {
 20478      this.stopped = true;
 20479  
 20480      if (this.thread) {
 20481        this.thread.stop();
 20482      }
 20483  
 20484      this.emit('layoutstop');
 20485      return this; // chaining
 20486    };
 20487  
 20488    CoseLayout.prototype.destroy = function () {
 20489      if (this.thread) {
 20490        this.thread.stop();
 20491      }
 20492  
 20493      return this; // chaining
 20494    };
 20495    /**
 20496     * @brief     : Creates an object which is contains all the data
 20497     *              used in the layout process
 20498     * @arg cy    : cytoscape.js object
 20499     * @return    : layoutInfo object initialized
 20500     */
 20501  
 20502  
 20503    var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
 20504      // Shortcut
 20505      var edges = options.eles.edges();
 20506      var nodes = options.eles.nodes();
 20507      var layoutInfo = {
 20508        isCompound: cy.hasCompoundNodes(),
 20509        layoutNodes: [],
 20510        idToIndex: {},
 20511        nodeSize: nodes.size(),
 20512        graphSet: [],
 20513        indexToGraph: [],
 20514        layoutEdges: [],
 20515        edgeSize: edges.size(),
 20516        temperature: options.initialTemp,
 20517        clientWidth: cy.width(),
 20518        clientHeight: cy.width(),
 20519        boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
 20520          x1: 0,
 20521          y1: 0,
 20522          w: cy.width(),
 20523          h: cy.height()
 20524        })
 20525      };
 20526      var components = options.eles.components();
 20527      var id2cmptId = {};
 20528  
 20529      for (var i = 0; i < components.length; i++) {
 20530        var component = components[i];
 20531  
 20532        for (var j = 0; j < component.length; j++) {
 20533          var node = component[j];
 20534          id2cmptId[node.id()] = i;
 20535        }
 20536      } // Iterate over all nodes, creating layout nodes
 20537  
 20538  
 20539      for (var i = 0; i < layoutInfo.nodeSize; i++) {
 20540        var n = nodes[i];
 20541        var nbb = n.layoutDimensions(options);
 20542        var tempNode = {};
 20543        tempNode.isLocked = n.locked();
 20544        tempNode.id = n.data('id');
 20545        tempNode.parentId = n.data('parent');
 20546        tempNode.cmptId = id2cmptId[n.id()];
 20547        tempNode.children = [];
 20548        tempNode.positionX = n.position('x');
 20549        tempNode.positionY = n.position('y');
 20550        tempNode.offsetX = 0;
 20551        tempNode.offsetY = 0;
 20552        tempNode.height = nbb.w;
 20553        tempNode.width = nbb.h;
 20554        tempNode.maxX = tempNode.positionX + tempNode.width / 2;
 20555        tempNode.minX = tempNode.positionX - tempNode.width / 2;
 20556        tempNode.maxY = tempNode.positionY + tempNode.height / 2;
 20557        tempNode.minY = tempNode.positionY - tempNode.height / 2;
 20558        tempNode.padLeft = parseFloat(n.style('padding'));
 20559        tempNode.padRight = parseFloat(n.style('padding'));
 20560        tempNode.padTop = parseFloat(n.style('padding'));
 20561        tempNode.padBottom = parseFloat(n.style('padding')); // forces
 20562  
 20563        tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
 20564  
 20565        layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
 20566  
 20567        layoutInfo.idToIndex[tempNode.id] = i;
 20568      } // Inline implementation of a queue, used for traversing the graph in BFS order
 20569  
 20570  
 20571      var queue = [];
 20572      var start = 0; // Points to the start the queue
 20573  
 20574      var end = -1; // Points to the end of the queue
 20575  
 20576      var tempGraph = []; // Second pass to add child information and
 20577      // initialize queue for hierarchical traversal
 20578  
 20579      for (var i = 0; i < layoutInfo.nodeSize; i++) {
 20580        var n = layoutInfo.layoutNodes[i];
 20581        var p_id = n.parentId; // Check if node n has a parent node
 20582  
 20583        if (null != p_id) {
 20584          // Add node Id to parent's list of children
 20585          layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
 20586        } else {
 20587          // If a node doesn't have a parent, then it's in the root graph
 20588          queue[++end] = n.id;
 20589          tempGraph.push(n.id);
 20590        }
 20591      } // Add root graph to graphSet
 20592  
 20593  
 20594      layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
 20595  
 20596      while (start <= end) {
 20597        // Get the node to visit and remove it from queue
 20598        var node_id = queue[start++];
 20599        var node_ix = layoutInfo.idToIndex[node_id];
 20600        var node = layoutInfo.layoutNodes[node_ix];
 20601        var children = node.children;
 20602  
 20603        if (children.length > 0) {
 20604          // Add children nodes as a new graph to graph set
 20605          layoutInfo.graphSet.push(children); // Add children to que queue to be visited
 20606  
 20607          for (var i = 0; i < children.length; i++) {
 20608            queue[++end] = children[i];
 20609          }
 20610        }
 20611      } // Create indexToGraph map
 20612  
 20613  
 20614      for (var i = 0; i < layoutInfo.graphSet.length; i++) {
 20615        var graph = layoutInfo.graphSet[i];
 20616  
 20617        for (var j = 0; j < graph.length; j++) {
 20618          var index = layoutInfo.idToIndex[graph[j]];
 20619          layoutInfo.indexToGraph[index] = i;
 20620        }
 20621      } // Iterate over all edges, creating Layout Edges
 20622  
 20623  
 20624      for (var i = 0; i < layoutInfo.edgeSize; i++) {
 20625        var e = edges[i];
 20626        var tempEdge = {};
 20627        tempEdge.id = e.data('id');
 20628        tempEdge.sourceId = e.data('source');
 20629        tempEdge.targetId = e.data('target'); // Compute ideal length
 20630  
 20631        var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
 20632        var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
 20633  
 20634        var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
 20635        var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
 20636        var sourceGraph = layoutInfo.indexToGraph[sourceIx];
 20637        var targetGraph = layoutInfo.indexToGraph[targetIx];
 20638  
 20639        if (sourceGraph != targetGraph) {
 20640          // Find lowest common graph ancestor
 20641          var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
 20642  
 20643          var lcaGraph = layoutInfo.graphSet[lca];
 20644          var depth = 0; // Source depth
 20645  
 20646          var tempNode = layoutInfo.layoutNodes[sourceIx];
 20647  
 20648          while (-1 === lcaGraph.indexOf(tempNode.id)) {
 20649            tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
 20650            depth++;
 20651          } // Target depth
 20652  
 20653  
 20654          tempNode = layoutInfo.layoutNodes[targetIx];
 20655  
 20656          while (-1 === lcaGraph.indexOf(tempNode.id)) {
 20657            tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
 20658            depth++;
 20659          } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
 20660          //  ". Index: " + lca + " Contents: " + lcaGraph.toString() +
 20661          //  ". Depth: " + depth);
 20662          // Update idealLength
 20663  
 20664  
 20665          idealLength *= depth * options.nestingFactor;
 20666        }
 20667  
 20668        tempEdge.idealLength = idealLength;
 20669        tempEdge.elasticity = elasticity;
 20670        layoutInfo.layoutEdges.push(tempEdge);
 20671      } // Finally, return layoutInfo object
 20672  
 20673  
 20674      return layoutInfo;
 20675    };
 20676    /**
 20677     * @brief : This function finds the index of the lowest common
 20678     *          graph ancestor between 2 nodes in the subtree
 20679     *          (from the graph hierarchy induced tree) whose
 20680     *          root is graphIx
 20681     *
 20682     * @arg node1: node1's ID
 20683     * @arg node2: node2's ID
 20684     * @arg layoutInfo: layoutInfo object
 20685     *
 20686     */
 20687  
 20688  
 20689    var findLCA = function findLCA(node1, node2, layoutInfo) {
 20690      // Find their common ancester, starting from the root graph
 20691      var res = findLCA_aux(node1, node2, 0, layoutInfo);
 20692  
 20693      if (2 > res.count) {
 20694        // If aux function couldn't find the common ancester,
 20695        // then it is the root graph
 20696        return 0;
 20697      } else {
 20698        return res.graph;
 20699      }
 20700    };
 20701    /**
 20702     * @brief          : Auxiliary function used for LCA computation
 20703     *
 20704     * @arg node1      : node1's ID
 20705     * @arg node2      : node2's ID
 20706     * @arg graphIx    : subgraph index
 20707     * @arg layoutInfo : layoutInfo object
 20708     *
 20709     * @return         : object of the form {count: X, graph: Y}, where:
 20710     *                   X is the number of ancesters (max: 2) found in
 20711     *                   graphIx (and it's subgraphs),
 20712     *                   Y is the graph index of the lowest graph containing
 20713     *                   all X nodes
 20714     */
 20715  
 20716  
 20717    var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
 20718      var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
 20719  
 20720      if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
 20721        return {
 20722          count: 2,
 20723          graph: graphIx
 20724        };
 20725      } // Make recursive calls for all subgraphs
 20726  
 20727  
 20728      var c = 0;
 20729  
 20730      for (var i = 0; i < graph.length; i++) {
 20731        var nodeId = graph[i];
 20732        var nodeIx = layoutInfo.idToIndex[nodeId];
 20733        var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
 20734  
 20735        if (0 === children.length) {
 20736          continue;
 20737        }
 20738  
 20739        var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
 20740        var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
 20741  
 20742        if (0 === result.count) {
 20743          // Neither node1 nor node2 are present in this subgraph
 20744          continue;
 20745        } else if (1 === result.count) {
 20746          // One of (node1, node2) is present in this subgraph
 20747          c++;
 20748  
 20749          if (2 === c) {
 20750            // We've already found both nodes, no need to keep searching
 20751            break;
 20752          }
 20753        } else {
 20754          // Both nodes are present in this subgraph
 20755          return result;
 20756        }
 20757      }
 20758  
 20759      return {
 20760        count: c,
 20761        graph: graphIx
 20762      };
 20763    };
 20764    /**
 20765     * @brief: printsLayoutInfo into js console
 20766     *         Only used for debbuging
 20767     */
 20768  
 20769  
 20770    if (false) {
 20771      var printLayoutInfo;
 20772    }
 20773    /**
 20774     * @brief : Randomizes the position of all nodes
 20775     */
 20776  
 20777  
 20778    var randomizePositions = function randomizePositions(layoutInfo, cy) {
 20779      var width = layoutInfo.clientWidth;
 20780      var height = layoutInfo.clientHeight;
 20781  
 20782      for (var i = 0; i < layoutInfo.nodeSize; i++) {
 20783        var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
 20784  
 20785        if (0 === n.children.length && !n.isLocked) {
 20786          n.positionX = Math.random() * width;
 20787          n.positionY = Math.random() * height;
 20788        }
 20789      }
 20790    };
 20791  
 20792    var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
 20793      var bb = layoutInfo.boundingBox;
 20794      var coseBB = {
 20795        x1: Infinity,
 20796        x2: -Infinity,
 20797        y1: Infinity,
 20798        y2: -Infinity
 20799      };
 20800  
 20801      if (options.boundingBox) {
 20802        nodes.forEach(function (node) {
 20803          var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
 20804          coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
 20805          coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
 20806          coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
 20807          coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
 20808        });
 20809        coseBB.w = coseBB.x2 - coseBB.x1;
 20810        coseBB.h = coseBB.y2 - coseBB.y1;
 20811      }
 20812  
 20813      return function (ele, i) {
 20814        var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
 20815  
 20816        if (options.boundingBox) {
 20817          // then add extra bounding box constraint
 20818          var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
 20819          var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
 20820          return {
 20821            x: bb.x1 + pctX * bb.w,
 20822            y: bb.y1 + pctY * bb.h
 20823          };
 20824        } else {
 20825          return {
 20826            x: lnode.positionX,
 20827            y: lnode.positionY
 20828          };
 20829        }
 20830      };
 20831    };
 20832    /**
 20833     * @brief          : Updates the positions of nodes in the network
 20834     * @arg layoutInfo : LayoutInfo object
 20835     * @arg cy         : Cytoscape object
 20836     * @arg options    : Layout options
 20837     */
 20838  
 20839  
 20840    var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
 20841      // var s = 'Refreshing positions';
 20842      // logDebug(s);
 20843      var layout = options.layout;
 20844      var nodes = options.eles.nodes();
 20845      var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
 20846      nodes.positions(getScaledPos); // Trigger layoutReady only on first call
 20847  
 20848      if (true !== layoutInfo.ready) {
 20849        // s = 'Triggering layoutready';
 20850        // logDebug(s);
 20851        layoutInfo.ready = true;
 20852        layout.one('layoutready', options.ready);
 20853        layout.emit({
 20854          type: 'layoutready',
 20855          layout: this
 20856        });
 20857      }
 20858    };
 20859    /**
 20860     * @brief : Logs a debug message in JS console, if DEBUG is ON
 20861     */
 20862    // var logDebug = function(text) {
 20863    //   if (DEBUG) {
 20864    //     console.debug(text);
 20865    //   }
 20866    // };
 20867  
 20868    /**
 20869     * @brief          : Performs one iteration of the physical simulation
 20870     * @arg layoutInfo : LayoutInfo object already initialized
 20871     * @arg cy         : Cytoscape object
 20872     * @arg options    : Layout options
 20873     */
 20874  
 20875  
 20876    var step$1 = function step(layoutInfo, options, _step) {
 20877      // var s = "\n\n###############################";
 20878      // s += "\nSTEP: " + step;
 20879      // s += "\n###############################\n";
 20880      // logDebug(s);
 20881      // Calculate node repulsions
 20882      calculateNodeForces(layoutInfo, options); // Calculate edge forces
 20883  
 20884      calculateEdgeForces(layoutInfo); // Calculate gravity forces
 20885  
 20886      calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
 20887  
 20888      propagateForces(layoutInfo); // Update positions based on calculated forces
 20889  
 20890      updatePositions(layoutInfo);
 20891    };
 20892    /**
 20893     * @brief : Computes the node repulsion forces
 20894     */
 20895  
 20896  
 20897    var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
 20898      // Go through each of the graphs in graphSet
 20899      // Nodes only repel each other if they belong to the same graph
 20900      // var s = 'calculateNodeForces';
 20901      // logDebug(s);
 20902      for (var i = 0; i < layoutInfo.graphSet.length; i++) {
 20903        var graph = layoutInfo.graphSet[i];
 20904        var numNodes = graph.length; // s = "Set: " + graph.toString();
 20905        // logDebug(s);
 20906        // Now get all the pairs of nodes
 20907        // Only get each pair once, (A, B) = (B, A)
 20908  
 20909        for (var j = 0; j < numNodes; j++) {
 20910          var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
 20911  
 20912          for (var k = j + 1; k < numNodes; k++) {
 20913            var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
 20914            nodeRepulsion(node1, node2, layoutInfo, options);
 20915          }
 20916        }
 20917      }
 20918    };
 20919  
 20920    var randomDistance = function randomDistance(max) {
 20921      return -max + 2 * max * Math.random();
 20922    };
 20923    /**
 20924     * @brief : Compute the node repulsion forces between a pair of nodes
 20925     */
 20926  
 20927  
 20928    var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
 20929      // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
 20930      var cmptId1 = node1.cmptId;
 20931      var cmptId2 = node2.cmptId;
 20932  
 20933      if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
 20934        return;
 20935      } // Get direction of line connecting both node centers
 20936  
 20937  
 20938      var directionX = node2.positionX - node1.positionX;
 20939      var directionY = node2.positionY - node1.positionY;
 20940      var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
 20941      // If both centers are the same, apply a random force
 20942  
 20943      if (0 === directionX && 0 === directionY) {
 20944        directionX = randomDistance(maxRandDist);
 20945        directionY = randomDistance(maxRandDist);
 20946      }
 20947  
 20948      var overlap = nodesOverlap(node1, node2, directionX, directionY);
 20949  
 20950      if (overlap > 0) {
 20951        // s += "\nNodes DO overlap.";
 20952        // s += "\nOverlap: " + overlap;
 20953        // If nodes overlap, repulsion force is proportional
 20954        // to the overlap
 20955        var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
 20956  
 20957        var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
 20958  
 20959        var forceX = force * directionX / distance;
 20960        var forceY = force * directionY / distance;
 20961      } else {
 20962        // s += "\nNodes do NOT overlap.";
 20963        // If there's no overlap, force is inversely proportional
 20964        // to squared distance
 20965        // Get clipping points for both nodes
 20966        var point1 = findClippingPoint(node1, directionX, directionY);
 20967        var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
 20968  
 20969        var distanceX = point2.x - point1.x;
 20970        var distanceY = point2.y - point1.y;
 20971        var distanceSqr = distanceX * distanceX + distanceY * distanceY;
 20972        var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
 20973        // Compute the module and components of the force vector
 20974  
 20975        var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
 20976        var forceX = force * distanceX / distance;
 20977        var forceY = force * distanceY / distance;
 20978      } // Apply force
 20979  
 20980  
 20981      if (!node1.isLocked) {
 20982        node1.offsetX -= forceX;
 20983        node1.offsetY -= forceY;
 20984      }
 20985  
 20986      if (!node2.isLocked) {
 20987        node2.offsetX += forceX;
 20988        node2.offsetY += forceY;
 20989      } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
 20990      // logDebug(s);
 20991  
 20992  
 20993      return;
 20994    };
 20995    /**
 20996     * @brief  : Determines whether two nodes overlap or not
 20997     * @return : Amount of overlapping (0 => no overlap)
 20998     */
 20999  
 21000  
 21001    var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
 21002      if (dX > 0) {
 21003        var overlapX = node1.maxX - node2.minX;
 21004      } else {
 21005        var overlapX = node2.maxX - node1.minX;
 21006      }
 21007  
 21008      if (dY > 0) {
 21009        var overlapY = node1.maxY - node2.minY;
 21010      } else {
 21011        var overlapY = node2.maxY - node1.minY;
 21012      }
 21013  
 21014      if (overlapX >= 0 && overlapY >= 0) {
 21015        return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
 21016      } else {
 21017        return 0;
 21018      }
 21019    };
 21020    /**
 21021     * @brief : Finds the point in which an edge (direction dX, dY) intersects
 21022     *          the rectangular bounding box of it's source/target node
 21023     */
 21024  
 21025  
 21026    var findClippingPoint = function findClippingPoint(node, dX, dY) {
 21027      // Shorcuts
 21028      var X = node.positionX;
 21029      var Y = node.positionY;
 21030      var H = node.height || 1;
 21031      var W = node.width || 1;
 21032      var dirSlope = dY / dX;
 21033      var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
 21034      //   " . Height:  " + H + ", Width: " + W +
 21035      //   "\nDirection " + dX + ", " + dY;
 21036      //
 21037      // Compute intersection
 21038  
 21039      var res = {}; // Case: Vertical direction (up)
 21040  
 21041      if (0 === dX && 0 < dY) {
 21042        res.x = X; // s += "\nUp direction";
 21043  
 21044        res.y = Y + H / 2;
 21045        return res;
 21046      } // Case: Vertical direction (down)
 21047  
 21048  
 21049      if (0 === dX && 0 > dY) {
 21050        res.x = X;
 21051        res.y = Y + H / 2; // s += "\nDown direction";
 21052  
 21053        return res;
 21054      } // Case: Intersects the right border
 21055  
 21056  
 21057      if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
 21058        res.x = X + W / 2;
 21059        res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
 21060  
 21061        return res;
 21062      } // Case: Intersects the left border
 21063  
 21064  
 21065      if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
 21066        res.x = X - W / 2;
 21067        res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
 21068  
 21069        return res;
 21070      } // Case: Intersects the top border
 21071  
 21072  
 21073      if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
 21074        res.x = X + H * dX / 2 / dY;
 21075        res.y = Y + H / 2; // s += "\nTop border";
 21076  
 21077        return res;
 21078      } // Case: Intersects the bottom border
 21079  
 21080  
 21081      if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
 21082        res.x = X - H * dX / 2 / dY;
 21083        res.y = Y - H / 2; // s += "\nBottom border";
 21084  
 21085        return res;
 21086      } // s += "\nClipping point found at " + res.x + ", " + res.y;
 21087      // logDebug(s);
 21088  
 21089  
 21090      return res;
 21091    };
 21092    /**
 21093     * @brief : Calculates all edge forces
 21094     */
 21095  
 21096  
 21097    var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
 21098      // Iterate over all edges
 21099      for (var i = 0; i < layoutInfo.edgeSize; i++) {
 21100        // Get edge, source & target nodes
 21101        var edge = layoutInfo.layoutEdges[i];
 21102        var sourceIx = layoutInfo.idToIndex[edge.sourceId];
 21103        var source = layoutInfo.layoutNodes[sourceIx];
 21104        var targetIx = layoutInfo.idToIndex[edge.targetId];
 21105        var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
 21106  
 21107        var directionX = target.positionX - source.positionX;
 21108        var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
 21109        // A random force has already been applied as node repulsion
 21110  
 21111        if (0 === directionX && 0 === directionY) {
 21112          continue;
 21113        } // Get clipping points for both nodes
 21114  
 21115  
 21116        var point1 = findClippingPoint(source, directionX, directionY);
 21117        var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
 21118        var lx = point2.x - point1.x;
 21119        var ly = point2.y - point1.y;
 21120        var l = Math.sqrt(lx * lx + ly * ly);
 21121        var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
 21122  
 21123        if (0 !== l) {
 21124          var forceX = force * lx / l;
 21125          var forceY = force * ly / l;
 21126        } else {
 21127          var forceX = 0;
 21128          var forceY = 0;
 21129        } // Add this force to target and source nodes
 21130  
 21131  
 21132        if (!source.isLocked) {
 21133          source.offsetX += forceX;
 21134          source.offsetY += forceY;
 21135        }
 21136  
 21137        if (!target.isLocked) {
 21138          target.offsetX -= forceX;
 21139          target.offsetY -= forceY;
 21140        } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
 21141        // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
 21142        // logDebug(s);
 21143  
 21144      }
 21145    };
 21146    /**
 21147     * @brief : Computes gravity forces for all nodes
 21148     */
 21149  
 21150  
 21151    var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
 21152      var distThreshold = 1; // var s = 'calculateGravityForces';
 21153      // logDebug(s);
 21154  
 21155      for (var i = 0; i < layoutInfo.graphSet.length; i++) {
 21156        var graph = layoutInfo.graphSet[i];
 21157        var numNodes = graph.length; // s = "Set: " + graph.toString();
 21158        // logDebug(s);
 21159        // Compute graph center
 21160  
 21161        if (0 === i) {
 21162          var centerX = layoutInfo.clientHeight / 2;
 21163          var centerY = layoutInfo.clientWidth / 2;
 21164        } else {
 21165          // Get Parent node for this graph, and use its position as center
 21166          var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
 21167          var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
 21168          var centerX = parent.positionX;
 21169          var centerY = parent.positionY;
 21170        } // s = "Center found at: " + centerX + ", " + centerY;
 21171        // logDebug(s);
 21172        // Apply force to all nodes in graph
 21173  
 21174  
 21175        for (var j = 0; j < numNodes; j++) {
 21176          var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
 21177  
 21178          if (node.isLocked) {
 21179            continue;
 21180          }
 21181  
 21182          var dx = centerX - node.positionX;
 21183          var dy = centerY - node.positionY;
 21184          var d = Math.sqrt(dx * dx + dy * dy);
 21185  
 21186          if (d > distThreshold) {
 21187            var fx = options.gravity * dx / d;
 21188            var fy = options.gravity * dy / d;
 21189            node.offsetX += fx;
 21190            node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
 21191          } // s += ": skypped since it's too close to center";
 21192            // logDebug(s);
 21193  
 21194        }
 21195      }
 21196    };
 21197    /**
 21198     * @brief          : This function propagates the existing offsets from
 21199     *                   parent nodes to its descendents.
 21200     * @arg layoutInfo : layoutInfo Object
 21201     * @arg cy         : cytoscape Object
 21202     * @arg options    : Layout options
 21203     */
 21204  
 21205  
 21206    var propagateForces = function propagateForces(layoutInfo, options) {
 21207      // Inline implementation of a queue, used for traversing the graph in BFS order
 21208      var queue = [];
 21209      var start = 0; // Points to the start the queue
 21210  
 21211      var end = -1; // Points to the end of the queue
 21212      // logDebug('propagateForces');
 21213      // Start by visiting the nodes in the root graph
 21214  
 21215      queue.push.apply(queue, layoutInfo.graphSet[0]);
 21216      end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
 21217  
 21218      while (start <= end) {
 21219        // Get the node to visit and remove it from queue
 21220        var nodeId = queue[start++];
 21221        var nodeIndex = layoutInfo.idToIndex[nodeId];
 21222        var node = layoutInfo.layoutNodes[nodeIndex];
 21223        var children = node.children; // We only need to process the node if it's compound
 21224  
 21225        if (0 < children.length && !node.isLocked) {
 21226          var offX = node.offsetX;
 21227          var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
 21228          //   ". OffsetX: " + offX + ". OffsetY: " + offY;
 21229          // s += "\n Children: " + children.toString();
 21230          // logDebug(s);
 21231  
 21232          for (var i = 0; i < children.length; i++) {
 21233            var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
 21234  
 21235            childNode.offsetX += offX;
 21236            childNode.offsetY += offY; // Add children to queue to be visited
 21237  
 21238            queue[++end] = children[i];
 21239          } // Reset parent offsets
 21240  
 21241  
 21242          node.offsetX = 0;
 21243          node.offsetY = 0;
 21244        }
 21245      }
 21246    };
 21247    /**
 21248     * @brief : Updates the layout model positions, based on
 21249     *          the accumulated forces
 21250     */
 21251  
 21252  
 21253    var updatePositions = function updatePositions(layoutInfo, options) {
 21254      // var s = 'Updating positions';
 21255      // logDebug(s);
 21256      // Reset boundaries for compound nodes
 21257      for (var i = 0; i < layoutInfo.nodeSize; i++) {
 21258        var n = layoutInfo.layoutNodes[i];
 21259  
 21260        if (0 < n.children.length) {
 21261          // logDebug("Resetting boundaries of compound node: " + n.id);
 21262          n.maxX = undefined;
 21263          n.minX = undefined;
 21264          n.maxY = undefined;
 21265          n.minY = undefined;
 21266        }
 21267      }
 21268  
 21269      for (var i = 0; i < layoutInfo.nodeSize; i++) {
 21270        var n = layoutInfo.layoutNodes[i];
 21271  
 21272        if (0 < n.children.length || n.isLocked) {
 21273          // No need to set compound or locked node position
 21274          // logDebug("Skipping position update of node: " + n.id);
 21275          continue;
 21276        } // s = "Node: " + n.id + " Previous position: (" +
 21277        // n.positionX + ", " + n.positionY + ").";
 21278        // Limit displacement in order to improve stability
 21279  
 21280  
 21281        var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
 21282        n.positionX += tempForce.x;
 21283        n.positionY += tempForce.y;
 21284        n.offsetX = 0;
 21285        n.offsetY = 0;
 21286        n.minX = n.positionX - n.width;
 21287        n.maxX = n.positionX + n.width;
 21288        n.minY = n.positionY - n.height;
 21289        n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
 21290        // logDebug(s);
 21291        // Update ancestry boudaries
 21292  
 21293        updateAncestryBoundaries(n, layoutInfo);
 21294      } // Update size, position of compund nodes
 21295  
 21296  
 21297      for (var i = 0; i < layoutInfo.nodeSize; i++) {
 21298        var n = layoutInfo.layoutNodes[i];
 21299  
 21300        if (0 < n.children.length && !n.isLocked) {
 21301          n.positionX = (n.maxX + n.minX) / 2;
 21302          n.positionY = (n.maxY + n.minY) / 2;
 21303          n.width = n.maxX - n.minX;
 21304          n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
 21305          // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
 21306          // s += "\nWidth: " + n.width + ", Height: " + n.height;
 21307          // logDebug(s);
 21308        }
 21309      }
 21310    };
 21311    /**
 21312     * @brief : Limits a force (forceX, forceY) to be not
 21313     *          greater (in modulo) than max.
 21314     8          Preserves force direction.
 21315      */
 21316  
 21317  
 21318    var limitForce = function limitForce(forceX, forceY, max) {
 21319      // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
 21320      var force = Math.sqrt(forceX * forceX + forceY * forceY);
 21321  
 21322      if (force > max) {
 21323        var res = {
 21324          x: max * forceX / force,
 21325          y: max * forceY / force
 21326        };
 21327      } else {
 21328        var res = {
 21329          x: forceX,
 21330          y: forceY
 21331        };
 21332      } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
 21333      // logDebug(s);
 21334  
 21335  
 21336      return res;
 21337    };
 21338    /**
 21339     * @brief : Function used for keeping track of compound node
 21340     *          sizes, since they should bound all their subnodes.
 21341     */
 21342  
 21343  
 21344    var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
 21345      // var s = "Propagating new position/size of node " + node.id;
 21346      var parentId = node.parentId;
 21347  
 21348      if (null == parentId) {
 21349        // If there's no parent, we are done
 21350        // s += ". No parent node.";
 21351        // logDebug(s);
 21352        return;
 21353      } // Get Parent Node
 21354  
 21355  
 21356      var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
 21357      var flag = false; // MaxX
 21358  
 21359      if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
 21360        p.maxX = node.maxX + p.padRight;
 21361        flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
 21362      } // MinX
 21363  
 21364  
 21365      if (null == p.minX || node.minX - p.padLeft < p.minX) {
 21366        p.minX = node.minX - p.padLeft;
 21367        flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
 21368      } // MaxY
 21369  
 21370  
 21371      if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
 21372        p.maxY = node.maxY + p.padBottom;
 21373        flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
 21374      } // MinY
 21375  
 21376  
 21377      if (null == p.minY || node.minY - p.padTop < p.minY) {
 21378        p.minY = node.minY - p.padTop;
 21379        flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
 21380      } // If updated boundaries, propagate changes upward
 21381  
 21382  
 21383      if (flag) {
 21384        // logDebug(s);
 21385        return updateAncestryBoundaries(p, layoutInfo);
 21386      } // s += ". No changes in boundaries/position of parent node " + p.id;
 21387      // logDebug(s);
 21388  
 21389  
 21390      return;
 21391    };
 21392  
 21393    var separateComponents = function separateComponents(layoutInfo, options) {
 21394      var nodes = layoutInfo.layoutNodes;
 21395      var components = [];
 21396  
 21397      for (var i = 0; i < nodes.length; i++) {
 21398        var node = nodes[i];
 21399        var cid = node.cmptId;
 21400        var component = components[cid] = components[cid] || [];
 21401        component.push(node);
 21402      }
 21403  
 21404      var totalA = 0;
 21405  
 21406      for (var i = 0; i < components.length; i++) {
 21407        var c = components[i];
 21408  
 21409        if (!c) {
 21410          continue;
 21411        }
 21412  
 21413        c.x1 = Infinity;
 21414        c.x2 = -Infinity;
 21415        c.y1 = Infinity;
 21416        c.y2 = -Infinity;
 21417  
 21418        for (var j = 0; j < c.length; j++) {
 21419          var n = c[j];
 21420          c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
 21421          c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
 21422          c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
 21423          c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
 21424        }
 21425  
 21426        c.w = c.x2 - c.x1;
 21427        c.h = c.y2 - c.y1;
 21428        totalA += c.w * c.h;
 21429      }
 21430  
 21431      components.sort(function (c1, c2) {
 21432        return c2.w * c2.h - c1.w * c1.h;
 21433      });
 21434      var x = 0;
 21435      var y = 0;
 21436      var usedW = 0;
 21437      var rowH = 0;
 21438      var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
 21439  
 21440      for (var i = 0; i < components.length; i++) {
 21441        var c = components[i];
 21442  
 21443        if (!c) {
 21444          continue;
 21445        }
 21446  
 21447        for (var j = 0; j < c.length; j++) {
 21448          var n = c[j];
 21449  
 21450          if (!n.isLocked) {
 21451            n.positionX += x - c.x1;
 21452            n.positionY += y - c.y1;
 21453          }
 21454        }
 21455  
 21456        x += c.w + options.componentSpacing;
 21457        usedW += c.w + options.componentSpacing;
 21458        rowH = Math.max(rowH, c.h);
 21459  
 21460        if (usedW > maxRowW) {
 21461          y += rowH + options.componentSpacing;
 21462          x = 0;
 21463          usedW = 0;
 21464          rowH = 0;
 21465        }
 21466      }
 21467    };
 21468  
 21469    var defaults$d = {
 21470      fit: true,
 21471      // whether to fit the viewport to the graph
 21472      padding: 30,
 21473      // padding used on fit
 21474      boundingBox: undefined,
 21475      // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 21476      avoidOverlap: true,
 21477      // prevents node overlap, may overflow boundingBox if not enough space
 21478      avoidOverlapPadding: 10,
 21479      // extra spacing around nodes when avoidOverlap: true
 21480      nodeDimensionsIncludeLabels: false,
 21481      // Excludes the label when calculating node bounding boxes for the layout algorithm
 21482      spacingFactor: undefined,
 21483      // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
 21484      condense: false,
 21485      // uses all available space on false, uses minimal space on true
 21486      rows: undefined,
 21487      // force num of rows in the grid
 21488      cols: undefined,
 21489      // force num of columns in the grid
 21490      position: function position(node) {},
 21491      // returns { row, col } for element
 21492      sort: undefined,
 21493      // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
 21494      animate: false,
 21495      // whether to transition the node positions
 21496      animationDuration: 500,
 21497      // duration of animation in ms if enabled
 21498      animationEasing: undefined,
 21499      // easing of animation if enabled
 21500      animateFilter: function animateFilter(node, i) {
 21501        return true;
 21502      },
 21503      // 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
 21504      ready: undefined,
 21505      // callback on layoutready
 21506      stop: undefined,
 21507      // callback on layoutstop
 21508      transform: function transform(node, position) {
 21509        return position;
 21510      } // transform a given node position. Useful for changing flow direction in discrete layouts 
 21511  
 21512    };
 21513  
 21514    function GridLayout(options) {
 21515      this.options = extend({}, defaults$d, options);
 21516    }
 21517  
 21518    GridLayout.prototype.run = function () {
 21519      var params = this.options;
 21520      var options = params;
 21521      var cy = params.cy;
 21522      var eles = options.eles;
 21523      var nodes = eles.nodes().not(':parent');
 21524  
 21525      if (options.sort) {
 21526        nodes = nodes.sort(options.sort);
 21527      }
 21528  
 21529      var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
 21530        x1: 0,
 21531        y1: 0,
 21532        w: cy.width(),
 21533        h: cy.height()
 21534      });
 21535  
 21536      if (bb.h === 0 || bb.w === 0) {
 21537        nodes.layoutPositions(this, options, function (ele) {
 21538          return {
 21539            x: bb.x1,
 21540            y: bb.y1
 21541          };
 21542        });
 21543      } else {
 21544        // width/height * splits^2 = cells where splits is number of times to split width
 21545        var cells = nodes.size();
 21546        var splits = Math.sqrt(cells * bb.h / bb.w);
 21547        var rows = Math.round(splits);
 21548        var cols = Math.round(bb.w / bb.h * splits);
 21549  
 21550        var small = function small(val) {
 21551          if (val == null) {
 21552            return Math.min(rows, cols);
 21553          } else {
 21554            var min = Math.min(rows, cols);
 21555  
 21556            if (min == rows) {
 21557              rows = val;
 21558            } else {
 21559              cols = val;
 21560            }
 21561          }
 21562        };
 21563  
 21564        var large = function large(val) {
 21565          if (val == null) {
 21566            return Math.max(rows, cols);
 21567          } else {
 21568            var max = Math.max(rows, cols);
 21569  
 21570            if (max == rows) {
 21571              rows = val;
 21572            } else {
 21573              cols = val;
 21574            }
 21575          }
 21576        };
 21577  
 21578        var oRows = options.rows;
 21579        var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
 21580  
 21581        if (oRows != null && oCols != null) {
 21582          rows = oRows;
 21583          cols = oCols;
 21584        } else if (oRows != null && oCols == null) {
 21585          rows = oRows;
 21586          cols = Math.ceil(cells / rows);
 21587        } else if (oRows == null && oCols != null) {
 21588          cols = oCols;
 21589          rows = Math.ceil(cells / cols);
 21590        } // otherwise use the automatic values and adjust accordingly
 21591        // if rounding was up, see if we can reduce rows or columns
 21592        else if (cols * rows > cells) {
 21593            var sm = small();
 21594            var lg = large(); // reducing the small side takes away the most cells, so try it first
 21595  
 21596            if ((sm - 1) * lg >= cells) {
 21597              small(sm - 1);
 21598            } else if ((lg - 1) * sm >= cells) {
 21599              large(lg - 1);
 21600            }
 21601          } else {
 21602            // if rounding was too low, add rows or columns
 21603            while (cols * rows < cells) {
 21604              var _sm = small();
 21605  
 21606              var _lg = large(); // try to add to larger side first (adds less in multiplication)
 21607  
 21608  
 21609              if ((_lg + 1) * _sm >= cells) {
 21610                large(_lg + 1);
 21611              } else {
 21612                small(_sm + 1);
 21613              }
 21614            }
 21615          }
 21616  
 21617        var cellWidth = bb.w / cols;
 21618        var cellHeight = bb.h / rows;
 21619  
 21620        if (options.condense) {
 21621          cellWidth = 0;
 21622          cellHeight = 0;
 21623        }
 21624  
 21625        if (options.avoidOverlap) {
 21626          for (var i = 0; i < nodes.length; i++) {
 21627            var node = nodes[i];
 21628            var pos = node._private.position;
 21629  
 21630            if (pos.x == null || pos.y == null) {
 21631              // for bb
 21632              pos.x = 0;
 21633              pos.y = 0;
 21634            }
 21635  
 21636            var nbb = node.layoutDimensions(options);
 21637            var p = options.avoidOverlapPadding;
 21638            var w = nbb.w + p;
 21639            var h = nbb.h + p;
 21640            cellWidth = Math.max(cellWidth, w);
 21641            cellHeight = Math.max(cellHeight, h);
 21642          }
 21643        }
 21644  
 21645        var cellUsed = {}; // e.g. 'c-0-2' => true
 21646  
 21647        var used = function used(row, col) {
 21648          return cellUsed['c-' + row + '-' + col] ? true : false;
 21649        };
 21650  
 21651        var use = function use(row, col) {
 21652          cellUsed['c-' + row + '-' + col] = true;
 21653        }; // to keep track of current cell position
 21654  
 21655  
 21656        var row = 0;
 21657        var col = 0;
 21658  
 21659        var moveToNextCell = function moveToNextCell() {
 21660          col++;
 21661  
 21662          if (col >= cols) {
 21663            col = 0;
 21664            row++;
 21665          }
 21666        }; // get a cache of all the manual positions
 21667  
 21668  
 21669        var id2manPos = {};
 21670  
 21671        for (var _i = 0; _i < nodes.length; _i++) {
 21672          var _node = nodes[_i];
 21673          var rcPos = options.position(_node);
 21674  
 21675          if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
 21676            // must have at least row or col def'd
 21677            var _pos = {
 21678              row: rcPos.row,
 21679              col: rcPos.col
 21680            };
 21681  
 21682            if (_pos.col === undefined) {
 21683              // find unused col
 21684              _pos.col = 0;
 21685  
 21686              while (used(_pos.row, _pos.col)) {
 21687                _pos.col++;
 21688              }
 21689            } else if (_pos.row === undefined) {
 21690              // find unused row
 21691              _pos.row = 0;
 21692  
 21693              while (used(_pos.row, _pos.col)) {
 21694                _pos.row++;
 21695              }
 21696            }
 21697  
 21698            id2manPos[_node.id()] = _pos;
 21699            use(_pos.row, _pos.col);
 21700          }
 21701        }
 21702  
 21703        var getPos = function getPos(element, i) {
 21704          var x, y;
 21705  
 21706          if (element.locked() || element.isParent()) {
 21707            return false;
 21708          } // see if we have a manual position set
 21709  
 21710  
 21711          var rcPos = id2manPos[element.id()];
 21712  
 21713          if (rcPos) {
 21714            x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
 21715            y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
 21716          } else {
 21717            // otherwise set automatically
 21718            while (used(row, col)) {
 21719              moveToNextCell();
 21720            }
 21721  
 21722            x = col * cellWidth + cellWidth / 2 + bb.x1;
 21723            y = row * cellHeight + cellHeight / 2 + bb.y1;
 21724            use(row, col);
 21725            moveToNextCell();
 21726          }
 21727  
 21728          return {
 21729            x: x,
 21730            y: y
 21731          };
 21732        };
 21733  
 21734        nodes.layoutPositions(this, options, getPos);
 21735      }
 21736  
 21737      return this; // chaining
 21738    };
 21739  
 21740    var defaults$e = {
 21741      ready: function ready() {},
 21742      // on layoutready
 21743      stop: function stop() {} // on layoutstop
 21744  
 21745    }; // constructor
 21746    // options : object containing layout options
 21747  
 21748    function NullLayout(options) {
 21749      this.options = extend({}, defaults$e, options);
 21750    } // runs the layout
 21751  
 21752  
 21753    NullLayout.prototype.run = function () {
 21754      var options = this.options;
 21755      var eles = options.eles; // elements to consider in the layout
 21756  
 21757      var layout = this; // cy is automatically populated for us in the constructor
 21758      // (disable eslint for next line as this serves as example layout code to external developers)
 21759      // eslint-disable-next-line no-unused-vars
 21760  
 21761      var cy = options.cy;
 21762      layout.emit('layoutstart'); // puts all nodes at (0, 0)
 21763      // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
 21764  
 21765      eles.nodes().positions(function () {
 21766        return {
 21767          x: 0,
 21768          y: 0
 21769        };
 21770      }); // trigger layoutready when each node has had its position set at least once
 21771  
 21772      layout.one('layoutready', options.ready);
 21773      layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
 21774  
 21775      layout.one('layoutstop', options.stop);
 21776      layout.emit('layoutstop');
 21777      return this; // chaining
 21778    }; // called on continuous layouts to stop them before they finish
 21779  
 21780  
 21781    NullLayout.prototype.stop = function () {
 21782      return this; // chaining
 21783    };
 21784  
 21785    var defaults$f = {
 21786      positions: undefined,
 21787      // map of (node id) => (position obj); or function(node){ return somPos; }
 21788      zoom: undefined,
 21789      // the zoom level to set (prob want fit = false if set)
 21790      pan: undefined,
 21791      // the pan level to set (prob want fit = false if set)
 21792      fit: true,
 21793      // whether to fit to viewport
 21794      padding: 30,
 21795      // padding on fit
 21796      animate: false,
 21797      // whether to transition the node positions
 21798      animationDuration: 500,
 21799      // duration of animation in ms if enabled
 21800      animationEasing: undefined,
 21801      // easing of animation if enabled
 21802      animateFilter: function animateFilter(node, i) {
 21803        return true;
 21804      },
 21805      // 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
 21806      ready: undefined,
 21807      // callback on layoutready
 21808      stop: undefined,
 21809      // callback on layoutstop
 21810      transform: function transform(node, position) {
 21811        return position;
 21812      } // transform a given node position. Useful for changing flow direction in discrete layouts
 21813  
 21814    };
 21815  
 21816    function PresetLayout(options) {
 21817      this.options = extend({}, defaults$f, options);
 21818    }
 21819  
 21820    PresetLayout.prototype.run = function () {
 21821      var options = this.options;
 21822      var eles = options.eles;
 21823      var nodes = eles.nodes();
 21824      var posIsFn = fn(options.positions);
 21825  
 21826      function getPosition(node) {
 21827        if (options.positions == null) {
 21828          return copyPosition(node.position());
 21829        }
 21830  
 21831        if (posIsFn) {
 21832          return options.positions(node);
 21833        }
 21834  
 21835        var pos = options.positions[node._private.data.id];
 21836  
 21837        if (pos == null) {
 21838          return null;
 21839        }
 21840  
 21841        return pos;
 21842      }
 21843  
 21844      nodes.layoutPositions(this, options, function (node, i) {
 21845        var position = getPosition(node);
 21846  
 21847        if (node.locked() || position == null) {
 21848          return false;
 21849        }
 21850  
 21851        return position;
 21852      });
 21853      return this; // chaining
 21854    };
 21855  
 21856    var defaults$g = {
 21857      fit: true,
 21858      // whether to fit to viewport
 21859      padding: 30,
 21860      // fit padding
 21861      boundingBox: undefined,
 21862      // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
 21863      animate: false,
 21864      // whether to transition the node positions
 21865      animationDuration: 500,
 21866      // duration of animation in ms if enabled
 21867      animationEasing: undefined,
 21868      // easing of animation if enabled
 21869      animateFilter: function animateFilter(node, i) {
 21870        return true;
 21871      },
 21872      // 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
 21873      ready: undefined,
 21874      // callback on layoutready
 21875      stop: undefined,
 21876      // callback on layoutstop
 21877      transform: function transform(node, position) {
 21878        return position;
 21879      } // transform a given node position. Useful for changing flow direction in discrete layouts 
 21880  
 21881    };
 21882  
 21883    function RandomLayout(options) {
 21884      this.options = extend({}, defaults$g, options);
 21885    }
 21886  
 21887    RandomLayout.prototype.run = function () {
 21888      var options = this.options;
 21889      var cy = options.cy;
 21890      var eles = options.eles;
 21891      var nodes = eles.nodes().not(':parent');
 21892      var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
 21893        x1: 0,
 21894        y1: 0,
 21895        w: cy.width(),
 21896        h: cy.height()
 21897      });
 21898  
 21899      var getPos = function getPos(node, i) {
 21900        return {
 21901          x: bb.x1 + Math.round(Math.random() * bb.w),
 21902          y: bb.y1 + Math.round(Math.random() * bb.h)
 21903        };
 21904      };
 21905  
 21906      nodes.layoutPositions(this, options, getPos);
 21907      return this; // chaining
 21908    };
 21909  
 21910    var layout = [{
 21911      name: 'breadthfirst',
 21912      impl: BreadthFirstLayout
 21913    }, {
 21914      name: 'circle',
 21915      impl: CircleLayout
 21916    }, {
 21917      name: 'concentric',
 21918      impl: ConcentricLayout
 21919    }, {
 21920      name: 'cose',
 21921      impl: CoseLayout
 21922    }, {
 21923      name: 'grid',
 21924      impl: GridLayout
 21925    }, {
 21926      name: 'null',
 21927      impl: NullLayout
 21928    }, {
 21929      name: 'preset',
 21930      impl: PresetLayout
 21931    }, {
 21932      name: 'random',
 21933      impl: RandomLayout
 21934    }];
 21935  
 21936    function NullRenderer(options) {
 21937      this.options = options;
 21938      this.notifications = 0; // for testing
 21939    }
 21940  
 21941    var noop$1 = function noop() {};
 21942  
 21943    var throwImgErr = function throwImgErr() {
 21944      throw new Error('A headless instance can not render images');
 21945    };
 21946  
 21947    NullRenderer.prototype = {
 21948      recalculateRenderedStyle: noop$1,
 21949      notify: function notify() {
 21950        this.notifications++;
 21951      },
 21952      init: noop$1,
 21953      isHeadless: function isHeadless() {
 21954        return true;
 21955      },
 21956      png: throwImgErr,
 21957      jpg: throwImgErr
 21958    };
 21959  
 21960    var BRp = {};
 21961    BRp.arrowShapeWidth = 0.3;
 21962  
 21963    BRp.registerArrowShapes = function () {
 21964      var arrowShapes = this.arrowShapes = {};
 21965      var renderer = this; // Contract for arrow shapes:
 21966      // 0, 0 is arrow tip
 21967      // (0, 1) is direction towards node
 21968      // (1, 0) is right
 21969      //
 21970      // functional api:
 21971      // collide: check x, y in shape
 21972      // roughCollide: called before collide, no false negatives
 21973      // draw: draw
 21974      // spacing: dist(arrowTip, nodeBoundary)
 21975      // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
 21976  
 21977      var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
 21978        var x1 = translation.x - size / 2 - padding;
 21979        var x2 = translation.x + size / 2 + padding;
 21980        var y1 = translation.y - size / 2 - padding;
 21981        var y2 = translation.y + size / 2 + padding;
 21982        var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
 21983        return inside;
 21984      };
 21985  
 21986      var transform = function transform(x, y, size, angle, translation) {
 21987        var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
 21988        var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
 21989        var xScaled = xRotated * size;
 21990        var yScaled = yRotated * size;
 21991        var xTranslated = xScaled + translation.x;
 21992        var yTranslated = yScaled + translation.y;
 21993        return {
 21994          x: xTranslated,
 21995          y: yTranslated
 21996        };
 21997      };
 21998  
 21999      var transformPoints = function transformPoints(pts, size, angle, translation) {
 22000        var retPts = [];
 22001  
 22002        for (var i = 0; i < pts.length; i += 2) {
 22003          var x = pts[i];
 22004          var y = pts[i + 1];
 22005          retPts.push(transform(x, y, size, angle, translation));
 22006        }
 22007  
 22008        return retPts;
 22009      };
 22010  
 22011      var pointsToArr = function pointsToArr(pts) {
 22012        var ret = [];
 22013  
 22014        for (var i = 0; i < pts.length; i++) {
 22015          var p = pts[i];
 22016          ret.push(p.x, p.y);
 22017        }
 22018  
 22019        return ret;
 22020      };
 22021  
 22022      var standardGap = function standardGap(edge) {
 22023        return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
 22024      };
 22025  
 22026      var defineArrowShape = function defineArrowShape(name, defn) {
 22027        if (string(defn)) {
 22028          defn = arrowShapes[defn];
 22029        }
 22030  
 22031        arrowShapes[name] = extend({
 22032          name: name,
 22033          points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
 22034          collide: function collide(x, y, size, angle, translation, padding) {
 22035            var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
 22036            var inside = pointInsidePolygonPoints(x, y, points);
 22037            return inside;
 22038          },
 22039          roughCollide: bbCollide,
 22040          draw: function draw(context, size, angle, translation) {
 22041            var points = transformPoints(this.points, size, angle, translation);
 22042            renderer.arrowShapeImpl('polygon')(context, points);
 22043          },
 22044          spacing: function spacing(edge) {
 22045            return 0;
 22046          },
 22047          gap: standardGap
 22048        }, defn);
 22049      };
 22050  
 22051      defineArrowShape('none', {
 22052        collide: falsify,
 22053        roughCollide: falsify,
 22054        draw: noop,
 22055        spacing: zeroify,
 22056        gap: zeroify
 22057      });
 22058      defineArrowShape('triangle', {
 22059        points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
 22060      });
 22061      defineArrowShape('arrow', 'triangle');
 22062      defineArrowShape('triangle-backcurve', {
 22063        points: arrowShapes['triangle'].points,
 22064        controlPoint: [0, -0.15],
 22065        roughCollide: bbCollide,
 22066        draw: function draw(context, size, angle, translation, edgeWidth) {
 22067          var ptsTrans = transformPoints(this.points, size, angle, translation);
 22068          var ctrlPt = this.controlPoint;
 22069          var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
 22070          renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
 22071        },
 22072        gap: function gap(edge) {
 22073          return standardGap(edge) * 0.8;
 22074        }
 22075      });
 22076      defineArrowShape('triangle-tee', {
 22077        points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
 22078        pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
 22079        collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
 22080          var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
 22081          var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
 22082          var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
 22083          return inside;
 22084        },
 22085        draw: function draw(context, size, angle, translation, edgeWidth) {
 22086          var triPts = transformPoints(this.points, size, angle, translation);
 22087          var teePts = transformPoints(this.pointsTee, size, angle, translation);
 22088          renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
 22089        }
 22090      });
 22091      defineArrowShape('triangle-cross', {
 22092        points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
 22093        baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
 22094        -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
 22095        0.15, -0.4],
 22096        crossLinePts: function crossLinePts(size, edgeWidth) {
 22097          // shift points so that the distance between the cross points matches edge width
 22098          var p = this.baseCrossLinePts.slice();
 22099          var shiftFactor = edgeWidth / size;
 22100          var y0 = 3;
 22101          var y1 = 5;
 22102          p[y0] = p[y0] - shiftFactor;
 22103          p[y1] = p[y1] - shiftFactor;
 22104          return p;
 22105        },
 22106        collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
 22107          var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
 22108          var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
 22109          var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
 22110          return inside;
 22111        },
 22112        draw: function draw(context, size, angle, translation, edgeWidth) {
 22113          var triPts = transformPoints(this.points, size, angle, translation);
 22114          var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
 22115          renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
 22116        }
 22117      });
 22118      defineArrowShape('vee', {
 22119        points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
 22120        gap: function gap(edge) {
 22121          return standardGap(edge) * 0.525;
 22122        }
 22123      });
 22124      defineArrowShape('circle', {
 22125        radius: 0.15,
 22126        collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
 22127          var t = translation;
 22128          var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
 22129          return inside;
 22130        },
 22131        draw: function draw(context, size, angle, translation, edgeWidth) {
 22132          renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
 22133        },
 22134        spacing: function spacing(edge) {
 22135          return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
 22136        }
 22137      });
 22138      defineArrowShape('tee', {
 22139        points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
 22140        spacing: function spacing(edge) {
 22141          return 1;
 22142        },
 22143        gap: function gap(edge) {
 22144          return 1;
 22145        }
 22146      });
 22147      defineArrowShape('square', {
 22148        points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
 22149      });
 22150      defineArrowShape('diamond', {
 22151        points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
 22152        gap: function gap(edge) {
 22153          return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
 22154        }
 22155      });
 22156      defineArrowShape('chevron', {
 22157        points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
 22158        gap: function gap(edge) {
 22159          return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
 22160        }
 22161      });
 22162    };
 22163  
 22164    var BRp$1 = {}; // Project mouse
 22165  
 22166    BRp$1.projectIntoViewport = function (clientX, clientY) {
 22167      var cy = this.cy;
 22168      var offsets = this.findContainerClientCoords();
 22169      var offsetLeft = offsets[0];
 22170      var offsetTop = offsets[1];
 22171      var scale = offsets[4];
 22172      var pan = cy.pan();
 22173      var zoom = cy.zoom();
 22174      var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
 22175      var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
 22176      return [x, y];
 22177    };
 22178  
 22179    BRp$1.findContainerClientCoords = function () {
 22180      if (this.containerBB) {
 22181        return this.containerBB;
 22182      }
 22183  
 22184      var container = this.container;
 22185      var rect = container.getBoundingClientRect();
 22186      var style = window$1.getComputedStyle(container);
 22187  
 22188      var styleValue = function styleValue(name) {
 22189        return parseFloat(style.getPropertyValue(name));
 22190      };
 22191  
 22192      var padding = {
 22193        left: styleValue('padding-left'),
 22194        right: styleValue('padding-right'),
 22195        top: styleValue('padding-top'),
 22196        bottom: styleValue('padding-bottom')
 22197      };
 22198      var border = {
 22199        left: styleValue('border-left-width'),
 22200        right: styleValue('border-right-width'),
 22201        top: styleValue('border-top-width'),
 22202        bottom: styleValue('border-bottom-width')
 22203      };
 22204      var clientWidth = container.clientWidth;
 22205      var clientHeight = container.clientHeight;
 22206      var paddingHor = padding.left + padding.right;
 22207      var paddingVer = padding.top + padding.bottom;
 22208      var borderHor = border.left + border.right;
 22209      var scale = rect.width / (clientWidth + borderHor);
 22210      var unscaledW = clientWidth - paddingHor;
 22211      var unscaledH = clientHeight - paddingVer;
 22212      var left = rect.left + padding.left + border.left;
 22213      var top = rect.top + padding.top + border.top;
 22214      return this.containerBB = [left, top, unscaledW, unscaledH, scale];
 22215    };
 22216  
 22217    BRp$1.invalidateContainerClientCoordsCache = function () {
 22218      this.containerBB = null;
 22219    };
 22220  
 22221    BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
 22222      return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
 22223    };
 22224  
 22225    BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
 22226      var self = this;
 22227      var r = this;
 22228      var eles = r.getCachedZSortedEles();
 22229      var near = []; // 1 node max, 1 edge max
 22230  
 22231      var zoom = r.cy.zoom();
 22232      var hasCompounds = r.cy.hasCompoundNodes();
 22233      var edgeThreshold = (isTouch ? 24 : 8) / zoom;
 22234      var nodeThreshold = (isTouch ? 8 : 2) / zoom;
 22235      var labelThreshold = (isTouch ? 8 : 2) / zoom;
 22236      var minSqDist = Infinity;
 22237      var nearEdge;
 22238      var nearNode;
 22239  
 22240      if (interactiveElementsOnly) {
 22241        eles = eles.interactive;
 22242      }
 22243  
 22244      function addEle(ele, sqDist) {
 22245        if (ele.isNode()) {
 22246          if (nearNode) {
 22247            return; // can't replace node
 22248          } else {
 22249            nearNode = ele;
 22250            near.push(ele);
 22251          }
 22252        }
 22253  
 22254        if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
 22255          if (nearEdge) {
 22256            // then replace existing edge
 22257            // can replace only if same z-index
 22258            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) {
 22259              for (var i = 0; i < near.length; i++) {
 22260                if (near[i].isEdge()) {
 22261                  near[i] = ele;
 22262                  nearEdge = ele;
 22263                  minSqDist = sqDist != null ? sqDist : minSqDist;
 22264                  break;
 22265                }
 22266              }
 22267            }
 22268          } else {
 22269            near.push(ele);
 22270            nearEdge = ele;
 22271            minSqDist = sqDist != null ? sqDist : minSqDist;
 22272          }
 22273        }
 22274      }
 22275  
 22276      function checkNode(node) {
 22277        var width = node.outerWidth() + 2 * nodeThreshold;
 22278        var height = node.outerHeight() + 2 * nodeThreshold;
 22279        var hw = width / 2;
 22280        var hh = height / 2;
 22281        var pos = node.position();
 22282  
 22283        if (pos.x - hw <= x && x <= pos.x + hw // bb check x
 22284        && pos.y - hh <= y && y <= pos.y + hh // bb check y
 22285        ) {
 22286            var shape = r.nodeShapes[self.getNodeShape(node)];
 22287  
 22288            if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
 22289              addEle(node, 0);
 22290              return true;
 22291            }
 22292          }
 22293      }
 22294  
 22295      function checkEdge(edge) {
 22296        var _p = edge._private;
 22297        var rs = _p.rscratch;
 22298        var styleWidth = edge.pstyle('width').pfValue;
 22299        var scale = edge.pstyle('arrow-scale').value;
 22300        var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
 22301  
 22302        var widthSq = width * width;
 22303        var width2 = width * 2;
 22304        var src = _p.source;
 22305        var tgt = _p.target;
 22306        var sqDist;
 22307  
 22308        if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
 22309          var pts = rs.allpts;
 22310  
 22311          for (var i = 0; i + 3 < pts.length; i += 2) {
 22312            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]))) {
 22313              addEle(edge, sqDist);
 22314              return true;
 22315            }
 22316          }
 22317        } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
 22318          var pts = rs.allpts;
 22319  
 22320          for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
 22321            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]))) {
 22322              addEle(edge, sqDist);
 22323              return true;
 22324            }
 22325          }
 22326        } // if we're close to the edge but didn't hit it, maybe we hit its arrows
 22327  
 22328  
 22329        var src = src || _p.source;
 22330        var tgt = tgt || _p.target;
 22331        var arSize = self.getArrowWidth(styleWidth, scale);
 22332        var arrows = [{
 22333          name: 'source',
 22334          x: rs.arrowStartX,
 22335          y: rs.arrowStartY,
 22336          angle: rs.srcArrowAngle
 22337        }, {
 22338          name: 'target',
 22339          x: rs.arrowEndX,
 22340          y: rs.arrowEndY,
 22341          angle: rs.tgtArrowAngle
 22342        }, {
 22343          name: 'mid-source',
 22344          x: rs.midX,
 22345          y: rs.midY,
 22346          angle: rs.midsrcArrowAngle
 22347        }, {
 22348          name: 'mid-target',
 22349          x: rs.midX,
 22350          y: rs.midY,
 22351          angle: rs.midtgtArrowAngle
 22352        }];
 22353  
 22354        for (var i = 0; i < arrows.length; i++) {
 22355          var ar = arrows[i];
 22356          var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
 22357          var edgeWidth = edge.pstyle('width').pfValue;
 22358  
 22359          if (shape.roughCollide(x, y, arSize, ar.angle, {
 22360            x: ar.x,
 22361            y: ar.y
 22362          }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
 22363            x: ar.x,
 22364            y: ar.y
 22365          }, edgeWidth, edgeThreshold)) {
 22366            addEle(edge);
 22367            return true;
 22368          }
 22369        } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
 22370  
 22371  
 22372        if (hasCompounds && near.length > 0) {
 22373          checkNode(src);
 22374          checkNode(tgt);
 22375        }
 22376      }
 22377  
 22378      function preprop(obj, name, pre) {
 22379        return getPrefixedProperty(obj, name, pre);
 22380      }
 22381  
 22382      function checkLabel(ele, prefix) {
 22383        var _p = ele._private;
 22384        var th = labelThreshold;
 22385        var prefixDash;
 22386  
 22387        if (prefix) {
 22388          prefixDash = prefix + '-';
 22389        } else {
 22390          prefixDash = '';
 22391        }
 22392  
 22393        ele.boundingBox();
 22394        var bb = _p.labelBounds[prefix || 'main'];
 22395        var text = ele.pstyle(prefixDash + 'label').value;
 22396        var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
 22397  
 22398        if (!eventsEnabled || !text) {
 22399          return;
 22400        }
 22401  
 22402        var rstyle = _p.rstyle;
 22403        var lx = preprop(rstyle, 'labelX', prefix);
 22404        var ly = preprop(rstyle, 'labelY', prefix);
 22405        var theta = preprop(_p.rscratch, 'labelAngle', prefix);
 22406        var lx1 = bb.x1 - th;
 22407        var lx2 = bb.x2 + th;
 22408        var ly1 = bb.y1 - th;
 22409        var ly2 = bb.y2 + th;
 22410  
 22411        if (theta) {
 22412          var cos = Math.cos(theta);
 22413          var sin = Math.sin(theta);
 22414  
 22415          var rotate = function rotate(x, y) {
 22416            x = x - lx;
 22417            y = y - ly;
 22418            return {
 22419              x: x * cos - y * sin + lx,
 22420              y: x * sin + y * cos + ly
 22421            };
 22422          };
 22423  
 22424          var px1y1 = rotate(lx1, ly1);
 22425          var px1y2 = rotate(lx1, ly2);
 22426          var px2y1 = rotate(lx2, ly1);
 22427          var px2y2 = rotate(lx2, ly2);
 22428          var points = [px1y1.x, px1y1.y, px2y1.x, px2y1.y, px2y2.x, px2y2.y, px1y2.x, px1y2.y];
 22429  
 22430          if (pointInsidePolygonPoints(x, y, points)) {
 22431            addEle(ele);
 22432            return true;
 22433          }
 22434        } else {
 22435          // do a cheaper bb check
 22436          if (inBoundingBox(bb, x, y)) {
 22437            addEle(ele);
 22438            return true;
 22439          }
 22440        }
 22441      }
 22442  
 22443      for (var i = eles.length - 1; i >= 0; i--) {
 22444        // reverse order for precedence
 22445        var ele = eles[i];
 22446  
 22447        if (ele.isNode()) {
 22448          checkNode(ele) || checkLabel(ele);
 22449        } else {
 22450          // then edge
 22451          checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
 22452        }
 22453      }
 22454  
 22455      return near;
 22456    }; // 'Give me everything from this box'
 22457  
 22458  
 22459    BRp$1.getAllInBox = function (x1, y1, x2, y2) {
 22460      var eles = this.getCachedZSortedEles().interactive;
 22461      var box = [];
 22462      var x1c = Math.min(x1, x2);
 22463      var x2c = Math.max(x1, x2);
 22464      var y1c = Math.min(y1, y2);
 22465      var y2c = Math.max(y1, y2);
 22466      x1 = x1c;
 22467      x2 = x2c;
 22468      y1 = y1c;
 22469      y2 = y2c;
 22470      var boxBb = makeBoundingBox({
 22471        x1: x1,
 22472        y1: y1,
 22473        x2: x2,
 22474        y2: y2
 22475      });
 22476  
 22477      for (var e = 0; e < eles.length; e++) {
 22478        var ele = eles[e];
 22479  
 22480        if (ele.isNode()) {
 22481          var node = ele;
 22482          var nodeBb = node.boundingBox({
 22483            includeNodes: true,
 22484            includeEdges: false,
 22485            includeLabels: false
 22486          });
 22487  
 22488          if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
 22489            box.push(node);
 22490          }
 22491        } else {
 22492          var edge = ele;
 22493          var _p = edge._private;
 22494          var rs = _p.rscratch;
 22495  
 22496          if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
 22497            continue;
 22498          }
 22499  
 22500          if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
 22501            continue;
 22502          }
 22503  
 22504          if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
 22505            var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
 22506            var allInside = true;
 22507  
 22508            for (var i = 0; i < pts.length; i++) {
 22509              if (!pointInBoundingBox(boxBb, pts[i])) {
 22510                allInside = false;
 22511                break;
 22512              }
 22513            }
 22514  
 22515            if (allInside) {
 22516              box.push(edge);
 22517            }
 22518          } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
 22519            box.push(edge);
 22520          }
 22521        }
 22522      }
 22523  
 22524      return box;
 22525    };
 22526  
 22527    var BRp$2 = {};
 22528  
 22529    BRp$2.calculateArrowAngles = function (edge) {
 22530      var rs = edge._private.rscratch;
 22531      var isHaystack = rs.edgeType === 'haystack';
 22532      var isBezier = rs.edgeType === 'bezier';
 22533      var isMultibezier = rs.edgeType === 'multibezier';
 22534      var isSegments = rs.edgeType === 'segments';
 22535      var isCompound = rs.edgeType === 'compound';
 22536      var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
 22537  
 22538      var dispX, dispY;
 22539      var startX, startY, endX, endY, midX, midY;
 22540  
 22541      if (isHaystack) {
 22542        startX = rs.haystackPts[0];
 22543        startY = rs.haystackPts[1];
 22544        endX = rs.haystackPts[2];
 22545        endY = rs.haystackPts[3];
 22546      } else {
 22547        startX = rs.arrowStartX;
 22548        startY = rs.arrowStartY;
 22549        endX = rs.arrowEndX;
 22550        endY = rs.arrowEndY;
 22551      }
 22552  
 22553      midX = rs.midX;
 22554      midY = rs.midY; // source
 22555      //
 22556  
 22557      if (isSegments) {
 22558        dispX = startX - rs.segpts[0];
 22559        dispY = startY - rs.segpts[1];
 22560      } else if (isMultibezier || isCompound || isSelf || isBezier) {
 22561        var pts = rs.allpts;
 22562        var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
 22563        var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
 22564        dispX = startX - bX;
 22565        dispY = startY - bY;
 22566      } else {
 22567        dispX = startX - midX;
 22568        dispY = startY - midY;
 22569      }
 22570  
 22571      rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
 22572      //
 22573  
 22574      var midX = rs.midX;
 22575      var midY = rs.midY;
 22576  
 22577      if (isHaystack) {
 22578        midX = (startX + endX) / 2;
 22579        midY = (startY + endY) / 2;
 22580      }
 22581  
 22582      dispX = endX - startX;
 22583      dispY = endY - startY;
 22584  
 22585      if (isSegments) {
 22586        var pts = rs.allpts;
 22587  
 22588        if (pts.length / 2 % 2 === 0) {
 22589          var i2 = pts.length / 2;
 22590          var i1 = i2 - 2;
 22591          dispX = pts[i2] - pts[i1];
 22592          dispY = pts[i2 + 1] - pts[i1 + 1];
 22593        } else {
 22594          var i2 = pts.length / 2 - 1;
 22595          var i1 = i2 - 2;
 22596          var i3 = i2 + 2;
 22597          dispX = pts[i2] - pts[i1];
 22598          dispY = pts[i2 + 1] - pts[i1 + 1];
 22599        }
 22600      } else if (isMultibezier || isCompound || isSelf) {
 22601        var pts = rs.allpts;
 22602        var cpts = rs.ctrlpts;
 22603        var bp0x, bp0y;
 22604        var bp1x, bp1y;
 22605  
 22606        if (cpts.length / 2 % 2 === 0) {
 22607          var p0 = pts.length / 2 - 1; // startpt
 22608  
 22609          var ic = p0 + 2;
 22610          var p1 = ic + 2;
 22611          bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
 22612          bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
 22613          bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
 22614          bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
 22615        } else {
 22616          var ic = pts.length / 2 - 1; // ctrpt
 22617  
 22618          var p0 = ic - 2; // startpt
 22619  
 22620          var p1 = ic + 2; // endpt
 22621  
 22622          bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
 22623          bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
 22624          bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
 22625          bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
 22626        }
 22627  
 22628        dispX = bp1x - bp0x;
 22629        dispY = bp1y - bp0y;
 22630      }
 22631  
 22632      rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
 22633      rs.midDispX = dispX;
 22634      rs.midDispY = dispY; // mid source
 22635      //
 22636  
 22637      dispX *= -1;
 22638      dispY *= -1;
 22639  
 22640      if (isSegments) {
 22641        var pts = rs.allpts;
 22642  
 22643        if (pts.length / 2 % 2 === 0) ; else {
 22644          var i2 = pts.length / 2 - 1;
 22645          var i3 = i2 + 2;
 22646          dispX = -(pts[i3] - pts[i2]);
 22647          dispY = -(pts[i3 + 1] - pts[i2 + 1]);
 22648        }
 22649      }
 22650  
 22651      rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
 22652      //
 22653  
 22654      if (isSegments) {
 22655        dispX = endX - rs.segpts[rs.segpts.length - 2];
 22656        dispY = endY - rs.segpts[rs.segpts.length - 1];
 22657      } else if (isMultibezier || isCompound || isSelf || isBezier) {
 22658        var pts = rs.allpts;
 22659        var l = pts.length;
 22660        var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
 22661        var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
 22662        dispX = endX - bX;
 22663        dispY = endY - bY;
 22664      } else {
 22665        dispX = endX - midX;
 22666        dispY = endY - midY;
 22667      }
 22668  
 22669      rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
 22670    };
 22671  
 22672    BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
 22673      var cache = this.arrowWidthCache = this.arrowWidthCache || {};
 22674      var cachedVal = cache[edgeWidth + ', ' + scale];
 22675  
 22676      if (cachedVal) {
 22677        return cachedVal;
 22678      }
 22679  
 22680      cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
 22681      cache[edgeWidth + ', ' + scale] = cachedVal;
 22682      return cachedVal;
 22683    };
 22684  
 22685    var BRp$3 = {};
 22686  
 22687    BRp$3.findHaystackPoints = function (edges) {
 22688      for (var i = 0; i < edges.length; i++) {
 22689        var edge = edges[i];
 22690        var _p = edge._private;
 22691        var rs = _p.rscratch;
 22692  
 22693        if (!rs.haystack) {
 22694          var angle = Math.random() * 2 * Math.PI;
 22695          rs.source = {
 22696            x: Math.cos(angle),
 22697            y: Math.sin(angle)
 22698          };
 22699          angle = Math.random() * 2 * Math.PI;
 22700          rs.target = {
 22701            x: Math.cos(angle),
 22702            y: Math.sin(angle)
 22703          };
 22704        }
 22705  
 22706        var src = _p.source;
 22707        var tgt = _p.target;
 22708        var srcPos = src.position();
 22709        var tgtPos = tgt.position();
 22710        var srcW = src.width();
 22711        var tgtW = tgt.width();
 22712        var srcH = src.height();
 22713        var tgtH = tgt.height();
 22714        var radius = edge.pstyle('haystack-radius').value;
 22715        var halfRadius = radius / 2; // b/c have to half width/height
 22716  
 22717        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];
 22718        rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
 22719        rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
 22720  
 22721        rs.edgeType = 'haystack';
 22722        rs.haystack = true;
 22723        this.storeEdgeProjections(edge);
 22724        this.calculateArrowAngles(edge);
 22725        this.recalculateEdgeLabelProjections(edge);
 22726        this.calculateLabelAngles(edge);
 22727      }
 22728    };
 22729  
 22730    BRp$3.findSegmentsPoints = function (edge, pairInfo) {
 22731      // Segments (multiple straight lines)
 22732      var rs = edge._private.rscratch;
 22733      var posPts = pairInfo.posPts,
 22734          intersectionPts = pairInfo.intersectionPts,
 22735          vectorNormInverse = pairInfo.vectorNormInverse;
 22736      var edgeDistances = edge.pstyle('edge-distances').value;
 22737      var segmentWs = edge.pstyle('segment-weights');
 22738      var segmentDs = edge.pstyle('segment-distances');
 22739      var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
 22740      rs.edgeType = 'segments';
 22741      rs.segpts = [];
 22742  
 22743      for (var s = 0; s < segmentsN; s++) {
 22744        var w = segmentWs.pfValue[s];
 22745        var d = segmentDs.pfValue[s];
 22746        var w1 = 1 - w;
 22747        var w2 = w;
 22748        var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
 22749        var adjustedMidpt = {
 22750          x: midptPts.x1 * w1 + midptPts.x2 * w2,
 22751          y: midptPts.y1 * w1 + midptPts.y2 * w2
 22752        };
 22753        rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
 22754      }
 22755    };
 22756  
 22757    BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
 22758      // Self-edge
 22759      var rs = edge._private.rscratch;
 22760      var dirCounts = pairInfo.dirCounts,
 22761          srcPos = pairInfo.srcPos;
 22762      var ctrlptDists = edge.pstyle('control-point-distances');
 22763      var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
 22764      var loopDir = edge.pstyle('loop-direction').pfValue;
 22765      var loopSwp = edge.pstyle('loop-sweep').pfValue;
 22766      var stepSize = edge.pstyle('control-point-step-size').pfValue;
 22767      rs.edgeType = 'self';
 22768      var j = i;
 22769      var loopDist = stepSize;
 22770  
 22771      if (edgeIsUnbundled) {
 22772        j = 0;
 22773        loopDist = ctrlptDist;
 22774      }
 22775  
 22776      var loopAngle = loopDir - Math.PI / 2;
 22777      var outAngle = loopAngle - loopSwp / 2;
 22778      var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
 22779  
 22780      var dc = String(loopDir + '_' + loopSwp);
 22781      j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
 22782      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)];
 22783    };
 22784  
 22785    BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
 22786      // Compound edge
 22787      var rs = edge._private.rscratch;
 22788      rs.edgeType = 'compound';
 22789      var srcPos = pairInfo.srcPos,
 22790          tgtPos = pairInfo.tgtPos,
 22791          srcW = pairInfo.srcW,
 22792          srcH = pairInfo.srcH,
 22793          tgtW = pairInfo.tgtW,
 22794          tgtH = pairInfo.tgtH;
 22795      var stepSize = edge.pstyle('control-point-step-size').pfValue;
 22796      var ctrlptDists = edge.pstyle('control-point-distances');
 22797      var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
 22798      var j = i;
 22799      var loopDist = stepSize;
 22800  
 22801      if (edgeIsUnbundled) {
 22802        j = 0;
 22803        loopDist = ctrlptDist;
 22804      }
 22805  
 22806      var loopW = 50;
 22807      var loopaPos = {
 22808        x: srcPos.x - srcW / 2,
 22809        y: srcPos.y - srcH / 2
 22810      };
 22811      var loopbPos = {
 22812        x: tgtPos.x - tgtW / 2,
 22813        y: tgtPos.y - tgtH / 2
 22814      };
 22815      var loopPos = {
 22816        x: Math.min(loopaPos.x, loopbPos.x),
 22817        y: Math.min(loopaPos.y, loopbPos.y)
 22818      }; // avoids cases with impossible beziers
 22819  
 22820      var minCompoundStretch = 0.5;
 22821      var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
 22822      var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
 22823      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];
 22824    };
 22825  
 22826    BRp$3.findStraightEdgePoints = function (edge) {
 22827      // Straight edge within bundle
 22828      edge._private.rscratch.edgeType = 'straight';
 22829    };
 22830  
 22831    BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
 22832      var rs = edge._private.rscratch;
 22833      var vectorNormInverse = pairInfo.vectorNormInverse,
 22834          posPts = pairInfo.posPts,
 22835          intersectionPts = pairInfo.intersectionPts;
 22836      var edgeDistances = edge.pstyle('edge-distances').value;
 22837      var stepSize = edge.pstyle('control-point-step-size').pfValue;
 22838      var ctrlptDists = edge.pstyle('control-point-distances');
 22839      var ctrlptWs = edge.pstyle('control-point-weights');
 22840      var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
 22841      var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
 22842      var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
 22843  
 22844      var multi = edgeIsUnbundled;
 22845      rs.edgeType = multi ? 'multibezier' : 'bezier';
 22846      rs.ctrlpts = [];
 22847  
 22848      for (var b = 0; b < bezierN; b++) {
 22849        var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
 22850        var manctrlptDist = void 0;
 22851        var sign = signum(normctrlptDist);
 22852  
 22853        if (multi) {
 22854          ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
 22855  
 22856          ctrlptWeight = ctrlptWs.value[b];
 22857        }
 22858  
 22859        if (edgeIsUnbundled) {
 22860          // multi or single unbundled
 22861          manctrlptDist = ctrlptDist;
 22862        } else {
 22863          manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
 22864        }
 22865  
 22866        var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
 22867        var w1 = 1 - ctrlptWeight;
 22868        var w2 = ctrlptWeight;
 22869        var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
 22870        var adjustedMidpt = {
 22871          x: midptPts.x1 * w1 + midptPts.x2 * w2,
 22872          y: midptPts.y1 * w1 + midptPts.y2 * w2
 22873        };
 22874        rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
 22875      }
 22876    };
 22877  
 22878    BRp$3.findTaxiPoints = function (edge, pairInfo) {
 22879      // Taxicab geometry with two turns maximum
 22880      var rs = edge._private.rscratch;
 22881      rs.edgeType = 'segments';
 22882      var VERTICAL = 'vertical';
 22883      var HORIZONTAL = 'horizontal';
 22884      var LEFTWARD = 'leftward';
 22885      var RIGHTWARD = 'rightward';
 22886      var DOWNWARD = 'downward';
 22887      var UPWARD = 'upward';
 22888      var AUTO = 'auto';
 22889      var posPts = pairInfo.posPts,
 22890          srcW = pairInfo.srcW,
 22891          srcH = pairInfo.srcH,
 22892          tgtW = pairInfo.tgtW,
 22893          tgtH = pairInfo.tgtH;
 22894      var edgeDistances = edge.pstyle('edge-distances').value;
 22895      var dIncludesNodeBody = edgeDistances !== 'node-position';
 22896      var taxiDir = edge.pstyle('taxi-direction').value;
 22897      var rawTaxiDir = taxiDir; // unprocessed value
 22898  
 22899      var taxiTurn = edge.pstyle('taxi-turn');
 22900      var taxiTurnPfVal = taxiTurn.pfValue;
 22901      var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
 22902      var turnIsPercent = taxiTurn.units === '%';
 22903      var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
 22904      var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
 22905      var pdx = posPts.x2 - posPts.x1;
 22906      var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
 22907  
 22908      var subDWH = function subDWH(dxy, dwh) {
 22909        if (dxy > 0) {
 22910          return Math.max(dxy - dwh, 0);
 22911        } else {
 22912          return Math.min(dxy + dwh, 0);
 22913        }
 22914      };
 22915  
 22916      var dx = subDWH(pdx, dw);
 22917      var dy = subDWH(pdy, dh);
 22918      var isExplicitDir = false;
 22919  
 22920      if (taxiDir === AUTO) {
 22921        taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
 22922      } else if (taxiDir === UPWARD || taxiDir === DOWNWARD) {
 22923        taxiDir = VERTICAL;
 22924        isExplicitDir = true;
 22925      } else if (taxiDir === LEFTWARD || taxiDir === RIGHTWARD) {
 22926        taxiDir = HORIZONTAL;
 22927        isExplicitDir = true;
 22928      }
 22929  
 22930      var isVert = taxiDir === VERTICAL;
 22931      var l = isVert ? dy : dx;
 22932      var pl = isVert ? pdy : pdx;
 22933      var sgnL = signum(pl);
 22934      var forcedDir = false;
 22935  
 22936      if (!(isExplicitDir && turnIsPercent) // forcing in this case would cause weird growing in the opposite direction
 22937      && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
 22938        sgnL *= -1;
 22939        l = sgnL * Math.abs(l);
 22940        forcedDir = true;
 22941      }
 22942  
 22943      var d = turnIsPercent ? taxiTurnPfVal * l : taxiTurnPfVal * sgnL;
 22944  
 22945      var getIsTooClose = function getIsTooClose(d) {
 22946        return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
 22947      };
 22948  
 22949      var isTooCloseSrc = getIsTooClose(d);
 22950      var isTooCloseTgt = getIsTooClose(l - d);
 22951      var isTooClose = isTooCloseSrc || isTooCloseTgt;
 22952  
 22953      if (isTooClose && !forcedDir) {
 22954        // non-ideal routing
 22955        if (isVert) {
 22956          // vertical fallbacks
 22957          var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
 22958          var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
 22959  
 22960          if (lShapeInsideSrc) {
 22961            // horizontal Z-shape (direction not respected)
 22962            var x = (posPts.x1 + posPts.x2) / 2;
 22963            var y1 = posPts.y1,
 22964                y2 = posPts.y2;
 22965            rs.segpts = [x, y1, x, y2];
 22966          } else if (lShapeInsideTgt) {
 22967            // vertical Z-shape (distance not respected)
 22968            var y = (posPts.y1 + posPts.y2) / 2;
 22969            var x1 = posPts.x1,
 22970                x2 = posPts.x2;
 22971            rs.segpts = [x1, y, x2, y];
 22972          } else {
 22973            // L-shape fallback (turn distance not respected, but works well with tree siblings)
 22974            rs.segpts = [posPts.x1, posPts.y2];
 22975          }
 22976        } else {
 22977          // horizontal fallbacks
 22978          var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
 22979  
 22980          var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
 22981  
 22982          if (_lShapeInsideSrc) {
 22983            // vertical Z-shape (direction not respected)
 22984            var _y = (posPts.y1 + posPts.y2) / 2;
 22985  
 22986            var _x = posPts.x1,
 22987                _x2 = posPts.x2;
 22988            rs.segpts = [_x, _y, _x2, _y];
 22989          } else if (_lShapeInsideTgt) {
 22990            // horizontal Z-shape (turn distance not respected)
 22991            var _x3 = (posPts.x1 + posPts.x2) / 2;
 22992  
 22993            var _y2 = posPts.y1,
 22994                _y3 = posPts.y2;
 22995            rs.segpts = [_x3, _y2, _x3, _y3];
 22996          } else {
 22997            // L-shape (turn distance not respected, but works well for tree siblings)
 22998            rs.segpts = [posPts.x2, posPts.y1];
 22999          }
 23000        }
 23001      } else {
 23002        // ideal routing
 23003        if (isVert) {
 23004          var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
 23005  
 23006          var _x4 = posPts.x1,
 23007              _x5 = posPts.x2;
 23008          rs.segpts = [_x4, _y4, _x5, _y4];
 23009        } else {
 23010          // horizontal
 23011          var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
 23012  
 23013          var _y5 = posPts.y1,
 23014              _y6 = posPts.y2;
 23015          rs.segpts = [_x6, _y5, _x6, _y6];
 23016        }
 23017      }
 23018    };
 23019  
 23020    BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
 23021      var rs = edge._private.rscratch; // can only correct beziers for now...
 23022  
 23023      if (rs.edgeType === 'bezier') {
 23024        var srcPos = pairInfo.srcPos,
 23025            tgtPos = pairInfo.tgtPos,
 23026            srcW = pairInfo.srcW,
 23027            srcH = pairInfo.srcH,
 23028            tgtW = pairInfo.tgtW,
 23029            tgtH = pairInfo.tgtH,
 23030            srcShape = pairInfo.srcShape,
 23031            tgtShape = pairInfo.tgtShape;
 23032        var badStart = !number(rs.startX) || !number(rs.startY);
 23033        var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
 23034        var badEnd = !number(rs.endX) || !number(rs.endY);
 23035        var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
 23036        var minCpADistFactor = 3;
 23037        var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
 23038        var minCpADist = minCpADistFactor * arrowW;
 23039        var startACpDist = dist({
 23040          x: rs.ctrlpts[0],
 23041          y: rs.ctrlpts[1]
 23042        }, {
 23043          x: rs.startX,
 23044          y: rs.startY
 23045        });
 23046        var closeStartACp = startACpDist < minCpADist;
 23047        var endACpDist = dist({
 23048          x: rs.ctrlpts[0],
 23049          y: rs.ctrlpts[1]
 23050        }, {
 23051          x: rs.endX,
 23052          y: rs.endY
 23053        });
 23054        var closeEndACp = endACpDist < minCpADist;
 23055        var overlapping = false;
 23056  
 23057        if (badStart || badAStart || closeStartACp) {
 23058          overlapping = true; // project control point along line from src centre to outside the src shape
 23059          // (otherwise intersection will yield nothing)
 23060  
 23061          var cpD = {
 23062            // delta
 23063            x: rs.ctrlpts[0] - srcPos.x,
 23064            y: rs.ctrlpts[1] - srcPos.y
 23065          };
 23066          var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
 23067  
 23068          var cpM = {
 23069            // normalised delta
 23070            x: cpD.x / cpL,
 23071            y: cpD.y / cpL
 23072          };
 23073          var radius = Math.max(srcW, srcH);
 23074          var cpProj = {
 23075            // *2 radius guarantees outside shape
 23076            x: rs.ctrlpts[0] + cpM.x * 2 * radius,
 23077            y: rs.ctrlpts[1] + cpM.y * 2 * radius
 23078          };
 23079          var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
 23080  
 23081          if (closeStartACp) {
 23082            rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
 23083            rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
 23084          } else {
 23085            rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
 23086            rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
 23087          }
 23088        }
 23089  
 23090        if (badEnd || badAEnd || closeEndACp) {
 23091          overlapping = true; // project control point along line from tgt centre to outside the tgt shape
 23092          // (otherwise intersection will yield nothing)
 23093  
 23094          var _cpD = {
 23095            // delta
 23096            x: rs.ctrlpts[0] - tgtPos.x,
 23097            y: rs.ctrlpts[1] - tgtPos.y
 23098          };
 23099  
 23100          var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
 23101  
 23102  
 23103          var _cpM = {
 23104            // normalised delta
 23105            x: _cpD.x / _cpL,
 23106            y: _cpD.y / _cpL
 23107          };
 23108  
 23109          var _radius = Math.max(srcW, srcH);
 23110  
 23111          var _cpProj = {
 23112            // *2 radius guarantees outside shape
 23113            x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
 23114            y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
 23115          };
 23116          var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
 23117  
 23118          if (closeEndACp) {
 23119            rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
 23120            rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
 23121          } else {
 23122            rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
 23123            rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
 23124          }
 23125        }
 23126  
 23127        if (overlapping) {
 23128          // recalc endpts
 23129          this.findEndpoints(edge);
 23130        }
 23131      }
 23132    };
 23133  
 23134    BRp$3.storeAllpts = function (edge) {
 23135      var rs = edge._private.rscratch;
 23136  
 23137      if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
 23138        rs.allpts = [];
 23139        rs.allpts.push(rs.startX, rs.startY);
 23140  
 23141        for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
 23142          // ctrl pt itself
 23143          rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
 23144  
 23145          if (b + 3 < rs.ctrlpts.length) {
 23146            rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
 23147          }
 23148        }
 23149  
 23150        rs.allpts.push(rs.endX, rs.endY);
 23151        var m, mt;
 23152  
 23153        if (rs.ctrlpts.length / 2 % 2 === 0) {
 23154          m = rs.allpts.length / 2 - 1;
 23155          rs.midX = rs.allpts[m];
 23156          rs.midY = rs.allpts[m + 1];
 23157        } else {
 23158          m = rs.allpts.length / 2 - 3;
 23159          mt = 0.5;
 23160          rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
 23161          rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
 23162        }
 23163      } else if (rs.edgeType === 'straight') {
 23164        // need to calc these after endpts
 23165        rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
 23166  
 23167        rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
 23168        rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
 23169      } else if (rs.edgeType === 'segments') {
 23170        rs.allpts = [];
 23171        rs.allpts.push(rs.startX, rs.startY);
 23172        rs.allpts.push.apply(rs.allpts, rs.segpts);
 23173        rs.allpts.push(rs.endX, rs.endY);
 23174  
 23175        if (rs.segpts.length % 4 === 0) {
 23176          var i2 = rs.segpts.length / 2;
 23177          var i1 = i2 - 2;
 23178          rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
 23179          rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
 23180        } else {
 23181          var _i = rs.segpts.length / 2 - 1;
 23182  
 23183          rs.midX = rs.segpts[_i];
 23184          rs.midY = rs.segpts[_i + 1];
 23185        }
 23186      }
 23187    };
 23188  
 23189    BRp$3.checkForInvalidEdgeWarning = function (edge) {
 23190      var rs = edge[0]._private.rscratch;
 23191  
 23192      if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
 23193        rs.loggedErr = false;
 23194      } else {
 23195        if (!rs.loggedErr) {
 23196          rs.loggedErr = true;
 23197          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.');
 23198        }
 23199      }
 23200    };
 23201  
 23202    BRp$3.findEdgeControlPoints = function (edges) {
 23203      var _this = this;
 23204  
 23205      if (!edges || edges.length === 0) {
 23206        return;
 23207      }
 23208  
 23209      var r = this;
 23210      var cy = r.cy;
 23211      var hasCompounds = cy.hasCompoundNodes();
 23212      var hashTable = {
 23213        map: new Map$1(),
 23214        get: function get(pairId) {
 23215          var map2 = this.map.get(pairId[0]);
 23216  
 23217          if (map2 != null) {
 23218            return map2.get(pairId[1]);
 23219          } else {
 23220            return null;
 23221          }
 23222        },
 23223        set: function set(pairId, val) {
 23224          var map2 = this.map.get(pairId[0]);
 23225  
 23226          if (map2 == null) {
 23227            map2 = new Map$1();
 23228            this.map.set(pairId[0], map2);
 23229          }
 23230  
 23231          map2.set(pairId[1], val);
 23232        }
 23233      };
 23234      var pairIds = [];
 23235      var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
 23236  
 23237      for (var i = 0; i < edges.length; i++) {
 23238        var edge = edges[i];
 23239        var _p = edge._private;
 23240        var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
 23241        // they shouldn't take up space
 23242  
 23243        if (edge.removed() || !edge.takesUpSpace()) {
 23244          continue;
 23245        }
 23246  
 23247        if (curveStyle === 'haystack') {
 23248          haystackEdges.push(edge);
 23249          continue;
 23250        }
 23251  
 23252        var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'taxi';
 23253        var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
 23254        var src = _p.source;
 23255        var tgt = _p.target;
 23256        var srcIndex = src.poolIndex();
 23257        var tgtIndex = tgt.poolIndex();
 23258        var pairId = [srcIndex, tgtIndex].sort();
 23259        var tableEntry = hashTable.get(pairId);
 23260  
 23261        if (tableEntry == null) {
 23262          tableEntry = {
 23263            eles: []
 23264          };
 23265          hashTable.set(pairId, tableEntry);
 23266          pairIds.push(pairId);
 23267        }
 23268  
 23269        tableEntry.eles.push(edge);
 23270  
 23271        if (edgeIsUnbundled) {
 23272          tableEntry.hasUnbundled = true;
 23273        }
 23274  
 23275        if (edgeIsBezier) {
 23276          tableEntry.hasBezier = true;
 23277        }
 23278      } // for each pair (src, tgt), create the ctrl pts
 23279      // Nested for loop is OK; total number of iterations for both loops = edgeCount
 23280  
 23281  
 23282      var _loop = function _loop(p) {
 23283        var pairId = pairIds[p];
 23284        var pairInfo = hashTable.get(pairId);
 23285        var swappedpairInfo = void 0;
 23286  
 23287        if (!pairInfo.hasUnbundled) {
 23288          var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
 23289            return e.isBundledBezier();
 23290          });
 23291          clearArray(pairInfo.eles);
 23292          pllEdges.forEach(function (edge) {
 23293            return pairInfo.eles.push(edge);
 23294          }); // for each pair id, the edges should be sorted by index
 23295  
 23296          pairInfo.eles.sort(function (edge1, edge2) {
 23297            return edge1.poolIndex() - edge2.poolIndex();
 23298          });
 23299        }
 23300  
 23301        var firstEdge = pairInfo.eles[0];
 23302        var src = firstEdge.source();
 23303        var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
 23304  
 23305        if (src.poolIndex() > tgt.poolIndex()) {
 23306          var temp = src;
 23307          src = tgt;
 23308          tgt = temp;
 23309        }
 23310  
 23311        var srcPos = pairInfo.srcPos = src.position();
 23312        var tgtPos = pairInfo.tgtPos = tgt.position();
 23313        var srcW = pairInfo.srcW = src.outerWidth();
 23314        var srcH = pairInfo.srcH = src.outerHeight();
 23315        var tgtW = pairInfo.tgtW = tgt.outerWidth();
 23316        var tgtH = pairInfo.tgtH = tgt.outerHeight();
 23317  
 23318        var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
 23319  
 23320        var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
 23321  
 23322        pairInfo.dirCounts = {
 23323          'north': 0,
 23324          'west': 0,
 23325          'south': 0,
 23326          'east': 0,
 23327          'northwest': 0,
 23328          'southwest': 0,
 23329          'northeast': 0,
 23330          'southeast': 0
 23331        };
 23332  
 23333        for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
 23334          var _edge = pairInfo.eles[_i2];
 23335          var rs = _edge[0]._private.rscratch;
 23336  
 23337          var _curveStyle = _edge.pstyle('curve-style').value;
 23338  
 23339          var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
 23340  
 23341  
 23342          var edgeIsSwapped = !src.same(_edge.source());
 23343  
 23344          if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
 23345            pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
 23346  
 23347            var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
 23348            var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
 23349  
 23350            var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
 23351            var tgtIntn = pairInfo.tgtIntn = tgtOutside;
 23352            var intersectionPts = pairInfo.intersectionPts = {
 23353              x1: srcOutside[0],
 23354              x2: tgtOutside[0],
 23355              y1: srcOutside[1],
 23356              y2: tgtOutside[1]
 23357            };
 23358            var posPts = pairInfo.posPts = {
 23359              x1: srcPos.x,
 23360              x2: tgtPos.x,
 23361              y1: srcPos.y,
 23362              y2: tgtPos.y
 23363            };
 23364            var dy = tgtOutside[1] - srcOutside[1];
 23365            var dx = tgtOutside[0] - srcOutside[0];
 23366            var l = Math.sqrt(dx * dx + dy * dy);
 23367            var vector = pairInfo.vector = {
 23368              x: dx,
 23369              y: dy
 23370            };
 23371            var vectorNorm = pairInfo.vectorNorm = {
 23372              x: vector.x / l,
 23373              y: vector.y / l
 23374            };
 23375            var vectorNormInverse = {
 23376              x: -vectorNorm.y,
 23377              y: vectorNorm.x
 23378            }; // if node shapes overlap, then no ctrl pts to draw
 23379  
 23380            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);
 23381            pairInfo.vectorNormInverse = vectorNormInverse;
 23382            swappedpairInfo = {
 23383              nodesOverlap: pairInfo.nodesOverlap,
 23384              dirCounts: pairInfo.dirCounts,
 23385              calculatedIntersection: true,
 23386              hasBezier: pairInfo.hasBezier,
 23387              hasUnbundled: pairInfo.hasUnbundled,
 23388              eles: pairInfo.eles,
 23389              srcPos: tgtPos,
 23390              tgtPos: srcPos,
 23391              srcW: tgtW,
 23392              srcH: tgtH,
 23393              tgtW: srcW,
 23394              tgtH: srcH,
 23395              srcIntn: tgtIntn,
 23396              tgtIntn: srcIntn,
 23397              srcShape: tgtShape,
 23398              tgtShape: srcShape,
 23399              posPts: {
 23400                x1: posPts.x2,
 23401                y1: posPts.y2,
 23402                x2: posPts.x1,
 23403                y2: posPts.y1
 23404              },
 23405              intersectionPts: {
 23406                x1: intersectionPts.x2,
 23407                y1: intersectionPts.y2,
 23408                x2: intersectionPts.x1,
 23409                y2: intersectionPts.y1
 23410              },
 23411              vector: {
 23412                x: -vector.x,
 23413                y: -vector.y
 23414              },
 23415              vectorNorm: {
 23416                x: -vectorNorm.x,
 23417                y: -vectorNorm.y
 23418              },
 23419              vectorNormInverse: {
 23420                x: -vectorNormInverse.x,
 23421                y: -vectorNormInverse.y
 23422              }
 23423            };
 23424          }
 23425  
 23426          var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
 23427          rs.nodesOverlap = passedPairInfo.nodesOverlap;
 23428          rs.srcIntn = passedPairInfo.srcIntn;
 23429          rs.tgtIntn = passedPairInfo.tgtIntn;
 23430  
 23431          if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
 23432            _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
 23433          } else if (src === tgt) {
 23434            _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
 23435          } else if (_curveStyle === 'segments') {
 23436            _this.findSegmentsPoints(_edge, passedPairInfo);
 23437          } else if (_curveStyle === 'taxi') {
 23438            _this.findTaxiPoints(_edge, passedPairInfo);
 23439          } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
 23440            _this.findStraightEdgePoints(_edge);
 23441          } else {
 23442            _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
 23443          }
 23444  
 23445          _this.findEndpoints(_edge);
 23446  
 23447          _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
 23448  
 23449          _this.checkForInvalidEdgeWarning(_edge);
 23450  
 23451          _this.storeAllpts(_edge);
 23452  
 23453          _this.storeEdgeProjections(_edge);
 23454  
 23455          _this.calculateArrowAngles(_edge);
 23456  
 23457          _this.recalculateEdgeLabelProjections(_edge);
 23458  
 23459          _this.calculateLabelAngles(_edge);
 23460        } // for pair edges
 23461  
 23462      };
 23463  
 23464      for (var p = 0; p < pairIds.length; p++) {
 23465        _loop(p);
 23466      } // for pair ids
 23467      // haystacks avoid the expense of pairInfo stuff (intersections etc.)
 23468  
 23469  
 23470      this.findHaystackPoints(haystackEdges);
 23471    };
 23472  
 23473    function getPts(pts) {
 23474      var retPts = [];
 23475  
 23476      if (pts == null) {
 23477        return;
 23478      }
 23479  
 23480      for (var i = 0; i < pts.length; i += 2) {
 23481        var x = pts[i];
 23482        var y = pts[i + 1];
 23483        retPts.push({
 23484          x: x,
 23485          y: y
 23486        });
 23487      }
 23488  
 23489      return retPts;
 23490    }
 23491  
 23492    BRp$3.getSegmentPoints = function (edge) {
 23493      var rs = edge[0]._private.rscratch;
 23494      var type = rs.edgeType;
 23495  
 23496      if (type === 'segments') {
 23497        this.recalculateRenderedStyle(edge);
 23498        return getPts(rs.segpts);
 23499      }
 23500    };
 23501  
 23502    BRp$3.getControlPoints = function (edge) {
 23503      var rs = edge[0]._private.rscratch;
 23504      var type = rs.edgeType;
 23505  
 23506      if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
 23507        this.recalculateRenderedStyle(edge);
 23508        return getPts(rs.ctrlpts);
 23509      }
 23510    };
 23511  
 23512    BRp$3.getEdgeMidpoint = function (edge) {
 23513      var rs = edge[0]._private.rscratch;
 23514      this.recalculateRenderedStyle(edge);
 23515      return {
 23516        x: rs.midX,
 23517        y: rs.midY
 23518      };
 23519    };
 23520  
 23521    var BRp$4 = {};
 23522  
 23523    BRp$4.manualEndptToPx = function (node, prop) {
 23524      var r = this;
 23525      var npos = node.position();
 23526      var w = node.outerWidth();
 23527      var h = node.outerHeight();
 23528  
 23529      if (prop.value.length === 2) {
 23530        var p = [prop.pfValue[0], prop.pfValue[1]];
 23531  
 23532        if (prop.units[0] === '%') {
 23533          p[0] = p[0] * w;
 23534        }
 23535  
 23536        if (prop.units[1] === '%') {
 23537          p[1] = p[1] * h;
 23538        }
 23539  
 23540        p[0] += npos.x;
 23541        p[1] += npos.y;
 23542        return p;
 23543      } else {
 23544        var angle = prop.pfValue[0];
 23545        angle = -Math.PI / 2 + angle; // start at 12 o'clock
 23546  
 23547        var l = 2 * Math.max(w, h);
 23548        var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
 23549        return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
 23550      }
 23551    };
 23552  
 23553    BRp$4.findEndpoints = function (edge) {
 23554      var r = this;
 23555      var intersect;
 23556      var source = edge.source()[0];
 23557      var target = edge.target()[0];
 23558      var srcPos = source.position();
 23559      var tgtPos = target.position();
 23560      var tgtArShape = edge.pstyle('target-arrow-shape').value;
 23561      var srcArShape = edge.pstyle('source-arrow-shape').value;
 23562      var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
 23563      var srcDist = edge.pstyle('source-distance-from-node').pfValue;
 23564      var curveStyle = edge.pstyle('curve-style').value;
 23565      var rs = edge._private.rscratch;
 23566      var et = rs.edgeType;
 23567      var taxi = curveStyle === 'taxi';
 23568      var self = et === 'self' || et === 'compound';
 23569      var bezier = et === 'bezier' || et === 'multibezier' || self;
 23570      var multi = et !== 'bezier';
 23571      var lines = et === 'straight' || et === 'segments';
 23572      var segments = et === 'segments';
 23573      var hasEndpts = bezier || multi || lines;
 23574      var overrideEndpts = self || taxi;
 23575      var srcManEndpt = edge.pstyle('source-endpoint');
 23576      var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
 23577      var tgtManEndpt = edge.pstyle('target-endpoint');
 23578      var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
 23579      rs.srcManEndpt = srcManEndpt;
 23580      rs.tgtManEndpt = tgtManEndpt;
 23581      var p1; // last known point of edge on target side
 23582  
 23583      var p2; // last known point of edge on source side
 23584  
 23585      var p1_i; // point to intersect with target shape
 23586  
 23587      var p2_i; // point to intersect with source shape
 23588  
 23589      if (bezier) {
 23590        var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
 23591        var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
 23592        p1 = cpEnd;
 23593        p2 = cpStart;
 23594      } else if (lines) {
 23595        var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
 23596        var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
 23597        p1 = tgtArrowFromPt;
 23598        p2 = srcArrowFromPt;
 23599      }
 23600  
 23601      if (tgtManEndptVal === 'inside-to-node') {
 23602        intersect = [tgtPos.x, tgtPos.y];
 23603      } else if (tgtManEndpt.units) {
 23604        intersect = this.manualEndptToPx(target, tgtManEndpt);
 23605      } else if (tgtManEndptVal === 'outside-to-line') {
 23606        intersect = rs.tgtIntn; // use cached value from ctrlpt calc
 23607      } else {
 23608        if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
 23609          p1_i = p1;
 23610        } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
 23611          p1_i = [srcPos.x, srcPos.y];
 23612        }
 23613  
 23614        intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
 23615  
 23616        if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
 23617          var trs = target._private.rscratch;
 23618          var lw = trs.labelWidth;
 23619          var lh = trs.labelHeight;
 23620          var lx = trs.labelX;
 23621          var ly = trs.labelY;
 23622          var lw2 = lw / 2;
 23623          var lh2 = lh / 2;
 23624          var va = target.pstyle('text-valign').value;
 23625  
 23626          if (va === 'top') {
 23627            ly -= lh2;
 23628          } else if (va === 'bottom') {
 23629            ly += lh2;
 23630          }
 23631  
 23632          var ha = target.pstyle('text-halign').value;
 23633  
 23634          if (ha === 'left') {
 23635            lx -= lw2;
 23636          } else if (ha === 'right') {
 23637            lx += lw2;
 23638          }
 23639  
 23640          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);
 23641  
 23642          if (labelIntersect.length > 0) {
 23643            var refPt = srcPos;
 23644            var intSqdist = sqdist(refPt, array2point(intersect));
 23645            var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
 23646            var minSqDist = intSqdist;
 23647  
 23648            if (labIntSqdist < intSqdist) {
 23649              intersect = labelIntersect;
 23650              minSqDist = labIntSqdist;
 23651            }
 23652  
 23653            if (labelIntersect.length > 2) {
 23654              var labInt2SqDist = sqdist(refPt, {
 23655                x: labelIntersect[2],
 23656                y: labelIntersect[3]
 23657              });
 23658  
 23659              if (labInt2SqDist < minSqDist) {
 23660                intersect = [labelIntersect[2], labelIntersect[3]];
 23661              }
 23662            }
 23663          }
 23664        }
 23665      }
 23666  
 23667      var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
 23668      var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
 23669      rs.endX = edgeEnd[0];
 23670      rs.endY = edgeEnd[1];
 23671      rs.arrowEndX = arrowEnd[0];
 23672      rs.arrowEndY = arrowEnd[1];
 23673  
 23674      if (srcManEndptVal === 'inside-to-node') {
 23675        intersect = [srcPos.x, srcPos.y];
 23676      } else if (srcManEndpt.units) {
 23677        intersect = this.manualEndptToPx(source, srcManEndpt);
 23678      } else if (srcManEndptVal === 'outside-to-line') {
 23679        intersect = rs.srcIntn; // use cached value from ctrlpt calc
 23680      } else {
 23681        if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
 23682          p2_i = p2;
 23683        } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
 23684          p2_i = [tgtPos.x, tgtPos.y];
 23685        }
 23686  
 23687        intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
 23688  
 23689        if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
 23690          var srs = source._private.rscratch;
 23691          var _lw = srs.labelWidth;
 23692          var _lh = srs.labelHeight;
 23693          var _lx = srs.labelX;
 23694          var _ly = srs.labelY;
 23695  
 23696          var _lw2 = _lw / 2;
 23697  
 23698          var _lh2 = _lh / 2;
 23699  
 23700          var _va = source.pstyle('text-valign').value;
 23701  
 23702          if (_va === 'top') {
 23703            _ly -= _lh2;
 23704          } else if (_va === 'bottom') {
 23705            _ly += _lh2;
 23706          }
 23707  
 23708          var _ha = source.pstyle('text-halign').value;
 23709  
 23710          if (_ha === 'left') {
 23711            _lx -= _lw2;
 23712          } else if (_ha === 'right') {
 23713            _lx += _lw2;
 23714          }
 23715  
 23716          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);
 23717  
 23718          if (_labelIntersect.length > 0) {
 23719            var _refPt = tgtPos;
 23720  
 23721            var _intSqdist = sqdist(_refPt, array2point(intersect));
 23722  
 23723            var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
 23724  
 23725            var _minSqDist = _intSqdist;
 23726  
 23727            if (_labIntSqdist < _intSqdist) {
 23728              intersect = [_labelIntersect[0], _labelIntersect[1]];
 23729              _minSqDist = _labIntSqdist;
 23730            }
 23731  
 23732            if (_labelIntersect.length > 2) {
 23733              var _labInt2SqDist = sqdist(_refPt, {
 23734                x: _labelIntersect[2],
 23735                y: _labelIntersect[3]
 23736              });
 23737  
 23738              if (_labInt2SqDist < _minSqDist) {
 23739                intersect = [_labelIntersect[2], _labelIntersect[3]];
 23740              }
 23741            }
 23742          }
 23743        }
 23744      }
 23745  
 23746      var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
 23747      var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
 23748      rs.startX = edgeStart[0];
 23749      rs.startY = edgeStart[1];
 23750      rs.arrowStartX = arrowStart[0];
 23751      rs.arrowStartY = arrowStart[1];
 23752  
 23753      if (hasEndpts) {
 23754        if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
 23755          rs.badLine = true;
 23756        } else {
 23757          rs.badLine = false;
 23758        }
 23759      }
 23760    };
 23761  
 23762    BRp$4.getSourceEndpoint = function (edge) {
 23763      var rs = edge[0]._private.rscratch;
 23764      this.recalculateRenderedStyle(edge);
 23765  
 23766      switch (rs.edgeType) {
 23767        case 'haystack':
 23768          return {
 23769            x: rs.haystackPts[0],
 23770            y: rs.haystackPts[1]
 23771          };
 23772  
 23773        default:
 23774          return {
 23775            x: rs.arrowStartX,
 23776            y: rs.arrowStartY
 23777          };
 23778      }
 23779    };
 23780  
 23781    BRp$4.getTargetEndpoint = function (edge) {
 23782      var rs = edge[0]._private.rscratch;
 23783      this.recalculateRenderedStyle(edge);
 23784  
 23785      switch (rs.edgeType) {
 23786        case 'haystack':
 23787          return {
 23788            x: rs.haystackPts[2],
 23789            y: rs.haystackPts[3]
 23790          };
 23791  
 23792        default:
 23793          return {
 23794            x: rs.arrowEndX,
 23795            y: rs.arrowEndY
 23796          };
 23797      }
 23798    };
 23799  
 23800    var BRp$5 = {};
 23801  
 23802    function pushBezierPts(r, edge, pts) {
 23803      var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
 23804        return qbezierAt(p1, p2, p3, t);
 23805      };
 23806  
 23807      var _p = edge._private;
 23808      var bpts = _p.rstyle.bezierPts;
 23809  
 23810      for (var i = 0; i < r.bezierProjPcts.length; i++) {
 23811        var p = r.bezierProjPcts[i];
 23812        bpts.push({
 23813          x: qbezierAt$1(pts[0], pts[2], pts[4], p),
 23814          y: qbezierAt$1(pts[1], pts[3], pts[5], p)
 23815        });
 23816      }
 23817    }
 23818  
 23819    BRp$5.storeEdgeProjections = function (edge) {
 23820      var _p = edge._private;
 23821      var rs = _p.rscratch;
 23822      var et = rs.edgeType; // clear the cached points state
 23823  
 23824      _p.rstyle.bezierPts = null;
 23825      _p.rstyle.linePts = null;
 23826      _p.rstyle.haystackPts = null;
 23827  
 23828      if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
 23829        _p.rstyle.bezierPts = [];
 23830  
 23831        for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
 23832          pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
 23833        }
 23834      } else if (et === 'segments') {
 23835        var lpts = _p.rstyle.linePts = [];
 23836  
 23837        for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
 23838          lpts.push({
 23839            x: rs.allpts[i],
 23840            y: rs.allpts[i + 1]
 23841          });
 23842        }
 23843      } else if (et === 'haystack') {
 23844        var hpts = rs.haystackPts;
 23845        _p.rstyle.haystackPts = [{
 23846          x: hpts[0],
 23847          y: hpts[1]
 23848        }, {
 23849          x: hpts[2],
 23850          y: hpts[3]
 23851        }];
 23852      }
 23853  
 23854      _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
 23855    };
 23856  
 23857    BRp$5.recalculateEdgeProjections = function (edges) {
 23858      this.findEdgeControlPoints(edges);
 23859    };
 23860  
 23861    var BRp$6 = {};
 23862  
 23863    BRp$6.recalculateNodeLabelProjection = function (node) {
 23864      var content = node.pstyle('label').strValue;
 23865  
 23866      if (emptyString(content)) {
 23867        return;
 23868      }
 23869  
 23870      var textX, textY;
 23871      var _p = node._private;
 23872      var nodeWidth = node.width();
 23873      var nodeHeight = node.height();
 23874      var padding = node.padding();
 23875      var nodePos = node.position();
 23876      var textHalign = node.pstyle('text-halign').strValue;
 23877      var textValign = node.pstyle('text-valign').strValue;
 23878      var rs = _p.rscratch;
 23879      var rstyle = _p.rstyle;
 23880  
 23881      switch (textHalign) {
 23882        case 'left':
 23883          textX = nodePos.x - nodeWidth / 2 - padding;
 23884          break;
 23885  
 23886        case 'right':
 23887          textX = nodePos.x + nodeWidth / 2 + padding;
 23888          break;
 23889  
 23890        default:
 23891          // e.g. center
 23892          textX = nodePos.x;
 23893      }
 23894  
 23895      switch (textValign) {
 23896        case 'top':
 23897          textY = nodePos.y - nodeHeight / 2 - padding;
 23898          break;
 23899  
 23900        case 'bottom':
 23901          textY = nodePos.y + nodeHeight / 2 + padding;
 23902          break;
 23903  
 23904        default:
 23905          // e.g. middle
 23906          textY = nodePos.y;
 23907      }
 23908  
 23909      rs.labelX = textX;
 23910      rs.labelY = textY;
 23911      rstyle.labelX = textX;
 23912      rstyle.labelY = textY;
 23913      this.applyLabelDimensions(node);
 23914    };
 23915  
 23916    var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
 23917      var angle = Math.atan(dy / dx);
 23918  
 23919      if (dx === 0 && angle < 0) {
 23920        angle = angle * -1;
 23921      }
 23922  
 23923      return angle;
 23924    };
 23925  
 23926    var lineAngle = function lineAngle(p0, p1) {
 23927      var dx = p1.x - p0.x;
 23928      var dy = p1.y - p0.y;
 23929      return lineAngleFromDelta(dx, dy);
 23930    };
 23931  
 23932    var bezierAngle = function bezierAngle(p0, p1, p2, t) {
 23933      var t0 = bound(0, t - 0.001, 1);
 23934      var t1 = bound(0, t + 0.001, 1);
 23935      var lp0 = qbezierPtAt(p0, p1, p2, t0);
 23936      var lp1 = qbezierPtAt(p0, p1, p2, t1);
 23937      return lineAngle(lp0, lp1);
 23938    };
 23939  
 23940    BRp$6.recalculateEdgeLabelProjections = function (edge) {
 23941      var p;
 23942      var _p = edge._private;
 23943      var rs = _p.rscratch;
 23944      var r = this;
 23945      var content = {
 23946        mid: edge.pstyle('label').strValue,
 23947        source: edge.pstyle('source-label').strValue,
 23948        target: edge.pstyle('target-label').strValue
 23949      };
 23950  
 23951      if (content.mid || content.source || content.target) ; else {
 23952          return; // no labels => no calcs
 23953        } // add center point to style so bounding box calculations can use it
 23954      //
 23955  
 23956  
 23957      p = {
 23958        x: rs.midX,
 23959        y: rs.midY
 23960      };
 23961  
 23962      var setRs = function setRs(propName, prefix, value) {
 23963        setPrefixedProperty(_p.rscratch, propName, prefix, value);
 23964        setPrefixedProperty(_p.rstyle, propName, prefix, value);
 23965      };
 23966  
 23967      setRs('labelX', null, p.x);
 23968      setRs('labelY', null, p.y);
 23969      var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
 23970      setRs('labelAutoAngle', null, midAngle);
 23971  
 23972      var createControlPointInfo = function createControlPointInfo() {
 23973        if (createControlPointInfo.cache) {
 23974          return createControlPointInfo.cache;
 23975        } // use cache so only 1x per edge
 23976  
 23977  
 23978        var ctrlpts = []; // store each ctrlpt info init
 23979  
 23980        for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
 23981          var p0 = {
 23982            x: rs.allpts[i],
 23983            y: rs.allpts[i + 1]
 23984          };
 23985          var p1 = {
 23986            x: rs.allpts[i + 2],
 23987            y: rs.allpts[i + 3]
 23988          }; // ctrlpt
 23989  
 23990          var p2 = {
 23991            x: rs.allpts[i + 4],
 23992            y: rs.allpts[i + 5]
 23993          };
 23994          ctrlpts.push({
 23995            p0: p0,
 23996            p1: p1,
 23997            p2: p2,
 23998            startDist: 0,
 23999            length: 0,
 24000            segments: []
 24001          });
 24002        }
 24003  
 24004        var bpts = _p.rstyle.bezierPts;
 24005        var nProjs = r.bezierProjPcts.length;
 24006  
 24007        function addSegment(cp, p0, p1, t0, t1) {
 24008          var length = dist(p0, p1);
 24009          var prevSegment = cp.segments[cp.segments.length - 1];
 24010          var segment = {
 24011            p0: p0,
 24012            p1: p1,
 24013            t0: t0,
 24014            t1: t1,
 24015            startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
 24016            length: length
 24017          };
 24018          cp.segments.push(segment);
 24019          cp.length += length;
 24020        } // update each ctrlpt with segment info
 24021  
 24022  
 24023        for (var _i = 0; _i < ctrlpts.length; _i++) {
 24024          var cp = ctrlpts[_i];
 24025          var prevCp = ctrlpts[_i - 1];
 24026  
 24027          if (prevCp) {
 24028            cp.startDist = prevCp.startDist + prevCp.length;
 24029          }
 24030  
 24031          addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
 24032  
 24033          for (var j = 0; j < nProjs - 1; j++) {
 24034            addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
 24035          }
 24036  
 24037          addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
 24038        }
 24039  
 24040        return createControlPointInfo.cache = ctrlpts;
 24041      };
 24042  
 24043      var calculateEndProjection = function calculateEndProjection(prefix) {
 24044        var angle;
 24045        var isSrc = prefix === 'source';
 24046  
 24047        if (!content[prefix]) {
 24048          return;
 24049        }
 24050  
 24051        var offset = edge.pstyle(prefix + '-text-offset').pfValue;
 24052  
 24053        switch (rs.edgeType) {
 24054          case 'self':
 24055          case 'compound':
 24056          case 'bezier':
 24057          case 'multibezier':
 24058            {
 24059              var cps = createControlPointInfo();
 24060              var selected;
 24061              var startDist = 0;
 24062              var totalDist = 0; // find the segment we're on
 24063  
 24064              for (var i = 0; i < cps.length; i++) {
 24065                var _cp = cps[isSrc ? i : cps.length - 1 - i];
 24066  
 24067                for (var j = 0; j < _cp.segments.length; j++) {
 24068                  var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
 24069                  var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
 24070                  startDist = totalDist;
 24071                  totalDist += _seg.length;
 24072  
 24073                  if (totalDist >= offset || lastSeg) {
 24074                    selected = {
 24075                      cp: _cp,
 24076                      segment: _seg
 24077                    };
 24078                    break;
 24079                  }
 24080                }
 24081  
 24082                if (selected) {
 24083                  break;
 24084                }
 24085              }
 24086  
 24087              var cp = selected.cp;
 24088              var seg = selected.segment;
 24089              var tSegment = (offset - startDist) / seg.length;
 24090              var segDt = seg.t1 - seg.t0;
 24091              var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
 24092              t = bound(0, t, 1);
 24093              p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
 24094              angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
 24095              break;
 24096            }
 24097  
 24098          case 'straight':
 24099          case 'segments':
 24100          case 'haystack':
 24101            {
 24102              var d = 0,
 24103                  di,
 24104                  d0;
 24105              var p0, p1;
 24106              var l = rs.allpts.length;
 24107  
 24108              for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
 24109                if (isSrc) {
 24110                  p0 = {
 24111                    x: rs.allpts[_i2],
 24112                    y: rs.allpts[_i2 + 1]
 24113                  };
 24114                  p1 = {
 24115                    x: rs.allpts[_i2 + 2],
 24116                    y: rs.allpts[_i2 + 3]
 24117                  };
 24118                } else {
 24119                  p0 = {
 24120                    x: rs.allpts[l - 2 - _i2],
 24121                    y: rs.allpts[l - 1 - _i2]
 24122                  };
 24123                  p1 = {
 24124                    x: rs.allpts[l - 4 - _i2],
 24125                    y: rs.allpts[l - 3 - _i2]
 24126                  };
 24127                }
 24128  
 24129                di = dist(p0, p1);
 24130                d0 = d;
 24131                d += di;
 24132  
 24133                if (d >= offset) {
 24134                  break;
 24135                }
 24136              }
 24137  
 24138              var pD = offset - d0;
 24139  
 24140              var _t = pD / di;
 24141  
 24142              _t = bound(0, _t, 1);
 24143              p = lineAt(p0, p1, _t);
 24144              angle = lineAngle(p0, p1);
 24145              break;
 24146            }
 24147        }
 24148  
 24149        setRs('labelX', prefix, p.x);
 24150        setRs('labelY', prefix, p.y);
 24151        setRs('labelAutoAngle', prefix, angle);
 24152      };
 24153  
 24154      calculateEndProjection('source');
 24155      calculateEndProjection('target');
 24156      this.applyLabelDimensions(edge);
 24157    };
 24158  
 24159    BRp$6.applyLabelDimensions = function (ele) {
 24160      this.applyPrefixedLabelDimensions(ele);
 24161  
 24162      if (ele.isEdge()) {
 24163        this.applyPrefixedLabelDimensions(ele, 'source');
 24164        this.applyPrefixedLabelDimensions(ele, 'target');
 24165      }
 24166    };
 24167  
 24168    BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
 24169      var _p = ele._private;
 24170      var text = this.getLabelText(ele, prefix);
 24171      var labelDims = this.calculateLabelDimensions(ele, text);
 24172      var lineHeight = ele.pstyle('line-height').pfValue;
 24173      var textWrap = ele.pstyle('text-wrap').strValue;
 24174      var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
 24175      var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
 24176      var normPerLineHeight = labelDims.height / numLines;
 24177      var labelLineHeight = normPerLineHeight * lineHeight;
 24178      var width = labelDims.width;
 24179      var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
 24180      setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
 24181      setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
 24182      setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
 24183      setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
 24184      setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
 24185    };
 24186  
 24187    BRp$6.getLabelText = function (ele, prefix) {
 24188      var _p = ele._private;
 24189      var pfd = prefix ? prefix + '-' : '';
 24190      var text = ele.pstyle(pfd + 'label').strValue;
 24191      var textTransform = ele.pstyle('text-transform').value;
 24192  
 24193      var rscratch = function rscratch(propName, value) {
 24194        if (value) {
 24195          setPrefixedProperty(_p.rscratch, propName, prefix, value);
 24196          return value;
 24197        } else {
 24198          return getPrefixedProperty(_p.rscratch, propName, prefix);
 24199        }
 24200      }; // for empty text, skip all processing
 24201  
 24202  
 24203      if (!text) {
 24204        return '';
 24205      }
 24206  
 24207      if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
 24208        text = text.toUpperCase();
 24209      } else if (textTransform == 'lowercase') {
 24210        text = text.toLowerCase();
 24211      }
 24212  
 24213      var wrapStyle = ele.pstyle('text-wrap').value;
 24214  
 24215      if (wrapStyle === 'wrap') {
 24216        var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
 24217  
 24218        if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
 24219          return rscratch('labelWrapCachedText');
 24220        }
 24221  
 24222        var zwsp = "\u200B";
 24223        var lines = text.split('\n');
 24224        var maxW = ele.pstyle('text-max-width').pfValue;
 24225        var overflow = ele.pstyle('text-overflow-wrap').value;
 24226        var overflowAny = overflow === 'anywhere';
 24227        var wrappedLines = [];
 24228        var wordsRegex = /[\s\u200b]+/;
 24229        var wordSeparator = overflowAny ? '' : ' ';
 24230  
 24231        for (var l = 0; l < lines.length; l++) {
 24232          var line = lines[l];
 24233          var lineDims = this.calculateLabelDimensions(ele, line);
 24234          var lineW = lineDims.width;
 24235  
 24236          if (overflowAny) {
 24237            var processedLine = line.split('').join(zwsp);
 24238            line = processedLine;
 24239          }
 24240  
 24241          if (lineW > maxW) {
 24242            // line is too long
 24243            var words = line.split(wordsRegex);
 24244            var subline = '';
 24245  
 24246            for (var w = 0; w < words.length; w++) {
 24247              var word = words[w];
 24248              var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
 24249              var testDims = this.calculateLabelDimensions(ele, testLine);
 24250              var testW = testDims.width;
 24251  
 24252              if (testW <= maxW) {
 24253                // word fits on current line
 24254                subline += word + wordSeparator;
 24255              } else {
 24256                // word starts new line
 24257                if (subline) {
 24258                  wrappedLines.push(subline);
 24259                }
 24260  
 24261                subline = word + wordSeparator;
 24262              }
 24263            } // if there's remaining text, put it in a wrapped line
 24264  
 24265  
 24266            if (!subline.match(/^[\s\u200b]+$/)) {
 24267              wrappedLines.push(subline);
 24268            }
 24269          } else {
 24270            // line is already short enough
 24271            wrappedLines.push(line);
 24272          }
 24273        } // for
 24274  
 24275  
 24276        rscratch('labelWrapCachedLines', wrappedLines);
 24277        text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
 24278        rscratch('labelWrapKey', labelKey);
 24279      } else if (wrapStyle === 'ellipsis') {
 24280        var _maxW = ele.pstyle('text-max-width').pfValue;
 24281        var ellipsized = '';
 24282        var ellipsis = "\u2026";
 24283        var incLastCh = false;
 24284  
 24285        for (var i = 0; i < text.length; i++) {
 24286          var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
 24287  
 24288          if (widthWithNextCh > _maxW) {
 24289            break;
 24290          }
 24291  
 24292          ellipsized += text[i];
 24293  
 24294          if (i === text.length - 1) {
 24295            incLastCh = true;
 24296          }
 24297        }
 24298  
 24299        if (!incLastCh) {
 24300          ellipsized += ellipsis;
 24301        }
 24302  
 24303        return ellipsized;
 24304      } // if ellipsize
 24305  
 24306  
 24307      return text;
 24308    };
 24309  
 24310    BRp$6.getLabelJustification = function (ele) {
 24311      var justification = ele.pstyle('text-justification').strValue;
 24312      var textHalign = ele.pstyle('text-halign').strValue;
 24313  
 24314      if (justification === 'auto') {
 24315        if (ele.isNode()) {
 24316          switch (textHalign) {
 24317            case 'left':
 24318              return 'right';
 24319  
 24320            case 'right':
 24321              return 'left';
 24322  
 24323            default:
 24324              return 'center';
 24325          }
 24326        } else {
 24327          return 'center';
 24328        }
 24329      } else {
 24330        return justification;
 24331      }
 24332    };
 24333  
 24334    BRp$6.calculateLabelDimensions = function (ele, text) {
 24335      var r = this;
 24336      var cacheKey = hashString(text, ele._private.labelDimsKey);
 24337      var cache = r.labelDimCache || (r.labelDimCache = []);
 24338      var existingVal = cache[cacheKey];
 24339  
 24340      if (existingVal != null) {
 24341        return existingVal;
 24342      }
 24343  
 24344      var sizeMult = 1; // increase the scale to increase accuracy w.r.t. zoomed text
 24345  
 24346      var fStyle = ele.pstyle('font-style').strValue;
 24347      var size = sizeMult * ele.pstyle('font-size').pfValue + 'px';
 24348      var family = ele.pstyle('font-family').strValue;
 24349      var weight = ele.pstyle('font-weight').strValue;
 24350      var div = this.labelCalcDiv;
 24351  
 24352      if (!div) {
 24353        div = this.labelCalcDiv = document.createElement('div'); // eslint-disable-line no-undef
 24354  
 24355        document.body.appendChild(div); // eslint-disable-line no-undef
 24356      }
 24357  
 24358      var ds = div.style; // from ele style
 24359  
 24360      ds.fontFamily = family;
 24361      ds.fontStyle = fStyle;
 24362      ds.fontSize = size;
 24363      ds.fontWeight = weight; // forced style
 24364  
 24365      ds.position = 'absolute';
 24366      ds.left = '-9999px';
 24367      ds.top = '-9999px';
 24368      ds.zIndex = '-1';
 24369      ds.visibility = 'hidden';
 24370      ds.pointerEvents = 'none';
 24371      ds.padding = '0';
 24372      ds.lineHeight = '1'; // - newlines must be taken into account for text-wrap:wrap
 24373      // - since spaces are not collapsed, each space must be taken into account
 24374  
 24375      ds.whiteSpace = 'pre'; // put label content in div
 24376  
 24377      div.textContent = text;
 24378      return cache[cacheKey] = {
 24379        width: Math.ceil(div.clientWidth / sizeMult),
 24380        height: Math.ceil(div.clientHeight / sizeMult)
 24381      };
 24382    };
 24383  
 24384    BRp$6.calculateLabelAngle = function (ele, prefix) {
 24385      var _p = ele._private;
 24386      var rs = _p.rscratch;
 24387      var isEdge = ele.isEdge();
 24388      var prefixDash = prefix ? prefix + '-' : '';
 24389      var rot = ele.pstyle(prefixDash + 'text-rotation');
 24390      var rotStr = rot.strValue;
 24391  
 24392      if (rotStr === 'none') {
 24393        return 0;
 24394      } else if (isEdge && rotStr === 'autorotate') {
 24395        return rs.labelAutoAngle;
 24396      } else if (rotStr === 'autorotate') {
 24397        return 0;
 24398      } else {
 24399        return rot.pfValue;
 24400      }
 24401    };
 24402  
 24403    BRp$6.calculateLabelAngles = function (ele) {
 24404      var r = this;
 24405      var isEdge = ele.isEdge();
 24406      var _p = ele._private;
 24407      var rs = _p.rscratch;
 24408      rs.labelAngle = r.calculateLabelAngle(ele);
 24409  
 24410      if (isEdge) {
 24411        rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
 24412        rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
 24413      }
 24414    };
 24415  
 24416    var BRp$7 = {};
 24417    var TOO_SMALL_CUT_RECT = 28;
 24418    var warnedCutRect = false;
 24419  
 24420    BRp$7.getNodeShape = function (node) {
 24421      var r = this;
 24422      var shape = node.pstyle('shape').value;
 24423  
 24424      if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
 24425        if (!warnedCutRect) {
 24426          warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
 24427          warnedCutRect = true;
 24428        }
 24429  
 24430        return 'rectangle';
 24431      }
 24432  
 24433      if (node.isParent()) {
 24434        if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'cutrectangle' || shape === 'barrel') {
 24435          return shape;
 24436        } else {
 24437          return 'rectangle';
 24438        }
 24439      }
 24440  
 24441      if (shape === 'polygon') {
 24442        var points = node.pstyle('shape-polygon-points').value;
 24443        return r.nodeShapes.makePolygon(points).name;
 24444      }
 24445  
 24446      return shape;
 24447    };
 24448  
 24449    var BRp$8 = {};
 24450  
 24451    BRp$8.registerCalculationListeners = function () {
 24452      var cy = this.cy;
 24453      var elesToUpdate = cy.collection();
 24454      var r = this;
 24455  
 24456      var enqueue = function enqueue(eles) {
 24457        var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
 24458        elesToUpdate.merge(eles);
 24459  
 24460        if (dirtyStyleCaches) {
 24461          for (var i = 0; i < eles.length; i++) {
 24462            var ele = eles[i];
 24463            var _p = ele._private;
 24464            var rstyle = _p.rstyle;
 24465            rstyle.clean = false;
 24466            rstyle.cleanConnected = false;
 24467          }
 24468        }
 24469      };
 24470  
 24471      r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
 24472        var ele = e.target;
 24473        enqueue(ele);
 24474      }).on('style.* background.*', function onDirtyStyle(e) {
 24475        var ele = e.target;
 24476        enqueue(ele, false);
 24477      });
 24478  
 24479      var updateEleCalcs = function updateEleCalcs(willDraw) {
 24480        if (willDraw) {
 24481          var fns = r.onUpdateEleCalcsFns;
 24482  
 24483          for (var i = 0; i < elesToUpdate.length; i++) {
 24484            var ele = elesToUpdate[i];
 24485            var rstyle = ele._private.rstyle;
 24486  
 24487            if (ele.isNode() && !rstyle.cleanConnected) {
 24488              enqueue(ele.connectedEdges());
 24489              rstyle.cleanConnected = true;
 24490            }
 24491          }
 24492  
 24493          if (fns) {
 24494            for (var i = 0; i < fns.length; i++) {
 24495              var fn = fns[i];
 24496              fn(willDraw, elesToUpdate);
 24497            }
 24498          }
 24499  
 24500          r.recalculateRenderedStyle(elesToUpdate);
 24501          elesToUpdate = cy.collection();
 24502        }
 24503      };
 24504  
 24505      r.flushRenderedStyleQueue = function () {
 24506        updateEleCalcs(true);
 24507      };
 24508  
 24509      r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
 24510    };
 24511  
 24512    BRp$8.onUpdateEleCalcs = function (fn) {
 24513      var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
 24514      fns.push(fn);
 24515    };
 24516  
 24517    BRp$8.recalculateRenderedStyle = function (eles, useCache) {
 24518      var isCleanConnected = function isCleanConnected(ele) {
 24519        return ele._private.rstyle.cleanConnected;
 24520      };
 24521  
 24522      var edges = [];
 24523      var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
 24524  
 24525      if (this.destroyed) {
 24526        return;
 24527      } // use cache by default for perf
 24528  
 24529  
 24530      if (useCache === undefined) {
 24531        useCache = true;
 24532      }
 24533  
 24534      for (var i = 0; i < eles.length; i++) {
 24535        var ele = eles[i];
 24536        var _p = ele._private;
 24537        var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
 24538        // (and a request for recalc may come in between frames)
 24539  
 24540        if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
 24541          rstyle.clean = false;
 24542        } // only update if dirty and in graph
 24543  
 24544  
 24545        if (useCache && rstyle.clean || ele.removed()) {
 24546          continue;
 24547        } // only update if not display: none
 24548  
 24549  
 24550        if (ele.pstyle('display').value === 'none') {
 24551          continue;
 24552        }
 24553  
 24554        if (_p.group === 'nodes') {
 24555          nodes.push(ele);
 24556        } else {
 24557          // edges
 24558          edges.push(ele);
 24559        }
 24560  
 24561        rstyle.clean = true;
 24562      } // update node data from projections
 24563  
 24564  
 24565      for (var i = 0; i < nodes.length; i++) {
 24566        var ele = nodes[i];
 24567        var _p = ele._private;
 24568        var rstyle = _p.rstyle;
 24569        var pos = ele.position();
 24570        this.recalculateNodeLabelProjection(ele);
 24571        rstyle.nodeX = pos.x;
 24572        rstyle.nodeY = pos.y;
 24573        rstyle.nodeW = ele.pstyle('width').pfValue;
 24574        rstyle.nodeH = ele.pstyle('height').pfValue;
 24575      }
 24576  
 24577      this.recalculateEdgeProjections(edges); // update edge data from projections
 24578  
 24579      for (var i = 0; i < edges.length; i++) {
 24580        var ele = edges[i];
 24581        var _p = ele._private;
 24582        var rstyle = _p.rstyle;
 24583        var rs = _p.rscratch; // update rstyle positions
 24584  
 24585        rstyle.srcX = rs.arrowStartX;
 24586        rstyle.srcY = rs.arrowStartY;
 24587        rstyle.tgtX = rs.arrowEndX;
 24588        rstyle.tgtY = rs.arrowEndY;
 24589        rstyle.midX = rs.midX;
 24590        rstyle.midY = rs.midY;
 24591        rstyle.labelAngle = rs.labelAngle;
 24592        rstyle.sourceLabelAngle = rs.sourceLabelAngle;
 24593        rstyle.targetLabelAngle = rs.targetLabelAngle;
 24594      }
 24595    };
 24596  
 24597    var BRp$9 = {};
 24598  
 24599    BRp$9.updateCachedGrabbedEles = function () {
 24600      var eles = this.cachedZSortedEles;
 24601  
 24602      if (!eles) {
 24603        // just let this be recalculated on the next z sort tick
 24604        return;
 24605      }
 24606  
 24607      eles.drag = [];
 24608      eles.nondrag = [];
 24609      var grabTargets = [];
 24610  
 24611      for (var i = 0; i < eles.length; i++) {
 24612        var ele = eles[i];
 24613        var rs = ele._private.rscratch;
 24614  
 24615        if (ele.grabbed() && !ele.isParent()) {
 24616          grabTargets.push(ele);
 24617        } else if (rs.inDragLayer) {
 24618          eles.drag.push(ele);
 24619        } else {
 24620          eles.nondrag.push(ele);
 24621        }
 24622      } // put the grab target nodes last so it's on top of its neighbourhood
 24623  
 24624  
 24625      for (var i = 0; i < grabTargets.length; i++) {
 24626        var ele = grabTargets[i];
 24627        eles.drag.push(ele);
 24628      }
 24629    };
 24630  
 24631    BRp$9.invalidateCachedZSortedEles = function () {
 24632      this.cachedZSortedEles = null;
 24633    };
 24634  
 24635    BRp$9.getCachedZSortedEles = function (forceRecalc) {
 24636      if (forceRecalc || !this.cachedZSortedEles) {
 24637        var eles = this.cy.mutableElements().toArray();
 24638        eles.sort(zIndexSort);
 24639        eles.interactive = eles.filter(function (ele) {
 24640          return ele.interactive();
 24641        });
 24642        this.cachedZSortedEles = eles;
 24643        this.updateCachedGrabbedEles();
 24644      } else {
 24645        eles = this.cachedZSortedEles;
 24646      }
 24647  
 24648      return eles;
 24649    };
 24650  
 24651    var BRp$a = {};
 24652    [BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
 24653      extend(BRp$a, props);
 24654    });
 24655  
 24656    var BRp$b = {};
 24657  
 24658    BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
 24659      var r = this;
 24660      var imageCache = r.imageCache = r.imageCache || {};
 24661      var cache = imageCache[url];
 24662  
 24663      if (cache) {
 24664        if (!cache.image.complete) {
 24665          cache.image.addEventListener('load', onLoad);
 24666        }
 24667  
 24668        return cache.image;
 24669      } else {
 24670        cache = imageCache[url] = imageCache[url] || {};
 24671        var image = cache.image = new Image(); // eslint-disable-line no-undef
 24672  
 24673        image.addEventListener('load', onLoad);
 24674        image.addEventListener('error', function () {
 24675          image.error = true;
 24676        }); // #1582 safari doesn't load data uris with crossOrigin properly
 24677        // https://bugs.webkit.org/show_bug.cgi?id=123978
 24678  
 24679        var dataUriPrefix = 'data:';
 24680        var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
 24681  
 24682        if (!isDataUri) {
 24683          image.crossOrigin = crossOrigin; // prevent tainted canvas
 24684        }
 24685  
 24686        image.src = url;
 24687        return image;
 24688      }
 24689    };
 24690  
 24691    var BRp$c = {};
 24692    /* global document, window, ResizeObserver, MutationObserver */
 24693  
 24694    BRp$c.registerBinding = function (target, event, handler, useCapture) {
 24695      // eslint-disable-line no-unused-vars
 24696      var args = Array.prototype.slice.apply(arguments, [1]); // copy
 24697  
 24698      var b = this.binder(target);
 24699      return b.on.apply(b, args);
 24700    };
 24701  
 24702    BRp$c.binder = function (tgt) {
 24703      var r = this;
 24704      var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
 24705  
 24706      if (r.supportsPassiveEvents == null) {
 24707        // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
 24708        var supportsPassive = false;
 24709  
 24710        try {
 24711          var opts = Object.defineProperty({}, 'passive', {
 24712            get: function get() {
 24713              supportsPassive = true;
 24714              return true;
 24715            }
 24716          });
 24717          window.addEventListener('test', null, opts);
 24718        } catch (err) {// not supported
 24719        }
 24720  
 24721        r.supportsPassiveEvents = supportsPassive;
 24722      }
 24723  
 24724      var on = function on(event, handler, useCapture) {
 24725        var args = Array.prototype.slice.call(arguments);
 24726  
 24727        if (tgtIsDom && r.supportsPassiveEvents) {
 24728          // replace useCapture w/ opts obj
 24729          args[2] = {
 24730            capture: useCapture != null ? useCapture : false,
 24731            passive: false,
 24732            once: false
 24733          };
 24734        }
 24735  
 24736        r.bindings.push({
 24737          target: tgt,
 24738          args: args
 24739        });
 24740        (tgt.addEventListener || tgt.on).apply(tgt, args);
 24741        return this;
 24742      };
 24743  
 24744      return {
 24745        on: on,
 24746        addEventListener: on,
 24747        addListener: on,
 24748        bind: on
 24749      };
 24750    };
 24751  
 24752    BRp$c.nodeIsDraggable = function (node) {
 24753      return node && node.isNode() && !node.locked() && node.grabbable();
 24754    };
 24755  
 24756    BRp$c.nodeIsGrabbable = function (node) {
 24757      return this.nodeIsDraggable(node) && node.interactive();
 24758    };
 24759  
 24760    BRp$c.load = function () {
 24761      var r = this;
 24762  
 24763      var isSelected = function isSelected(ele) {
 24764        return ele.selected();
 24765      };
 24766  
 24767      var triggerEvents = function triggerEvents(target, names, e, position) {
 24768        if (target == null) {
 24769          target = r.cy;
 24770        }
 24771  
 24772        for (var i = 0; i < names.length; i++) {
 24773          var name = names[i];
 24774          target.emit({
 24775            originalEvent: e,
 24776            type: name,
 24777            position: position
 24778          });
 24779        }
 24780      };
 24781  
 24782      var isMultSelKeyDown = function isMultSelKeyDown(e) {
 24783        return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
 24784      };
 24785  
 24786      var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
 24787        var allowPassthrough = true;
 24788  
 24789        if (r.cy.hasCompoundNodes() && down && down.pannable()) {
 24790          // a grabbable compound node below the ele => no passthrough panning
 24791          for (var i = 0; downs && i < downs.length; i++) {
 24792            var down = downs[i];
 24793  
 24794            if (down.isNode() && down.isParent()) {
 24795              allowPassthrough = false;
 24796              break;
 24797            }
 24798          }
 24799        } else {
 24800          allowPassthrough = true;
 24801        }
 24802  
 24803        return allowPassthrough;
 24804      };
 24805  
 24806      var setGrabbed = function setGrabbed(ele) {
 24807        ele[0]._private.grabbed = true;
 24808      };
 24809  
 24810      var setFreed = function setFreed(ele) {
 24811        ele[0]._private.grabbed = false;
 24812      };
 24813  
 24814      var setInDragLayer = function setInDragLayer(ele) {
 24815        ele[0]._private.rscratch.inDragLayer = true;
 24816      };
 24817  
 24818      var setOutDragLayer = function setOutDragLayer(ele) {
 24819        ele[0]._private.rscratch.inDragLayer = false;
 24820      };
 24821  
 24822      var setGrabTarget = function setGrabTarget(ele) {
 24823        ele[0]._private.rscratch.isGrabTarget = true;
 24824      };
 24825  
 24826      var removeGrabTarget = function removeGrabTarget(ele) {
 24827        ele[0]._private.rscratch.isGrabTarget = false;
 24828      };
 24829  
 24830      var addToDragList = function addToDragList(ele, opts) {
 24831        var list = opts.addToList;
 24832        var listHasEle = list.has(ele);
 24833  
 24834        if (!listHasEle) {
 24835          list.merge(ele);
 24836          setGrabbed(ele);
 24837        }
 24838      }; // helper function to determine which child nodes and inner edges
 24839      // of a compound node to be dragged as well as the grabbed and selected nodes
 24840  
 24841  
 24842      var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
 24843        if (!node.cy().hasCompoundNodes()) {
 24844          return;
 24845        }
 24846  
 24847        if (opts.inDragLayer == null && opts.addToList == null) {
 24848          return;
 24849        } // nothing to do
 24850  
 24851  
 24852        var innerNodes = node.descendants();
 24853  
 24854        if (opts.inDragLayer) {
 24855          innerNodes.forEach(setInDragLayer);
 24856          innerNodes.connectedEdges().forEach(setInDragLayer);
 24857        }
 24858  
 24859        if (opts.addToList) {
 24860          opts.addToList.unmerge(innerNodes);
 24861        }
 24862      }; // adds the given nodes and its neighbourhood to the drag layer
 24863  
 24864  
 24865      var addNodesToDrag = function addNodesToDrag(nodes, opts) {
 24866        opts = opts || {};
 24867        var hasCompoundNodes = nodes.cy().hasCompoundNodes();
 24868  
 24869        if (opts.inDragLayer) {
 24870          nodes.forEach(setInDragLayer);
 24871          nodes.neighborhood().stdFilter(function (ele) {
 24872            return !hasCompoundNodes || ele.isEdge();
 24873          }).forEach(setInDragLayer);
 24874        }
 24875  
 24876        if (opts.addToList) {
 24877          nodes.forEach(function (ele) {
 24878            addToDragList(ele, opts);
 24879          });
 24880        }
 24881  
 24882        addDescendantsToDrag(nodes, opts); // always add to drag
 24883        // also add nodes and edges related to the topmost ancestor
 24884  
 24885        updateAncestorsInDragLayer(nodes, {
 24886          inDragLayer: opts.inDragLayer
 24887        });
 24888        r.updateCachedGrabbedEles();
 24889      };
 24890  
 24891      var addNodeToDrag = addNodesToDrag;
 24892  
 24893      var freeDraggedElements = function freeDraggedElements(grabbedEles) {
 24894        if (!grabbedEles) {
 24895          return;
 24896        } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
 24897  
 24898  
 24899        r.getCachedZSortedEles().forEach(function (ele) {
 24900          setFreed(ele);
 24901          setOutDragLayer(ele);
 24902          removeGrabTarget(ele);
 24903        });
 24904        r.updateCachedGrabbedEles();
 24905      }; // helper function to determine which ancestor nodes and edges should go
 24906      // to the drag layer (or should be removed from drag layer).
 24907  
 24908  
 24909      var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
 24910        if (opts.inDragLayer == null && opts.addToList == null) {
 24911          return;
 24912        } // nothing to do
 24913  
 24914  
 24915        if (!node.cy().hasCompoundNodes()) {
 24916          return;
 24917        } // find top-level parent
 24918  
 24919  
 24920        var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
 24921  
 24922        if (parent.same(node)) {
 24923          return;
 24924        }
 24925  
 24926        var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
 24927        var edges = nodes.connectedEdges();
 24928  
 24929        if (opts.inDragLayer) {
 24930          edges.forEach(setInDragLayer);
 24931          nodes.forEach(setInDragLayer);
 24932        }
 24933  
 24934        if (opts.addToList) {
 24935          nodes.forEach(function (ele) {
 24936            addToDragList(ele, opts);
 24937          });
 24938        }
 24939      };
 24940  
 24941      var blurActiveDomElement = function blurActiveDomElement() {
 24942        if (document.activeElement != null && document.activeElement.blur != null) {
 24943          document.activeElement.blur();
 24944        }
 24945      };
 24946  
 24947      var haveMutationsApi = typeof MutationObserver !== 'undefined';
 24948      var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
 24949  
 24950      if (haveMutationsApi) {
 24951        r.removeObserver = new MutationObserver(function (mutns) {
 24952          // eslint-disable-line no-undef
 24953          for (var i = 0; i < mutns.length; i++) {
 24954            var mutn = mutns[i];
 24955            var rNodes = mutn.removedNodes;
 24956  
 24957            if (rNodes) {
 24958              for (var j = 0; j < rNodes.length; j++) {
 24959                var rNode = rNodes[j];
 24960  
 24961                if (rNode === r.container) {
 24962                  r.destroy();
 24963                  break;
 24964                }
 24965              }
 24966            }
 24967          }
 24968        });
 24969  
 24970        if (r.container.parentNode) {
 24971          r.removeObserver.observe(r.container.parentNode, {
 24972            childList: true
 24973          });
 24974        }
 24975      } else {
 24976        r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
 24977          // eslint-disable-line no-unused-vars
 24978          r.destroy();
 24979        });
 24980      }
 24981  
 24982      var onResize = lodash_debounce(function () {
 24983        r.cy.resize();
 24984      }, 100);
 24985  
 24986      if (haveMutationsApi) {
 24987        r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
 24988  
 24989        r.styleObserver.observe(r.container, {
 24990          attributes: true
 24991        });
 24992      } // auto resize
 24993  
 24994  
 24995      r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
 24996  
 24997      if (haveResizeObserverApi) {
 24998        r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
 24999  
 25000        r.resizeObserver.observe(r.container);
 25001      }
 25002  
 25003      var forEachUp = function forEachUp(domEle, fn) {
 25004        while (domEle != null) {
 25005          fn(domEle);
 25006          domEle = domEle.parentNode;
 25007        }
 25008      };
 25009  
 25010      var invalidateCoords = function invalidateCoords() {
 25011        r.invalidateContainerClientCoordsCache();
 25012      };
 25013  
 25014      forEachUp(r.container, function (domEle) {
 25015        r.registerBinding(domEle, 'transitionend', invalidateCoords);
 25016        r.registerBinding(domEle, 'animationend', invalidateCoords);
 25017        r.registerBinding(domEle, 'scroll', invalidateCoords);
 25018      }); // stop right click menu from appearing on cy
 25019  
 25020      r.registerBinding(r.container, 'contextmenu', function (e) {
 25021        e.preventDefault();
 25022      });
 25023  
 25024      var inBoxSelection = function inBoxSelection() {
 25025        return r.selection[4] !== 0;
 25026      };
 25027  
 25028      var eventInContainer = function eventInContainer(e) {
 25029        // save cycles if mouse events aren't to be captured
 25030        var containerPageCoords = r.findContainerClientCoords();
 25031        var x = containerPageCoords[0];
 25032        var y = containerPageCoords[1];
 25033        var width = containerPageCoords[2];
 25034        var height = containerPageCoords[3];
 25035        var positions = e.touches ? e.touches : [e];
 25036        var atLeastOnePosInside = false;
 25037  
 25038        for (var i = 0; i < positions.length; i++) {
 25039          var p = positions[i];
 25040  
 25041          if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
 25042            atLeastOnePosInside = true;
 25043            break;
 25044          }
 25045        }
 25046  
 25047        if (!atLeastOnePosInside) {
 25048          return false;
 25049        }
 25050  
 25051        var container = r.container;
 25052        var target = e.target;
 25053        var tParent = target.parentNode;
 25054        var containerIsTarget = false;
 25055  
 25056        while (tParent) {
 25057          if (tParent === container) {
 25058            containerIsTarget = true;
 25059            break;
 25060          }
 25061  
 25062          tParent = tParent.parentNode;
 25063        }
 25064  
 25065        if (!containerIsTarget) {
 25066          return false;
 25067        } // if target is outisde cy container, then this event is not for us
 25068  
 25069  
 25070        return true;
 25071      }; // Primary key
 25072  
 25073  
 25074      r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
 25075        if (!eventInContainer(e)) {
 25076          return;
 25077        }
 25078  
 25079        e.preventDefault();
 25080        blurActiveDomElement();
 25081        r.hoverData.capture = true;
 25082        r.hoverData.which = e.which;
 25083        var cy = r.cy;
 25084        var gpos = [e.clientX, e.clientY];
 25085        var pos = r.projectIntoViewport(gpos[0], gpos[1]);
 25086        var select = r.selection;
 25087        var nears = r.findNearestElements(pos[0], pos[1], true, false);
 25088        var near = nears[0];
 25089        var draggedElements = r.dragData.possibleDragElements;
 25090        r.hoverData.mdownPos = pos;
 25091        r.hoverData.mdownGPos = gpos;
 25092  
 25093        var checkForTaphold = function checkForTaphold() {
 25094          r.hoverData.tapholdCancelled = false;
 25095          clearTimeout(r.hoverData.tapholdTimeout);
 25096          r.hoverData.tapholdTimeout = setTimeout(function () {
 25097            if (r.hoverData.tapholdCancelled) {
 25098              return;
 25099            } else {
 25100              var ele = r.hoverData.down;
 25101  
 25102              if (ele) {
 25103                ele.emit({
 25104                  originalEvent: e,
 25105                  type: 'taphold',
 25106                  position: {
 25107                    x: pos[0],
 25108                    y: pos[1]
 25109                  }
 25110                });
 25111              } else {
 25112                cy.emit({
 25113                  originalEvent: e,
 25114                  type: 'taphold',
 25115                  position: {
 25116                    x: pos[0],
 25117                    y: pos[1]
 25118                  }
 25119                });
 25120              }
 25121            }
 25122          }, r.tapholdDuration);
 25123        }; // Right click button
 25124  
 25125  
 25126        if (e.which == 3) {
 25127          r.hoverData.cxtStarted = true;
 25128          var cxtEvt = {
 25129            originalEvent: e,
 25130            type: 'cxttapstart',
 25131            position: {
 25132              x: pos[0],
 25133              y: pos[1]
 25134            }
 25135          };
 25136  
 25137          if (near) {
 25138            near.activate();
 25139            near.emit(cxtEvt);
 25140            r.hoverData.down = near;
 25141          } else {
 25142            cy.emit(cxtEvt);
 25143          }
 25144  
 25145          r.hoverData.downTime = new Date().getTime();
 25146          r.hoverData.cxtDragged = false; // Primary button
 25147        } else if (e.which == 1) {
 25148          if (near) {
 25149            near.activate();
 25150          } // Element dragging
 25151  
 25152  
 25153          {
 25154            // If something is under the cursor and it is draggable, prepare to grab it
 25155            if (near != null) {
 25156              if (r.nodeIsGrabbable(near)) {
 25157                var makeEvent = function makeEvent(type) {
 25158                  return {
 25159                    originalEvent: e,
 25160                    type: type,
 25161                    position: {
 25162                      x: pos[0],
 25163                      y: pos[1]
 25164                    }
 25165                  };
 25166                };
 25167  
 25168                var triggerGrab = function triggerGrab(ele) {
 25169                  ele.emit(makeEvent('grab'));
 25170                };
 25171  
 25172                setGrabTarget(near);
 25173  
 25174                if (!near.selected()) {
 25175                  draggedElements = r.dragData.possibleDragElements = cy.collection();
 25176                  addNodeToDrag(near, {
 25177                    addToList: draggedElements
 25178                  });
 25179                  near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
 25180                } else {
 25181                  draggedElements = r.dragData.possibleDragElements = cy.collection();
 25182                  var selectedNodes = cy.$(function (ele) {
 25183                    return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
 25184                  });
 25185                  addNodesToDrag(selectedNodes, {
 25186                    addToList: draggedElements
 25187                  });
 25188                  near.emit(makeEvent('grabon'));
 25189                  selectedNodes.forEach(triggerGrab);
 25190                }
 25191  
 25192                r.redrawHint('eles', true);
 25193                r.redrawHint('drag', true);
 25194              }
 25195            }
 25196  
 25197            r.hoverData.down = near;
 25198            r.hoverData.downs = nears;
 25199            r.hoverData.downTime = new Date().getTime();
 25200          }
 25201          triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
 25202            x: pos[0],
 25203            y: pos[1]
 25204          });
 25205  
 25206          if (near == null) {
 25207            select[4] = 1;
 25208            r.data.bgActivePosistion = {
 25209              x: pos[0],
 25210              y: pos[1]
 25211            };
 25212            r.redrawHint('select', true);
 25213            r.redraw();
 25214          } else if (near.pannable()) {
 25215            select[4] = 1; // for future pan
 25216          }
 25217  
 25218          checkForTaphold();
 25219        } // Initialize selection box coordinates
 25220  
 25221  
 25222        select[0] = select[2] = pos[0];
 25223        select[1] = select[3] = pos[1];
 25224      }, false);
 25225      r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
 25226        // eslint-disable-line no-undef
 25227        var capture = r.hoverData.capture;
 25228  
 25229        if (!capture && !eventInContainer(e)) {
 25230          return;
 25231        }
 25232  
 25233        var preventDefault = false;
 25234        var cy = r.cy;
 25235        var zoom = cy.zoom();
 25236        var gpos = [e.clientX, e.clientY];
 25237        var pos = r.projectIntoViewport(gpos[0], gpos[1]);
 25238        var mdownPos = r.hoverData.mdownPos;
 25239        var mdownGPos = r.hoverData.mdownGPos;
 25240        var select = r.selection;
 25241        var near = null;
 25242  
 25243        if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
 25244          near = r.findNearestElement(pos[0], pos[1], true, false);
 25245        }
 25246  
 25247        var last = r.hoverData.last;
 25248        var down = r.hoverData.down;
 25249        var disp = [pos[0] - select[2], pos[1] - select[3]];
 25250        var draggedElements = r.dragData.possibleDragElements;
 25251        var isOverThresholdDrag;
 25252  
 25253        if (mdownGPos) {
 25254          var dx = gpos[0] - mdownGPos[0];
 25255          var dx2 = dx * dx;
 25256          var dy = gpos[1] - mdownGPos[1];
 25257          var dy2 = dy * dy;
 25258          var dist2 = dx2 + dy2;
 25259          r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
 25260        }
 25261  
 25262        var multSelKeyDown = isMultSelKeyDown(e);
 25263  
 25264        if (isOverThresholdDrag) {
 25265          r.hoverData.tapholdCancelled = true;
 25266        }
 25267  
 25268        var updateDragDelta = function updateDragDelta() {
 25269          var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
 25270  
 25271          if (dragDelta.length === 0) {
 25272            dragDelta.push(disp[0]);
 25273            dragDelta.push(disp[1]);
 25274          } else {
 25275            dragDelta[0] += disp[0];
 25276            dragDelta[1] += disp[1];
 25277          }
 25278        };
 25279  
 25280        preventDefault = true;
 25281        triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
 25282          x: pos[0],
 25283          y: pos[1]
 25284        });
 25285  
 25286        var goIntoBoxMode = function goIntoBoxMode() {
 25287          r.data.bgActivePosistion = undefined;
 25288  
 25289          if (!r.hoverData.selecting) {
 25290            cy.emit({
 25291              originalEvent: e,
 25292              type: 'boxstart',
 25293              position: {
 25294                x: pos[0],
 25295                y: pos[1]
 25296              }
 25297            });
 25298          }
 25299  
 25300          select[4] = 1;
 25301          r.hoverData.selecting = true;
 25302          r.redrawHint('select', true);
 25303          r.redraw();
 25304        }; // trigger context drag if rmouse down
 25305  
 25306  
 25307        if (r.hoverData.which === 3) {
 25308          // but only if over threshold
 25309          if (isOverThresholdDrag) {
 25310            var cxtEvt = {
 25311              originalEvent: e,
 25312              type: 'cxtdrag',
 25313              position: {
 25314                x: pos[0],
 25315                y: pos[1]
 25316              }
 25317            };
 25318  
 25319            if (down) {
 25320              down.emit(cxtEvt);
 25321            } else {
 25322              cy.emit(cxtEvt);
 25323            }
 25324  
 25325            r.hoverData.cxtDragged = true;
 25326  
 25327            if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
 25328              if (r.hoverData.cxtOver) {
 25329                r.hoverData.cxtOver.emit({
 25330                  originalEvent: e,
 25331                  type: 'cxtdragout',
 25332                  position: {
 25333                    x: pos[0],
 25334                    y: pos[1]
 25335                  }
 25336                });
 25337              }
 25338  
 25339              r.hoverData.cxtOver = near;
 25340  
 25341              if (near) {
 25342                near.emit({
 25343                  originalEvent: e,
 25344                  type: 'cxtdragover',
 25345                  position: {
 25346                    x: pos[0],
 25347                    y: pos[1]
 25348                  }
 25349                });
 25350              }
 25351            }
 25352          } // Check if we are drag panning the entire graph
 25353  
 25354        } else if (r.hoverData.dragging) {
 25355          preventDefault = true;
 25356  
 25357          if (cy.panningEnabled() && cy.userPanningEnabled()) {
 25358            var deltaP;
 25359  
 25360            if (r.hoverData.justStartedPan) {
 25361              var mdPos = r.hoverData.mdownPos;
 25362              deltaP = {
 25363                x: (pos[0] - mdPos[0]) * zoom,
 25364                y: (pos[1] - mdPos[1]) * zoom
 25365              };
 25366              r.hoverData.justStartedPan = false;
 25367            } else {
 25368              deltaP = {
 25369                x: disp[0] * zoom,
 25370                y: disp[1] * zoom
 25371              };
 25372            }
 25373  
 25374            cy.panBy(deltaP);
 25375            r.hoverData.dragged = true;
 25376          } // Needs reproject due to pan changing viewport
 25377  
 25378  
 25379          pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
 25380        } else if (select[4] == 1 && (down == null || down.pannable())) {
 25381          if (isOverThresholdDrag) {
 25382            if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
 25383              goIntoBoxMode();
 25384            } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
 25385              var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
 25386  
 25387              if (allowPassthrough) {
 25388                r.hoverData.dragging = true;
 25389                r.hoverData.justStartedPan = true;
 25390                select[4] = 0;
 25391                r.data.bgActivePosistion = array2point(mdownPos);
 25392                r.redrawHint('select', true);
 25393                r.redraw();
 25394              }
 25395            }
 25396  
 25397            if (down && down.pannable() && down.active()) {
 25398              down.unactivate();
 25399            }
 25400          }
 25401        } else {
 25402          if (down && down.pannable() && down.active()) {
 25403            down.unactivate();
 25404          }
 25405  
 25406          if ((!down || !down.grabbed()) && near != last) {
 25407            if (last) {
 25408              triggerEvents(last, ['mouseout', 'tapdragout'], e, {
 25409                x: pos[0],
 25410                y: pos[1]
 25411              });
 25412            }
 25413  
 25414            if (near) {
 25415              triggerEvents(near, ['mouseover', 'tapdragover'], e, {
 25416                x: pos[0],
 25417                y: pos[1]
 25418              });
 25419            }
 25420  
 25421            r.hoverData.last = near;
 25422          }
 25423  
 25424          if (down) {
 25425            if (isOverThresholdDrag) {
 25426              // then we can take action
 25427              if (cy.boxSelectionEnabled() && multSelKeyDown) {
 25428                // then selection overrides
 25429                if (down && down.grabbed()) {
 25430                  freeDraggedElements(draggedElements);
 25431                  down.emit('freeon');
 25432                  draggedElements.emit('free');
 25433  
 25434                  if (r.dragData.didDrag) {
 25435                    down.emit('dragfreeon');
 25436                    draggedElements.emit('dragfree');
 25437                  }
 25438                }
 25439  
 25440                goIntoBoxMode();
 25441              } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
 25442                // drag node
 25443                var justStartedDrag = !r.dragData.didDrag;
 25444  
 25445                if (justStartedDrag) {
 25446                  r.redrawHint('eles', true);
 25447                }
 25448  
 25449                r.dragData.didDrag = true; // indicate that we actually did drag the node
 25450  
 25451                var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
 25452  
 25453                if (!r.hoverData.draggingEles) {
 25454                  addNodesToDrag(draggedElements, {
 25455                    inDragLayer: true
 25456                  });
 25457                }
 25458  
 25459                var totalShift = {
 25460                  x: 0,
 25461                  y: 0
 25462                };
 25463  
 25464                if (number(disp[0]) && number(disp[1])) {
 25465                  totalShift.x += disp[0];
 25466                  totalShift.y += disp[1];
 25467  
 25468                  if (justStartedDrag) {
 25469                    var dragDelta = r.hoverData.dragDelta;
 25470  
 25471                    if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
 25472                      totalShift.x += dragDelta[0];
 25473                      totalShift.y += dragDelta[1];
 25474                    }
 25475                  }
 25476                }
 25477  
 25478                for (var i = 0; i < draggedElements.length; i++) {
 25479                  var dEle = draggedElements[i];
 25480  
 25481                  if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
 25482                    toTrigger.merge(dEle);
 25483                  }
 25484                }
 25485  
 25486                r.hoverData.draggingEles = true;
 25487                toTrigger.silentShift(totalShift).emit('position drag');
 25488                r.redrawHint('drag', true);
 25489                r.redraw();
 25490              }
 25491            } else {
 25492              // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
 25493              updateDragDelta();
 25494            }
 25495          } // prevent the dragging from triggering text selection on the page
 25496  
 25497  
 25498          preventDefault = true;
 25499        }
 25500  
 25501        select[2] = pos[0];
 25502        select[3] = pos[1];
 25503  
 25504        if (preventDefault) {
 25505          if (e.stopPropagation) e.stopPropagation();
 25506          if (e.preventDefault) e.preventDefault();
 25507          return false;
 25508        }
 25509      }, false);
 25510      r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
 25511        // eslint-disable-line no-undef
 25512        var capture = r.hoverData.capture;
 25513  
 25514        if (!capture) {
 25515          return;
 25516        }
 25517  
 25518        r.hoverData.capture = false;
 25519        var cy = r.cy;
 25520        var pos = r.projectIntoViewport(e.clientX, e.clientY);
 25521        var select = r.selection;
 25522        var near = r.findNearestElement(pos[0], pos[1], true, false);
 25523        var draggedElements = r.dragData.possibleDragElements;
 25524        var down = r.hoverData.down;
 25525        var multSelKeyDown = isMultSelKeyDown(e);
 25526  
 25527        if (r.data.bgActivePosistion) {
 25528          r.redrawHint('select', true);
 25529          r.redraw();
 25530        }
 25531  
 25532        r.hoverData.tapholdCancelled = true;
 25533        r.data.bgActivePosistion = undefined; // not active bg now
 25534  
 25535        if (down) {
 25536          down.unactivate();
 25537        }
 25538  
 25539        if (r.hoverData.which === 3) {
 25540          var cxtEvt = {
 25541            originalEvent: e,
 25542            type: 'cxttapend',
 25543            position: {
 25544              x: pos[0],
 25545              y: pos[1]
 25546            }
 25547          };
 25548  
 25549          if (down) {
 25550            down.emit(cxtEvt);
 25551          } else {
 25552            cy.emit(cxtEvt);
 25553          }
 25554  
 25555          if (!r.hoverData.cxtDragged) {
 25556            var cxtTap = {
 25557              originalEvent: e,
 25558              type: 'cxttap',
 25559              position: {
 25560                x: pos[0],
 25561                y: pos[1]
 25562              }
 25563            };
 25564  
 25565            if (down) {
 25566              down.emit(cxtTap);
 25567            } else {
 25568              cy.emit(cxtTap);
 25569            }
 25570          }
 25571  
 25572          r.hoverData.cxtDragged = false;
 25573          r.hoverData.which = null;
 25574        } else if (r.hoverData.which === 1) {
 25575          triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
 25576            x: pos[0],
 25577            y: pos[1]
 25578          });
 25579  
 25580          if (!r.dragData.didDrag // didn't move a node around
 25581          && !r.hoverData.dragged // didn't pan
 25582          && !r.hoverData.selecting // not box selection
 25583          && !r.hoverData.isOverThresholdDrag // didn't move too much
 25584          ) {
 25585              triggerEvents(down, ['click', 'tap', 'vclick'], e, {
 25586                x: pos[0],
 25587                y: pos[1]
 25588              });
 25589            } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
 25590  
 25591  
 25592          if (down == null && // not mousedown on node
 25593          !r.dragData.didDrag // didn't move the node around
 25594          && !r.hoverData.selecting // not box selection
 25595          && !r.hoverData.dragged // didn't pan
 25596          && !isMultSelKeyDown(e)) {
 25597            cy.$(isSelected).unselect(['tapunselect']);
 25598  
 25599            if (draggedElements.length > 0) {
 25600              r.redrawHint('eles', true);
 25601            }
 25602  
 25603            r.dragData.possibleDragElements = draggedElements = cy.collection();
 25604          } // Single selection
 25605  
 25606  
 25607          if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
 25608            if (near != null && near._private.selectable) {
 25609              if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
 25610                if (near.selected()) {
 25611                  near.unselect(['tapunselect']);
 25612                } else {
 25613                  near.select(['tapselect']);
 25614                }
 25615              } else {
 25616                if (!multSelKeyDown) {
 25617                  cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
 25618                  near.select(['tapselect']);
 25619                }
 25620              }
 25621  
 25622              r.redrawHint('eles', true);
 25623            }
 25624          }
 25625  
 25626          if (r.hoverData.selecting) {
 25627            var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
 25628            r.redrawHint('select', true);
 25629  
 25630            if (box.length > 0) {
 25631              r.redrawHint('eles', true);
 25632            }
 25633  
 25634            cy.emit({
 25635              type: 'boxend',
 25636              originalEvent: e,
 25637              position: {
 25638                x: pos[0],
 25639                y: pos[1]
 25640              }
 25641            });
 25642  
 25643            var eleWouldBeSelected = function eleWouldBeSelected(ele) {
 25644              return ele.selectable() && !ele.selected();
 25645            };
 25646  
 25647            if (cy.selectionType() === 'additive') {
 25648              box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
 25649            } else {
 25650              if (!multSelKeyDown) {
 25651                cy.$(isSelected).unmerge(box).unselect();
 25652              }
 25653  
 25654              box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
 25655            } // always need redraw in case eles unselectable
 25656  
 25657  
 25658            r.redraw();
 25659          } // Cancel drag pan
 25660  
 25661  
 25662          if (r.hoverData.dragging) {
 25663            r.hoverData.dragging = false;
 25664            r.redrawHint('select', true);
 25665            r.redrawHint('eles', true);
 25666            r.redraw();
 25667          }
 25668  
 25669          if (!select[4]) {
 25670            r.redrawHint('drag', true);
 25671            r.redrawHint('eles', true);
 25672            var downWasGrabbed = down && down.grabbed();
 25673            freeDraggedElements(draggedElements);
 25674  
 25675            if (downWasGrabbed) {
 25676              down.emit('freeon');
 25677              draggedElements.emit('free');
 25678  
 25679              if (r.dragData.didDrag) {
 25680                down.emit('dragfreeon');
 25681                draggedElements.emit('dragfree');
 25682              }
 25683            }
 25684          }
 25685        } // else not right mouse
 25686  
 25687  
 25688        select[4] = 0;
 25689        r.hoverData.down = null;
 25690        r.hoverData.cxtStarted = false;
 25691        r.hoverData.draggingEles = false;
 25692        r.hoverData.selecting = false;
 25693        r.hoverData.isOverThresholdDrag = false;
 25694        r.dragData.didDrag = false;
 25695        r.hoverData.dragged = false;
 25696        r.hoverData.dragDelta = [];
 25697        r.hoverData.mdownPos = null;
 25698        r.hoverData.mdownGPos = null;
 25699      }, false);
 25700  
 25701      var wheelHandler = function wheelHandler(e) {
 25702        if (r.scrollingPage) {
 25703          return;
 25704        } // while scrolling, ignore wheel-to-zoom
 25705  
 25706  
 25707        var cy = r.cy;
 25708        var pos = r.projectIntoViewport(e.clientX, e.clientY);
 25709        var rpos = [pos[0] * cy.zoom() + cy.pan().x, pos[1] * cy.zoom() + cy.pan().y];
 25710  
 25711        if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
 25712          // if pan dragging or cxt dragging, wheel movements make no zoom
 25713          e.preventDefault();
 25714          return;
 25715        }
 25716  
 25717        if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
 25718          e.preventDefault();
 25719          r.data.wheelZooming = true;
 25720          clearTimeout(r.data.wheelTimeout);
 25721          r.data.wheelTimeout = setTimeout(function () {
 25722            r.data.wheelZooming = false;
 25723            r.redrawHint('eles', true);
 25724            r.redraw();
 25725          }, 150);
 25726          var diff;
 25727  
 25728          if (e.deltaY != null) {
 25729            diff = e.deltaY / -250;
 25730          } else if (e.wheelDeltaY != null) {
 25731            diff = e.wheelDeltaY / 1000;
 25732          } else {
 25733            diff = e.wheelDelta / 1000;
 25734          }
 25735  
 25736          diff = diff * r.wheelSensitivity;
 25737          var needsWheelFix = e.deltaMode === 1;
 25738  
 25739          if (needsWheelFix) {
 25740            // fixes slow wheel events on ff/linux and ff/windows
 25741            diff *= 33;
 25742          }
 25743  
 25744          cy.zoom({
 25745            level: cy.zoom() * Math.pow(10, diff),
 25746            renderedPosition: {
 25747              x: rpos[0],
 25748              y: rpos[1]
 25749            }
 25750          });
 25751        }
 25752      }; // Functions to help with whether mouse wheel should trigger zooming
 25753      // --
 25754  
 25755  
 25756      r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
 25757      // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
 25758      // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
 25759      // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
 25760  
 25761      r.registerBinding(window, 'scroll', function scrollHandler(e) {
 25762        // eslint-disable-line no-unused-vars
 25763        r.scrollingPage = true;
 25764        clearTimeout(r.scrollingPageTimeout);
 25765        r.scrollingPageTimeout = setTimeout(function () {
 25766          r.scrollingPage = false;
 25767        }, 250);
 25768      }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
 25769      // Handle mouseout on Cytoscape container
 25770  
 25771      r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
 25772        var pos = r.projectIntoViewport(e.clientX, e.clientY);
 25773        r.cy.emit({
 25774          originalEvent: e,
 25775          type: 'mouseout',
 25776          position: {
 25777            x: pos[0],
 25778            y: pos[1]
 25779          }
 25780        });
 25781      }, false);
 25782      r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
 25783        var pos = r.projectIntoViewport(e.clientX, e.clientY);
 25784        r.cy.emit({
 25785          originalEvent: e,
 25786          type: 'mouseover',
 25787          position: {
 25788            x: pos[0],
 25789            y: pos[1]
 25790          }
 25791        });
 25792      }, false);
 25793      var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
 25794  
 25795      var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
 25796  
 25797      var center1, modelCenter1; // center point on start pinch to zoom
 25798  
 25799      var offsetLeft, offsetTop;
 25800      var containerWidth, containerHeight;
 25801      var twoFingersStartInside;
 25802  
 25803      var distance = function distance(x1, y1, x2, y2) {
 25804        return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
 25805      };
 25806  
 25807      var distanceSq = function distanceSq(x1, y1, x2, y2) {
 25808        return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
 25809      };
 25810  
 25811      var touchstartHandler;
 25812      r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
 25813        if (!eventInContainer(e)) {
 25814          return;
 25815        }
 25816  
 25817        blurActiveDomElement();
 25818        r.touchData.capture = true;
 25819        r.data.bgActivePosistion = undefined;
 25820        var cy = r.cy;
 25821        var now = r.touchData.now;
 25822        var earlier = r.touchData.earlier;
 25823  
 25824        if (e.touches[0]) {
 25825          var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
 25826          now[0] = pos[0];
 25827          now[1] = pos[1];
 25828        }
 25829  
 25830        if (e.touches[1]) {
 25831          var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
 25832          now[2] = pos[0];
 25833          now[3] = pos[1];
 25834        }
 25835  
 25836        if (e.touches[2]) {
 25837          var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
 25838          now[4] = pos[0];
 25839          now[5] = pos[1];
 25840        } // record starting points for pinch-to-zoom
 25841  
 25842  
 25843        if (e.touches[1]) {
 25844          r.touchData.singleTouchMoved = true;
 25845          freeDraggedElements(r.dragData.touchDragEles);
 25846          var offsets = r.findContainerClientCoords();
 25847          offsetLeft = offsets[0];
 25848          offsetTop = offsets[1];
 25849          containerWidth = offsets[2];
 25850          containerHeight = offsets[3];
 25851          f1x1 = e.touches[0].clientX - offsetLeft;
 25852          f1y1 = e.touches[0].clientY - offsetTop;
 25853          f2x1 = e.touches[1].clientX - offsetLeft;
 25854          f2y1 = e.touches[1].clientY - offsetTop;
 25855          twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
 25856          var pan = cy.pan();
 25857          var zoom = cy.zoom();
 25858          distance1 = distance(f1x1, f1y1, f2x1, f2y1);
 25859          distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
 25860          center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
 25861          modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
 25862  
 25863          var cxtDistThreshold = 200;
 25864          var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
 25865  
 25866          if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
 25867            var near1 = r.findNearestElement(now[0], now[1], true, true);
 25868            var near2 = r.findNearestElement(now[2], now[3], true, true);
 25869  
 25870            if (near1 && near1.isNode()) {
 25871              near1.activate().emit({
 25872                originalEvent: e,
 25873                type: 'cxttapstart',
 25874                position: {
 25875                  x: now[0],
 25876                  y: now[1]
 25877                }
 25878              });
 25879              r.touchData.start = near1;
 25880            } else if (near2 && near2.isNode()) {
 25881              near2.activate().emit({
 25882                originalEvent: e,
 25883                type: 'cxttapstart',
 25884                position: {
 25885                  x: now[0],
 25886                  y: now[1]
 25887                }
 25888              });
 25889              r.touchData.start = near2;
 25890            } else {
 25891              cy.emit({
 25892                originalEvent: e,
 25893                type: 'cxttapstart',
 25894                position: {
 25895                  x: now[0],
 25896                  y: now[1]
 25897                }
 25898              });
 25899            }
 25900  
 25901            if (r.touchData.start) {
 25902              r.touchData.start._private.grabbed = false;
 25903            }
 25904  
 25905            r.touchData.cxt = true;
 25906            r.touchData.cxtDragged = false;
 25907            r.data.bgActivePosistion = undefined;
 25908            r.redraw();
 25909            return;
 25910          }
 25911        }
 25912  
 25913        if (e.touches[2]) {
 25914          // ignore
 25915          // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
 25916          if (cy.boxSelectionEnabled()) {
 25917            e.preventDefault();
 25918          }
 25919        } else if (e.touches[1]) ; else if (e.touches[0]) {
 25920          var nears = r.findNearestElements(now[0], now[1], true, true);
 25921          var near = nears[0];
 25922  
 25923          if (near != null) {
 25924            near.activate();
 25925            r.touchData.start = near;
 25926            r.touchData.starts = nears;
 25927  
 25928            if (r.nodeIsGrabbable(near)) {
 25929              var draggedEles = r.dragData.touchDragEles = cy.collection();
 25930              var selectedNodes = null;
 25931              r.redrawHint('eles', true);
 25932              r.redrawHint('drag', true);
 25933  
 25934              if (near.selected()) {
 25935                // reset drag elements, since near will be added again
 25936                selectedNodes = cy.$(function (ele) {
 25937                  return ele.selected() && r.nodeIsGrabbable(ele);
 25938                });
 25939                addNodesToDrag(selectedNodes, {
 25940                  addToList: draggedEles
 25941                });
 25942              } else {
 25943                addNodeToDrag(near, {
 25944                  addToList: draggedEles
 25945                });
 25946              }
 25947  
 25948              setGrabTarget(near);
 25949  
 25950              var makeEvent = function makeEvent(type) {
 25951                return {
 25952                  originalEvent: e,
 25953                  type: type,
 25954                  position: {
 25955                    x: now[0],
 25956                    y: now[1]
 25957                  }
 25958                };
 25959              };
 25960  
 25961              near.emit(makeEvent('grabon'));
 25962  
 25963              if (selectedNodes) {
 25964                selectedNodes.forEach(function (n) {
 25965                  n.emit(makeEvent('grab'));
 25966                });
 25967              } else {
 25968                near.emit(makeEvent('grab'));
 25969              }
 25970            }
 25971          }
 25972  
 25973          triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
 25974            x: now[0],
 25975            y: now[1]
 25976          });
 25977  
 25978          if (near == null) {
 25979            r.data.bgActivePosistion = {
 25980              x: pos[0],
 25981              y: pos[1]
 25982            };
 25983            r.redrawHint('select', true);
 25984            r.redraw();
 25985          } // Tap, taphold
 25986          // -----
 25987  
 25988  
 25989          r.touchData.singleTouchMoved = false;
 25990          r.touchData.singleTouchStartTime = +new Date();
 25991          clearTimeout(r.touchData.tapholdTimeout);
 25992          r.touchData.tapholdTimeout = setTimeout(function () {
 25993            if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
 25994            && !r.touchData.selecting // box selection shouldn't allow taphold through
 25995            ) {
 25996                triggerEvents(r.touchData.start, ['taphold'], e, {
 25997                  x: now[0],
 25998                  y: now[1]
 25999                });
 26000              }
 26001          }, r.tapholdDuration);
 26002        }
 26003  
 26004        if (e.touches.length >= 1) {
 26005          var sPos = r.touchData.startPosition = [];
 26006  
 26007          for (var i = 0; i < now.length; i++) {
 26008            sPos[i] = earlier[i] = now[i];
 26009          }
 26010  
 26011          var touch0 = e.touches[0];
 26012          r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
 26013        }
 26014      }, false);
 26015      var touchmoveHandler;
 26016      r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
 26017        // eslint-disable-line no-undef
 26018        var capture = r.touchData.capture;
 26019  
 26020        if (!capture && !eventInContainer(e)) {
 26021          return;
 26022        }
 26023  
 26024        var select = r.selection;
 26025        var cy = r.cy;
 26026        var now = r.touchData.now;
 26027        var earlier = r.touchData.earlier;
 26028        var zoom = cy.zoom();
 26029  
 26030        if (e.touches[0]) {
 26031          var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
 26032          now[0] = pos[0];
 26033          now[1] = pos[1];
 26034        }
 26035  
 26036        if (e.touches[1]) {
 26037          var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
 26038          now[2] = pos[0];
 26039          now[3] = pos[1];
 26040        }
 26041  
 26042        if (e.touches[2]) {
 26043          var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
 26044          now[4] = pos[0];
 26045          now[5] = pos[1];
 26046        }
 26047  
 26048        var startGPos = r.touchData.startGPosition;
 26049        var isOverThresholdDrag;
 26050  
 26051        if (capture && e.touches[0] && startGPos) {
 26052          var disp = [];
 26053  
 26054          for (var j = 0; j < now.length; j++) {
 26055            disp[j] = now[j] - earlier[j];
 26056          }
 26057  
 26058          var dx = e.touches[0].clientX - startGPos[0];
 26059          var dx2 = dx * dx;
 26060          var dy = e.touches[0].clientY - startGPos[1];
 26061          var dy2 = dy * dy;
 26062          var dist2 = dx2 + dy2;
 26063          isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
 26064        } // context swipe cancelling
 26065  
 26066  
 26067        if (capture && r.touchData.cxt) {
 26068          e.preventDefault();
 26069          var f1x2 = e.touches[0].clientX - offsetLeft,
 26070              f1y2 = e.touches[0].clientY - offsetTop;
 26071          var f2x2 = e.touches[1].clientX - offsetLeft,
 26072              f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
 26073  
 26074          var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
 26075          var factorSq = distance2Sq / distance1Sq;
 26076          var distThreshold = 150;
 26077          var distThresholdSq = distThreshold * distThreshold;
 26078          var factorThreshold = 1.5;
 26079          var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
 26080  
 26081          if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
 26082            r.touchData.cxt = false;
 26083            r.data.bgActivePosistion = undefined;
 26084            r.redrawHint('select', true);
 26085            var cxtEvt = {
 26086              originalEvent: e,
 26087              type: 'cxttapend',
 26088              position: {
 26089                x: now[0],
 26090                y: now[1]
 26091              }
 26092            };
 26093  
 26094            if (r.touchData.start) {
 26095              r.touchData.start.unactivate().emit(cxtEvt);
 26096              r.touchData.start = null;
 26097            } else {
 26098              cy.emit(cxtEvt);
 26099            }
 26100          }
 26101        } // context swipe
 26102  
 26103  
 26104        if (capture && r.touchData.cxt) {
 26105          var cxtEvt = {
 26106            originalEvent: e,
 26107            type: 'cxtdrag',
 26108            position: {
 26109              x: now[0],
 26110              y: now[1]
 26111            }
 26112          };
 26113          r.data.bgActivePosistion = undefined;
 26114          r.redrawHint('select', true);
 26115  
 26116          if (r.touchData.start) {
 26117            r.touchData.start.emit(cxtEvt);
 26118          } else {
 26119            cy.emit(cxtEvt);
 26120          }
 26121  
 26122          if (r.touchData.start) {
 26123            r.touchData.start._private.grabbed = false;
 26124          }
 26125  
 26126          r.touchData.cxtDragged = true;
 26127          var near = r.findNearestElement(now[0], now[1], true, true);
 26128  
 26129          if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
 26130            if (r.touchData.cxtOver) {
 26131              r.touchData.cxtOver.emit({
 26132                originalEvent: e,
 26133                type: 'cxtdragout',
 26134                position: {
 26135                  x: now[0],
 26136                  y: now[1]
 26137                }
 26138              });
 26139            }
 26140  
 26141            r.touchData.cxtOver = near;
 26142  
 26143            if (near) {
 26144              near.emit({
 26145                originalEvent: e,
 26146                type: 'cxtdragover',
 26147                position: {
 26148                  x: now[0],
 26149                  y: now[1]
 26150                }
 26151              });
 26152            }
 26153          } // box selection
 26154  
 26155        } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
 26156          e.preventDefault();
 26157          r.data.bgActivePosistion = undefined;
 26158          this.lastThreeTouch = +new Date();
 26159  
 26160          if (!r.touchData.selecting) {
 26161            cy.emit({
 26162              originalEvent: e,
 26163              type: 'boxstart',
 26164              position: {
 26165                x: now[0],
 26166                y: now[1]
 26167              }
 26168            });
 26169          }
 26170  
 26171          r.touchData.selecting = true;
 26172          r.touchData.didSelect = true;
 26173          select[4] = 1;
 26174  
 26175          if (!select || select.length === 0 || select[0] === undefined) {
 26176            select[0] = (now[0] + now[2] + now[4]) / 3;
 26177            select[1] = (now[1] + now[3] + now[5]) / 3;
 26178            select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
 26179            select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
 26180          } else {
 26181            select[2] = (now[0] + now[2] + now[4]) / 3;
 26182            select[3] = (now[1] + now[3] + now[5]) / 3;
 26183          }
 26184  
 26185          r.redrawHint('select', true);
 26186          r.redraw(); // pinch to zoom
 26187        } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
 26188        && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
 26189          // two fingers => pinch to zoom
 26190          e.preventDefault();
 26191          r.data.bgActivePosistion = undefined;
 26192          r.redrawHint('select', true);
 26193          var draggedEles = r.dragData.touchDragEles;
 26194  
 26195          if (draggedEles) {
 26196            r.redrawHint('drag', true);
 26197  
 26198            for (var i = 0; i < draggedEles.length; i++) {
 26199              var de_p = draggedEles[i]._private;
 26200              de_p.grabbed = false;
 26201              de_p.rscratch.inDragLayer = false;
 26202            }
 26203          }
 26204  
 26205          var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
 26206  
 26207          var f1x2 = e.touches[0].clientX - offsetLeft,
 26208              f1y2 = e.touches[0].clientY - offsetTop;
 26209          var f2x2 = e.touches[1].clientX - offsetLeft,
 26210              f2y2 = e.touches[1].clientY - offsetTop;
 26211          var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
 26212          // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
 26213  
 26214          var factor = distance2 / distance1;
 26215  
 26216          if (twoFingersStartInside) {
 26217            // delta finger1
 26218            var df1x = f1x2 - f1x1;
 26219            var df1y = f1y2 - f1y1; // delta finger 2
 26220  
 26221            var df2x = f2x2 - f2x1;
 26222            var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
 26223            // i.e. so pinching cancels out and moving together pans
 26224  
 26225            var tx = (df1x + df2x) / 2;
 26226            var ty = (df1y + df2y) / 2; // now calculate the zoom
 26227  
 26228            var zoom1 = cy.zoom();
 26229            var zoom2 = zoom1 * factor;
 26230            var pan1 = cy.pan(); // the model center point converted to the current rendered pos
 26231  
 26232            var ctrx = modelCenter1[0] * zoom1 + pan1.x;
 26233            var ctry = modelCenter1[1] * zoom1 + pan1.y;
 26234            var pan2 = {
 26235              x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
 26236              y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
 26237            }; // remove dragged eles
 26238  
 26239            if (_start && _start.active()) {
 26240              var draggedEles = r.dragData.touchDragEles;
 26241              freeDraggedElements(draggedEles);
 26242              r.redrawHint('drag', true);
 26243              r.redrawHint('eles', true);
 26244  
 26245              _start.unactivate().emit('freeon');
 26246  
 26247              draggedEles.emit('free');
 26248  
 26249              if (r.dragData.didDrag) {
 26250                _start.emit('dragfreeon');
 26251  
 26252                draggedEles.emit('dragfree');
 26253              }
 26254            }
 26255  
 26256            cy.viewport({
 26257              zoom: zoom2,
 26258              pan: pan2,
 26259              cancelOnFailedZoom: true
 26260            });
 26261            distance1 = distance2;
 26262            f1x1 = f1x2;
 26263            f1y1 = f1y2;
 26264            f2x1 = f2x2;
 26265            f2y1 = f2y2;
 26266            r.pinching = true;
 26267          } // Re-project
 26268  
 26269  
 26270          if (e.touches[0]) {
 26271            var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
 26272            now[0] = pos[0];
 26273            now[1] = pos[1];
 26274          }
 26275  
 26276          if (e.touches[1]) {
 26277            var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
 26278            now[2] = pos[0];
 26279            now[3] = pos[1];
 26280          }
 26281  
 26282          if (e.touches[2]) {
 26283            var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
 26284            now[4] = pos[0];
 26285            now[5] = pos[1];
 26286          }
 26287        } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
 26288        ) {
 26289            var start = r.touchData.start;
 26290            var last = r.touchData.last;
 26291            var near;
 26292  
 26293            if (!r.hoverData.draggingEles && !r.swipePanning) {
 26294              near = r.findNearestElement(now[0], now[1], true, true);
 26295            }
 26296  
 26297            if (capture && start != null) {
 26298              e.preventDefault();
 26299            } // dragging nodes
 26300  
 26301  
 26302            if (capture && start != null && r.nodeIsDraggable(start)) {
 26303              if (isOverThresholdDrag) {
 26304                // then dragging can happen
 26305                var draggedEles = r.dragData.touchDragEles;
 26306                var justStartedDrag = !r.dragData.didDrag;
 26307  
 26308                if (justStartedDrag) {
 26309                  addNodesToDrag(draggedEles, {
 26310                    inDragLayer: true
 26311                  });
 26312                }
 26313  
 26314                r.dragData.didDrag = true;
 26315                var totalShift = {
 26316                  x: 0,
 26317                  y: 0
 26318                };
 26319  
 26320                if (number(disp[0]) && number(disp[1])) {
 26321                  totalShift.x += disp[0];
 26322                  totalShift.y += disp[1];
 26323  
 26324                  if (justStartedDrag) {
 26325                    r.redrawHint('eles', true);
 26326                    var dragDelta = r.touchData.dragDelta;
 26327  
 26328                    if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
 26329                      totalShift.x += dragDelta[0];
 26330                      totalShift.y += dragDelta[1];
 26331                    }
 26332                  }
 26333                }
 26334  
 26335                r.hoverData.draggingEles = true;
 26336                draggedEles.silentShift(totalShift).emit('position drag');
 26337                r.redrawHint('drag', true);
 26338  
 26339                if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
 26340                  r.redrawHint('eles', true);
 26341                }
 26342  
 26343                r.redraw();
 26344              } else {
 26345                // otherise keep track of drag delta for later
 26346                var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
 26347  
 26348                if (dragDelta.length === 0) {
 26349                  dragDelta.push(disp[0]);
 26350                  dragDelta.push(disp[1]);
 26351                } else {
 26352                  dragDelta[0] += disp[0];
 26353                  dragDelta[1] += disp[1];
 26354                }
 26355              }
 26356            } // touchmove
 26357  
 26358  
 26359            {
 26360              triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
 26361                x: now[0],
 26362                y: now[1]
 26363              });
 26364  
 26365              if ((!start || !start.grabbed()) && near != last) {
 26366                if (last) {
 26367                  last.emit({
 26368                    originalEvent: e,
 26369                    type: 'tapdragout',
 26370                    position: {
 26371                      x: now[0],
 26372                      y: now[1]
 26373                    }
 26374                  });
 26375                }
 26376  
 26377                if (near) {
 26378                  near.emit({
 26379                    originalEvent: e,
 26380                    type: 'tapdragover',
 26381                    position: {
 26382                      x: now[0],
 26383                      y: now[1]
 26384                    }
 26385                  });
 26386                }
 26387              }
 26388  
 26389              r.touchData.last = near;
 26390            } // check to cancel taphold
 26391  
 26392            if (capture) {
 26393              for (var i = 0; i < now.length; i++) {
 26394                if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
 26395                  r.touchData.singleTouchMoved = true;
 26396                }
 26397              }
 26398            } // panning
 26399  
 26400  
 26401            if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
 26402              var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
 26403  
 26404              if (allowPassthrough) {
 26405                e.preventDefault();
 26406  
 26407                if (!r.data.bgActivePosistion) {
 26408                  r.data.bgActivePosistion = array2point(r.touchData.startPosition);
 26409                }
 26410  
 26411                if (r.swipePanning) {
 26412                  cy.panBy({
 26413                    x: disp[0] * zoom,
 26414                    y: disp[1] * zoom
 26415                  });
 26416                } else if (isOverThresholdDrag) {
 26417                  r.swipePanning = true;
 26418                  cy.panBy({
 26419                    x: dx * zoom,
 26420                    y: dy * zoom
 26421                  });
 26422  
 26423                  if (start) {
 26424                    start.unactivate();
 26425                    r.redrawHint('select', true);
 26426                    r.touchData.start = null;
 26427                  }
 26428                }
 26429              } // Re-project
 26430  
 26431  
 26432              var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
 26433              now[0] = pos[0];
 26434              now[1] = pos[1];
 26435            }
 26436          }
 26437  
 26438        for (var j = 0; j < now.length; j++) {
 26439          earlier[j] = now[j];
 26440        } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
 26441  
 26442  
 26443        if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
 26444          r.data.bgActivePosistion = undefined;
 26445          r.redrawHint('select', true);
 26446          r.redraw();
 26447        }
 26448      }, false);
 26449      var touchcancelHandler;
 26450      r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
 26451        // eslint-disable-line no-unused-vars
 26452        var start = r.touchData.start;
 26453        r.touchData.capture = false;
 26454  
 26455        if (start) {
 26456          start.unactivate();
 26457        }
 26458      });
 26459      var touchendHandler;
 26460      r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
 26461        // eslint-disable-line no-unused-vars
 26462        var start = r.touchData.start;
 26463        var capture = r.touchData.capture;
 26464  
 26465        if (capture) {
 26466          if (e.touches.length === 0) {
 26467            r.touchData.capture = false;
 26468          }
 26469  
 26470          e.preventDefault();
 26471        } else {
 26472          return;
 26473        }
 26474  
 26475        var select = r.selection;
 26476        r.swipePanning = false;
 26477        r.hoverData.draggingEles = false;
 26478        var cy = r.cy;
 26479        var zoom = cy.zoom();
 26480        var now = r.touchData.now;
 26481        var earlier = r.touchData.earlier;
 26482  
 26483        if (e.touches[0]) {
 26484          var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
 26485          now[0] = pos[0];
 26486          now[1] = pos[1];
 26487        }
 26488  
 26489        if (e.touches[1]) {
 26490          var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
 26491          now[2] = pos[0];
 26492          now[3] = pos[1];
 26493        }
 26494  
 26495        if (e.touches[2]) {
 26496          var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
 26497          now[4] = pos[0];
 26498          now[5] = pos[1];
 26499        }
 26500  
 26501        if (start) {
 26502          start.unactivate();
 26503        }
 26504  
 26505        var ctxTapend;
 26506  
 26507        if (r.touchData.cxt) {
 26508          ctxTapend = {
 26509            originalEvent: e,
 26510            type: 'cxttapend',
 26511            position: {
 26512              x: now[0],
 26513              y: now[1]
 26514            }
 26515          };
 26516  
 26517          if (start) {
 26518            start.emit(ctxTapend);
 26519          } else {
 26520            cy.emit(ctxTapend);
 26521          }
 26522  
 26523          if (!r.touchData.cxtDragged) {
 26524            var ctxTap = {
 26525              originalEvent: e,
 26526              type: 'cxttap',
 26527              position: {
 26528                x: now[0],
 26529                y: now[1]
 26530              }
 26531            };
 26532  
 26533            if (start) {
 26534              start.emit(ctxTap);
 26535            } else {
 26536              cy.emit(ctxTap);
 26537            }
 26538          }
 26539  
 26540          if (r.touchData.start) {
 26541            r.touchData.start._private.grabbed = false;
 26542          }
 26543  
 26544          r.touchData.cxt = false;
 26545          r.touchData.start = null;
 26546          r.redraw();
 26547          return;
 26548        } // no more box selection if we don't have three fingers
 26549  
 26550  
 26551        if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
 26552          r.touchData.selecting = false;
 26553          var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
 26554          select[0] = undefined;
 26555          select[1] = undefined;
 26556          select[2] = undefined;
 26557          select[3] = undefined;
 26558          select[4] = 0;
 26559          r.redrawHint('select', true);
 26560          cy.emit({
 26561            type: 'boxend',
 26562            originalEvent: e,
 26563            position: {
 26564              x: now[0],
 26565              y: now[1]
 26566            }
 26567          });
 26568  
 26569          var eleWouldBeSelected = function eleWouldBeSelected(ele) {
 26570            return ele.selectable() && !ele.selected();
 26571          };
 26572  
 26573          box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
 26574  
 26575          if (box.nonempty()) {
 26576            r.redrawHint('eles', true);
 26577          }
 26578  
 26579          r.redraw();
 26580        }
 26581  
 26582        if (start != null) {
 26583          start.unactivate();
 26584        }
 26585  
 26586        if (e.touches[2]) {
 26587          r.data.bgActivePosistion = undefined;
 26588          r.redrawHint('select', true);
 26589        } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
 26590          r.data.bgActivePosistion = undefined;
 26591          r.redrawHint('select', true);
 26592          var draggedEles = r.dragData.touchDragEles;
 26593  
 26594          if (start != null) {
 26595            var startWasGrabbed = start._private.grabbed;
 26596            freeDraggedElements(draggedEles);
 26597            r.redrawHint('drag', true);
 26598            r.redrawHint('eles', true);
 26599  
 26600            if (startWasGrabbed) {
 26601              start.emit('freeon');
 26602              draggedEles.emit('free');
 26603  
 26604              if (r.dragData.didDrag) {
 26605                start.emit('dragfreeon');
 26606                draggedEles.emit('dragfree');
 26607              }
 26608            }
 26609  
 26610            triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
 26611              x: now[0],
 26612              y: now[1]
 26613            });
 26614            start.unactivate();
 26615            r.touchData.start = null;
 26616          } else {
 26617            var near = r.findNearestElement(now[0], now[1], true, true);
 26618            triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
 26619              x: now[0],
 26620              y: now[1]
 26621            });
 26622          }
 26623  
 26624          var dx = r.touchData.startPosition[0] - now[0];
 26625          var dx2 = dx * dx;
 26626          var dy = r.touchData.startPosition[1] - now[1];
 26627          var dy2 = dy * dy;
 26628          var dist2 = dx2 + dy2;
 26629          var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
 26630  
 26631          if (!r.touchData.singleTouchMoved) {
 26632            if (!start) {
 26633              cy.$(':selected').unselect(['tapunselect']);
 26634            }
 26635  
 26636            triggerEvents(start, ['tap', 'vclick'], e, {
 26637              x: now[0],
 26638              y: now[1]
 26639            });
 26640          } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
 26641  
 26642  
 26643          if (start != null && !r.dragData.didDrag // didn't drag nodes around
 26644          && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
 26645          ) {
 26646              if (cy.selectionType() === 'single') {
 26647                cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
 26648                start.select(['tapselect']);
 26649              } else {
 26650                if (start.selected()) {
 26651                  start.unselect(['tapunselect']);
 26652                } else {
 26653                  start.select(['tapselect']);
 26654                }
 26655              }
 26656  
 26657              r.redrawHint('eles', true);
 26658            }
 26659  
 26660          r.touchData.singleTouchMoved = true;
 26661        }
 26662  
 26663        for (var j = 0; j < now.length; j++) {
 26664          earlier[j] = now[j];
 26665        }
 26666  
 26667        r.dragData.didDrag = false; // reset for next touchstart
 26668  
 26669        if (e.touches.length === 0) {
 26670          r.touchData.dragDelta = [];
 26671          r.touchData.startPosition = null;
 26672          r.touchData.startGPosition = null;
 26673          r.touchData.didSelect = false;
 26674        }
 26675  
 26676        if (e.touches.length < 2) {
 26677          if (e.touches.length === 1) {
 26678            // the old start global pos'n may not be the same finger that remains
 26679            r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
 26680          }
 26681  
 26682          r.pinching = false;
 26683          r.redrawHint('eles', true);
 26684          r.redraw();
 26685        } //r.redraw();
 26686  
 26687      }, false); // fallback compatibility layer for ms pointer events
 26688  
 26689      if (typeof TouchEvent === 'undefined') {
 26690        var pointers = [];
 26691  
 26692        var makeTouch = function makeTouch(e) {
 26693          return {
 26694            clientX: e.clientX,
 26695            clientY: e.clientY,
 26696            force: 1,
 26697            identifier: e.pointerId,
 26698            pageX: e.pageX,
 26699            pageY: e.pageY,
 26700            radiusX: e.width / 2,
 26701            radiusY: e.height / 2,
 26702            screenX: e.screenX,
 26703            screenY: e.screenY,
 26704            target: e.target
 26705          };
 26706        };
 26707  
 26708        var makePointer = function makePointer(e) {
 26709          return {
 26710            event: e,
 26711            touch: makeTouch(e)
 26712          };
 26713        };
 26714  
 26715        var addPointer = function addPointer(e) {
 26716          pointers.push(makePointer(e));
 26717        };
 26718  
 26719        var removePointer = function removePointer(e) {
 26720          for (var i = 0; i < pointers.length; i++) {
 26721            var p = pointers[i];
 26722  
 26723            if (p.event.pointerId === e.pointerId) {
 26724              pointers.splice(i, 1);
 26725              return;
 26726            }
 26727          }
 26728        };
 26729  
 26730        var updatePointer = function updatePointer(e) {
 26731          var p = pointers.filter(function (p) {
 26732            return p.event.pointerId === e.pointerId;
 26733          })[0];
 26734          p.event = e;
 26735          p.touch = makeTouch(e);
 26736        };
 26737  
 26738        var addTouchesToEvent = function addTouchesToEvent(e) {
 26739          e.touches = pointers.map(function (p) {
 26740            return p.touch;
 26741          });
 26742        };
 26743  
 26744        var pointerIsMouse = function pointerIsMouse(e) {
 26745          return e.pointerType === 'mouse' || e.pointerType === 4;
 26746        };
 26747  
 26748        r.registerBinding(r.container, 'pointerdown', function (e) {
 26749          if (pointerIsMouse(e)) {
 26750            return;
 26751          } // mouse already handled
 26752  
 26753  
 26754          e.preventDefault();
 26755          addPointer(e);
 26756          addTouchesToEvent(e);
 26757          touchstartHandler(e);
 26758        });
 26759        r.registerBinding(r.container, 'pointerup', function (e) {
 26760          if (pointerIsMouse(e)) {
 26761            return;
 26762          } // mouse already handled
 26763  
 26764  
 26765          removePointer(e);
 26766          addTouchesToEvent(e);
 26767          touchendHandler(e);
 26768        });
 26769        r.registerBinding(r.container, 'pointercancel', function (e) {
 26770          if (pointerIsMouse(e)) {
 26771            return;
 26772          } // mouse already handled
 26773  
 26774  
 26775          removePointer(e);
 26776          addTouchesToEvent(e);
 26777          touchcancelHandler(e);
 26778        });
 26779        r.registerBinding(r.container, 'pointermove', function (e) {
 26780          if (pointerIsMouse(e)) {
 26781            return;
 26782          } // mouse already handled
 26783  
 26784  
 26785          e.preventDefault();
 26786          updatePointer(e);
 26787          addTouchesToEvent(e);
 26788          touchmoveHandler(e);
 26789        });
 26790      }
 26791    };
 26792  
 26793    var BRp$d = {};
 26794  
 26795    BRp$d.generatePolygon = function (name, points) {
 26796      return this.nodeShapes[name] = {
 26797        renderer: this,
 26798        name: name,
 26799        points: points,
 26800        draw: function draw(context, centerX, centerY, width, height) {
 26801          this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
 26802        },
 26803        intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26804          return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
 26805        },
 26806        checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26807          return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
 26808        }
 26809      };
 26810    };
 26811  
 26812    BRp$d.generateEllipse = function () {
 26813      return this.nodeShapes['ellipse'] = {
 26814        renderer: this,
 26815        name: 'ellipse',
 26816        draw: function draw(context, centerX, centerY, width, height) {
 26817          this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
 26818        },
 26819        intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26820          return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
 26821        },
 26822        checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26823          return checkInEllipse(x, y, width, height, centerX, centerY, padding);
 26824        }
 26825      };
 26826    };
 26827  
 26828    BRp$d.generateRoundPolygon = function (name, points) {
 26829      // Pre-compute control points
 26830      // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
 26831      // the unit vectors.
 26832      // For simplicity the layout will be:
 26833      // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
 26834      var allPoints = new Array(points.length * 2);
 26835  
 26836      for (var i = 0; i < points.length / 2; i++) {
 26837        var sourceIndex = i * 2;
 26838        var destIndex = void 0;
 26839  
 26840        if (i < points.length / 2 - 1) {
 26841          destIndex = (i + 1) * 2;
 26842        } else {
 26843          destIndex = 0;
 26844        }
 26845  
 26846        allPoints[i * 4] = points[sourceIndex];
 26847        allPoints[i * 4 + 1] = points[sourceIndex + 1];
 26848        var xDest = points[destIndex] - points[sourceIndex];
 26849        var yDest = points[destIndex + 1] - points[sourceIndex + 1];
 26850        var norm = Math.sqrt(xDest * xDest + yDest * yDest);
 26851        allPoints[i * 4 + 2] = xDest / norm;
 26852        allPoints[i * 4 + 3] = yDest / norm;
 26853      }
 26854  
 26855      return this.nodeShapes[name] = {
 26856        renderer: this,
 26857        name: name,
 26858        points: allPoints,
 26859        draw: function draw(context, centerX, centerY, width, height) {
 26860          this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
 26861        },
 26862        intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26863          return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
 26864        },
 26865        checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26866          return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
 26867        }
 26868      };
 26869    };
 26870  
 26871    BRp$d.generateRoundRectangle = function () {
 26872      return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
 26873        renderer: this,
 26874        name: 'round-rectangle',
 26875        points: generateUnitNgonPointsFitToSquare(4, 0),
 26876        draw: function draw(context, centerX, centerY, width, height) {
 26877          this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
 26878        },
 26879        intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26880          return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
 26881        },
 26882        checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26883          var cornerRadius = getRoundRectangleRadius(width, height);
 26884          var diam = cornerRadius * 2; // Check hBox
 26885  
 26886          if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
 26887            return true;
 26888          } // Check vBox
 26889  
 26890  
 26891          if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
 26892            return true;
 26893          } // Check top left quarter circle
 26894  
 26895  
 26896          if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
 26897            return true;
 26898          } // Check top right quarter circle
 26899  
 26900  
 26901          if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
 26902            return true;
 26903          } // Check bottom right quarter circle
 26904  
 26905  
 26906          if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
 26907            return true;
 26908          } // Check bottom left quarter circle
 26909  
 26910  
 26911          if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
 26912            return true;
 26913          }
 26914  
 26915          return false;
 26916        }
 26917      };
 26918    };
 26919  
 26920    BRp$d.generateCutRectangle = function () {
 26921      return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
 26922        renderer: this,
 26923        name: 'cut-rectangle',
 26924        cornerLength: getCutRectangleCornerLength(),
 26925        points: generateUnitNgonPointsFitToSquare(4, 0),
 26926        draw: function draw(context, centerX, centerY, width, height) {
 26927          this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
 26928        },
 26929        generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
 26930          var cl = this.cornerLength;
 26931          var hh = height / 2;
 26932          var hw = width / 2;
 26933          var xBegin = centerX - hw;
 26934          var xEnd = centerX + hw;
 26935          var yBegin = centerY - hh;
 26936          var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
 26937  
 26938          return {
 26939            topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
 26940            topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
 26941            bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
 26942            bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
 26943          };
 26944        },
 26945        intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26946          var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
 26947          var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
 26948          return polygonIntersectLine(x, y, pts, nodeX, nodeY);
 26949        },
 26950        checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 26951          // Check hBox
 26952          if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
 26953            return true;
 26954          } // Check vBox
 26955  
 26956  
 26957          if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
 26958            return true;
 26959          }
 26960  
 26961          var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
 26962          return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
 26963        }
 26964      };
 26965    };
 26966  
 26967    BRp$d.generateBarrel = function () {
 26968      return this.nodeShapes['barrel'] = {
 26969        renderer: this,
 26970        name: 'barrel',
 26971        points: generateUnitNgonPointsFitToSquare(4, 0),
 26972        draw: function draw(context, centerX, centerY, width, height) {
 26973          this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
 26974        },
 26975        intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 26976          // use two fixed t values for the bezier curve approximation
 26977          var t0 = 0.15;
 26978          var t1 = 0.5;
 26979          var t2 = 0.85;
 26980          var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
 26981  
 26982          var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
 26983            // approximate curve pts based on the two t values
 26984            var m0 = qbezierPtAt({
 26985              x: pts[0],
 26986              y: pts[1]
 26987            }, {
 26988              x: pts[2],
 26989              y: pts[3]
 26990            }, {
 26991              x: pts[4],
 26992              y: pts[5]
 26993            }, t0);
 26994            var m1 = qbezierPtAt({
 26995              x: pts[0],
 26996              y: pts[1]
 26997            }, {
 26998              x: pts[2],
 26999              y: pts[3]
 27000            }, {
 27001              x: pts[4],
 27002              y: pts[5]
 27003            }, t1);
 27004            var m2 = qbezierPtAt({
 27005              x: pts[0],
 27006              y: pts[1]
 27007            }, {
 27008              x: pts[2],
 27009              y: pts[3]
 27010            }, {
 27011              x: pts[4],
 27012              y: pts[5]
 27013            }, t2);
 27014            return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
 27015          };
 27016  
 27017          var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
 27018          return polygonIntersectLine(x, y, pts, nodeX, nodeY);
 27019        },
 27020        generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
 27021          var hh = height / 2;
 27022          var hw = width / 2;
 27023          var xBegin = centerX - hw;
 27024          var xEnd = centerX + hw;
 27025          var yBegin = centerY - hh;
 27026          var yEnd = centerY + hh;
 27027          var curveConstants = getBarrelCurveConstants(width, height);
 27028          var hOffset = curveConstants.heightOffset;
 27029          var wOffset = curveConstants.widthOffset;
 27030          var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
 27031  
 27032          var pts = {
 27033            topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
 27034            topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
 27035            bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
 27036            bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
 27037          };
 27038          pts.topLeft.isTop = true;
 27039          pts.topRight.isTop = true;
 27040          pts.bottomLeft.isBottom = true;
 27041          pts.bottomRight.isBottom = true;
 27042          return pts;
 27043        },
 27044        checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 27045          var curveConstants = getBarrelCurveConstants(width, height);
 27046          var hOffset = curveConstants.heightOffset;
 27047          var wOffset = curveConstants.widthOffset; // Check hBox
 27048  
 27049          if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
 27050            return true;
 27051          } // Check vBox
 27052  
 27053  
 27054          if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
 27055            return true;
 27056          }
 27057  
 27058          var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
 27059  
 27060          var getCurveT = function getCurveT(x, y, curvePts) {
 27061            var x0 = curvePts[4];
 27062            var x1 = curvePts[2];
 27063            var x2 = curvePts[0];
 27064            var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
 27065  
 27066            var y2 = curvePts[1];
 27067            var xMin = Math.min(x0, x2);
 27068            var xMax = Math.max(x0, x2);
 27069            var yMin = Math.min(y0, y2);
 27070            var yMax = Math.max(y0, y2);
 27071  
 27072            if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
 27073              var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
 27074              var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
 27075              var validRoots = roots.filter(function (r) {
 27076                return 0 <= r && r <= 1;
 27077              });
 27078  
 27079              if (validRoots.length > 0) {
 27080                return validRoots[0];
 27081              }
 27082            }
 27083  
 27084            return null;
 27085          };
 27086  
 27087          var curveRegions = Object.keys(barrelCurvePts);
 27088  
 27089          for (var i = 0; i < curveRegions.length; i++) {
 27090            var corner = curveRegions[i];
 27091            var cornerPts = barrelCurvePts[corner];
 27092            var t = getCurveT(x, y, cornerPts);
 27093  
 27094            if (t == null) {
 27095              continue;
 27096            }
 27097  
 27098            var y0 = cornerPts[5];
 27099            var y1 = cornerPts[3];
 27100            var y2 = cornerPts[1];
 27101            var bezY = qbezierAt(y0, y1, y2, t);
 27102  
 27103            if (cornerPts.isTop && bezY <= y) {
 27104              return true;
 27105            }
 27106  
 27107            if (cornerPts.isBottom && y <= bezY) {
 27108              return true;
 27109            }
 27110          }
 27111  
 27112          return false;
 27113        }
 27114      };
 27115    };
 27116  
 27117    BRp$d.generateBottomRoundrectangle = function () {
 27118      return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
 27119        renderer: this,
 27120        name: 'bottom-round-rectangle',
 27121        points: generateUnitNgonPointsFitToSquare(4, 0),
 27122        draw: function draw(context, centerX, centerY, width, height) {
 27123          this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
 27124        },
 27125        intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
 27126          var topStartX = nodeX - (width / 2 + padding);
 27127          var topStartY = nodeY - (height / 2 + padding);
 27128          var topEndY = topStartY;
 27129          var topEndX = nodeX + (width / 2 + padding);
 27130          var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
 27131  
 27132          if (topIntersections.length > 0) {
 27133            return topIntersections;
 27134          }
 27135  
 27136          return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
 27137        },
 27138        checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
 27139          var cornerRadius = getRoundRectangleRadius(width, height);
 27140          var diam = 2 * cornerRadius; // Check hBox
 27141  
 27142          if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
 27143            return true;
 27144          } // Check vBox
 27145  
 27146  
 27147          if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
 27148            return true;
 27149          } // check non-rounded top side
 27150  
 27151  
 27152          var outerWidth = width / 2 + 2 * padding;
 27153          var outerHeight = height / 2 + 2 * padding;
 27154          var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
 27155  
 27156          if (pointInsidePolygonPoints(x, y, points)) {
 27157            return true;
 27158          } // Check bottom right quarter circle
 27159  
 27160  
 27161          if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
 27162            return true;
 27163          } // Check bottom left quarter circle
 27164  
 27165  
 27166          if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
 27167            return true;
 27168          }
 27169  
 27170          return false;
 27171        }
 27172      };
 27173    };
 27174  
 27175    BRp$d.registerNodeShapes = function () {
 27176      var nodeShapes = this.nodeShapes = {};
 27177      var renderer = this;
 27178      this.generateEllipse();
 27179      this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
 27180      this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
 27181      this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
 27182      nodeShapes['square'] = nodeShapes['rectangle'];
 27183      this.generateRoundRectangle();
 27184      this.generateCutRectangle();
 27185      this.generateBarrel();
 27186      this.generateBottomRoundrectangle();
 27187      {
 27188        var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
 27189        this.generatePolygon('diamond', diamondPoints);
 27190        this.generateRoundPolygon('round-diamond', diamondPoints);
 27191      }
 27192      this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
 27193      this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
 27194      this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
 27195      this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
 27196      this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
 27197      this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
 27198      this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
 27199      this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
 27200      var star5Points = new Array(20);
 27201      {
 27202        var outerPoints = generateUnitNgonPoints(5, 0);
 27203        var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
 27204  
 27205        var innerRadius = 0.5 * (3 - Math.sqrt(5));
 27206        innerRadius *= 1.57;
 27207  
 27208        for (var i = 0; i < innerPoints.length / 2; i++) {
 27209          innerPoints[i * 2] *= innerRadius;
 27210          innerPoints[i * 2 + 1] *= innerRadius;
 27211        }
 27212  
 27213        for (var i = 0; i < 20 / 4; i++) {
 27214          star5Points[i * 4] = outerPoints[i * 2];
 27215          star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
 27216          star5Points[i * 4 + 2] = innerPoints[i * 2];
 27217          star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
 27218        }
 27219      }
 27220      star5Points = fitPolygonToSquare(star5Points);
 27221      this.generatePolygon('star', star5Points);
 27222      this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
 27223      this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
 27224      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]);
 27225      {
 27226        var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
 27227        this.generatePolygon('tag', tagPoints);
 27228        this.generateRoundPolygon('round-tag', tagPoints);
 27229      }
 27230  
 27231      nodeShapes.makePolygon = function (points) {
 27232        // use caching on user-specified polygons so they are as fast as native shapes
 27233        var key = points.join('$');
 27234        var name = 'polygon-' + key;
 27235        var shape;
 27236  
 27237        if (shape = this[name]) {
 27238          // got cached shape
 27239          return shape;
 27240        } // create and cache new shape
 27241  
 27242  
 27243        return renderer.generatePolygon(name, points);
 27244      };
 27245    };
 27246  
 27247    var BRp$e = {};
 27248  
 27249    BRp$e.timeToRender = function () {
 27250      return this.redrawTotalTime / this.redrawCount;
 27251    };
 27252  
 27253    BRp$e.redraw = function (options) {
 27254      options = options || staticEmptyObject();
 27255      var r = this;
 27256  
 27257      if (r.averageRedrawTime === undefined) {
 27258        r.averageRedrawTime = 0;
 27259      }
 27260  
 27261      if (r.lastRedrawTime === undefined) {
 27262        r.lastRedrawTime = 0;
 27263      }
 27264  
 27265      if (r.lastDrawTime === undefined) {
 27266        r.lastDrawTime = 0;
 27267      }
 27268  
 27269      r.requestedFrame = true;
 27270      r.renderOptions = options;
 27271    };
 27272  
 27273    BRp$e.beforeRender = function (fn, priority) {
 27274      // the renderer can't add tick callbacks when destroyed
 27275      if (this.destroyed) {
 27276        return;
 27277      }
 27278  
 27279      if (priority == null) {
 27280        error('Priority is not optional for beforeRender');
 27281      }
 27282  
 27283      var cbs = this.beforeRenderCallbacks;
 27284      cbs.push({
 27285        fn: fn,
 27286        priority: priority
 27287      }); // higher priority callbacks executed first
 27288  
 27289      cbs.sort(function (a, b) {
 27290        return b.priority - a.priority;
 27291      });
 27292    };
 27293  
 27294    var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
 27295      var cbs = r.beforeRenderCallbacks;
 27296  
 27297      for (var i = 0; i < cbs.length; i++) {
 27298        cbs[i].fn(willDraw, startTime);
 27299      }
 27300    };
 27301  
 27302    BRp$e.startRenderLoop = function () {
 27303      var r = this;
 27304      var cy = r.cy;
 27305  
 27306      if (r.renderLoopStarted) {
 27307        return;
 27308      } else {
 27309        r.renderLoopStarted = true;
 27310      }
 27311  
 27312      var renderFn = function renderFn(requestTime) {
 27313        if (r.destroyed) {
 27314          return;
 27315        }
 27316  
 27317        if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
 27318          beforeRenderCallbacks(r, true, requestTime);
 27319          var startTime = performanceNow();
 27320          r.render(r.renderOptions);
 27321          var endTime = r.lastDrawTime = performanceNow();
 27322  
 27323          if (r.averageRedrawTime === undefined) {
 27324            r.averageRedrawTime = endTime - startTime;
 27325          }
 27326  
 27327          if (r.redrawCount === undefined) {
 27328            r.redrawCount = 0;
 27329          }
 27330  
 27331          r.redrawCount++;
 27332  
 27333          if (r.redrawTotalTime === undefined) {
 27334            r.redrawTotalTime = 0;
 27335          }
 27336  
 27337          var duration = endTime - startTime;
 27338          r.redrawTotalTime += duration;
 27339          r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
 27340  
 27341          r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
 27342          r.requestedFrame = false;
 27343        } else {
 27344          beforeRenderCallbacks(r, false, requestTime);
 27345        }
 27346  
 27347        r.skipFrame = false;
 27348        requestAnimationFrame(renderFn);
 27349      };
 27350  
 27351      requestAnimationFrame(renderFn);
 27352    };
 27353  
 27354    var BaseRenderer = function BaseRenderer(options) {
 27355      this.init(options);
 27356    };
 27357  
 27358    var BR = BaseRenderer;
 27359    var BRp$f = BR.prototype;
 27360    BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
 27361  
 27362    BRp$f.init = function (options) {
 27363      var r = this;
 27364      r.options = options;
 27365      r.cy = options.cy;
 27366      var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
 27367  
 27368      if (window$1) {
 27369        var document = window$1.document;
 27370        var head = document.head;
 27371        var stylesheetId = '__________cytoscape_stylesheet';
 27372        var className = '__________cytoscape_container';
 27373        var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
 27374  
 27375        if (ctr.className.indexOf(className) < 0) {
 27376          ctr.className = (ctr.className || '') + ' ' + className;
 27377        }
 27378  
 27379        if (!stylesheetAlreadyExists) {
 27380          var stylesheet = document.createElement('style');
 27381          stylesheet.id = stylesheetId;
 27382          stylesheet.innerHTML = '.' + className + ' { position: relative; }';
 27383          head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
 27384        }
 27385  
 27386        var computedStyle = window$1.getComputedStyle(ctr);
 27387        var position = computedStyle.getPropertyValue('position');
 27388  
 27389        if (position === 'static') {
 27390          warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
 27391        }
 27392      }
 27393  
 27394      r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
 27395  
 27396      r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
 27397  
 27398      r.hoverData = {
 27399        down: null,
 27400        last: null,
 27401        downTime: null,
 27402        triggerMode: null,
 27403        dragging: false,
 27404        initialPan: [null, null],
 27405        capture: false
 27406      };
 27407      r.dragData = {
 27408        possibleDragElements: []
 27409      };
 27410      r.touchData = {
 27411        start: null,
 27412        capture: false,
 27413        // These 3 fields related to tap, taphold events
 27414        startPosition: [null, null, null, null, null, null],
 27415        singleTouchStartTime: null,
 27416        singleTouchMoved: true,
 27417        now: [null, null, null, null, null, null],
 27418        earlier: [null, null, null, null, null, null]
 27419      };
 27420      r.redraws = 0;
 27421      r.showFps = options.showFps;
 27422      r.debug = options.debug;
 27423      r.hideEdgesOnViewport = options.hideEdgesOnViewport;
 27424      r.textureOnViewport = options.textureOnViewport;
 27425      r.wheelSensitivity = options.wheelSensitivity;
 27426      r.motionBlurEnabled = options.motionBlur; // on by default
 27427  
 27428      r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
 27429      r.motionBlur = options.motionBlur; // for initial kick off
 27430  
 27431      r.motionBlurOpacity = options.motionBlurOpacity;
 27432      r.motionBlurTransparency = 1 - r.motionBlurOpacity;
 27433      r.motionBlurPxRatio = 1;
 27434      r.mbPxRBlurry = 1; //0.8;
 27435  
 27436      r.minMbLowQualFrames = 4;
 27437      r.fullQualityMb = false;
 27438      r.clearedForMotionBlur = [];
 27439      r.desktopTapThreshold = options.desktopTapThreshold;
 27440      r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
 27441      r.touchTapThreshold = options.touchTapThreshold;
 27442      r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
 27443      r.tapholdDuration = 500;
 27444      r.bindings = [];
 27445      r.beforeRenderCallbacks = [];
 27446      r.beforeRenderPriorities = {
 27447        // higher priority execs before lower one
 27448        animations: 400,
 27449        eleCalcs: 300,
 27450        eleTxrDeq: 200,
 27451        lyrTxrDeq: 150,
 27452        lyrTxrSkip: 100
 27453      };
 27454      r.registerNodeShapes();
 27455      r.registerArrowShapes();
 27456      r.registerCalculationListeners();
 27457    };
 27458  
 27459    BRp$f.notify = function (eventName, eles) {
 27460      var r = this;
 27461      var cy = r.cy; // the renderer can't be notified after it's destroyed
 27462  
 27463      if (this.destroyed) {
 27464        return;
 27465      }
 27466  
 27467      if (eventName === 'init') {
 27468        r.load();
 27469        return;
 27470      }
 27471  
 27472      if (eventName === 'destroy') {
 27473        r.destroy();
 27474        return;
 27475      }
 27476  
 27477      if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
 27478        r.invalidateCachedZSortedEles();
 27479      }
 27480  
 27481      if (eventName === 'viewport') {
 27482        r.redrawHint('select', true);
 27483      }
 27484  
 27485      if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
 27486        r.invalidateContainerClientCoordsCache();
 27487        r.matchCanvasSize(r.container);
 27488      }
 27489  
 27490      r.redrawHint('eles', true);
 27491      r.redrawHint('drag', true);
 27492      this.startRenderLoop();
 27493      this.redraw();
 27494    };
 27495  
 27496    BRp$f.destroy = function () {
 27497      var r = this;
 27498      r.destroyed = true;
 27499      r.cy.stopAnimationLoop();
 27500  
 27501      for (var i = 0; i < r.bindings.length; i++) {
 27502        var binding = r.bindings[i];
 27503        var b = binding;
 27504        var tgt = b.target;
 27505        (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
 27506      }
 27507  
 27508      r.bindings = [];
 27509      r.beforeRenderCallbacks = [];
 27510      r.onUpdateEleCalcsFns = [];
 27511  
 27512      if (r.removeObserver) {
 27513        r.removeObserver.disconnect();
 27514      }
 27515  
 27516      if (r.styleObserver) {
 27517        r.styleObserver.disconnect();
 27518      }
 27519  
 27520      if (r.resizeObserver) {
 27521        r.resizeObserver.disconnect();
 27522      }
 27523  
 27524      if (r.labelCalcDiv) {
 27525        try {
 27526          document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
 27527        } catch (e) {// ie10 issue #1014
 27528        }
 27529      }
 27530    };
 27531  
 27532    BRp$f.isHeadless = function () {
 27533      return false;
 27534    };
 27535  
 27536    [BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
 27537      extend(BRp$f, props);
 27538    });
 27539  
 27540    var fullFpsTime = 1000 / 60; // assume 60 frames per second
 27541  
 27542    var defs = {
 27543      setupDequeueing: function setupDequeueing(opts) {
 27544        return function setupDequeueingImpl() {
 27545          var self = this;
 27546          var r = this.renderer;
 27547  
 27548          if (self.dequeueingSetup) {
 27549            return;
 27550          } else {
 27551            self.dequeueingSetup = true;
 27552          }
 27553  
 27554          var queueRedraw = lodash_debounce(function () {
 27555            r.redrawHint('eles', true);
 27556            r.redrawHint('drag', true);
 27557            r.redraw();
 27558          }, opts.deqRedrawThreshold);
 27559  
 27560          var dequeue = function dequeue(willDraw, frameStartTime) {
 27561            var startTime = performanceNow();
 27562            var avgRenderTime = r.averageRedrawTime;
 27563            var renderTime = r.lastRedrawTime;
 27564            var deqd = [];
 27565            var extent = r.cy.extent();
 27566            var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
 27567            // queue won't automatically be flushed before dequeueing starts
 27568  
 27569            if (!willDraw) {
 27570              r.flushRenderedStyleQueue();
 27571            }
 27572  
 27573            while (true) {
 27574              // eslint-disable-line no-constant-condition
 27575              var now = performanceNow();
 27576              var duration = now - startTime;
 27577              var frameDuration = now - frameStartTime;
 27578  
 27579              if (renderTime < fullFpsTime) {
 27580                // if we're rendering faster than the ideal fps, then do dequeueing
 27581                // during all of the remaining frame time
 27582                var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
 27583  
 27584                if (frameDuration >= opts.deqFastCost * timeAvailable) {
 27585                  break;
 27586                }
 27587              } else {
 27588                if (willDraw) {
 27589                  if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
 27590                    break;
 27591                  }
 27592                } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
 27593                  break;
 27594                }
 27595              }
 27596  
 27597              var thisDeqd = opts.deq(self, pixelRatio, extent);
 27598  
 27599              if (thisDeqd.length > 0) {
 27600                for (var i = 0; i < thisDeqd.length; i++) {
 27601                  deqd.push(thisDeqd[i]);
 27602                }
 27603              } else {
 27604                break;
 27605              }
 27606            } // callbacks on dequeue
 27607  
 27608  
 27609            if (deqd.length > 0) {
 27610              opts.onDeqd(self, deqd);
 27611  
 27612              if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
 27613                queueRedraw();
 27614              }
 27615            }
 27616          };
 27617  
 27618          var priority = opts.priority || noop;
 27619          r.beforeRender(dequeue, priority(self));
 27620        };
 27621      }
 27622    };
 27623  
 27624    // Uses keys so elements may share the same cache.
 27625  
 27626    var ElementTextureCacheLookup =
 27627    /*#__PURE__*/
 27628    function () {
 27629      function ElementTextureCacheLookup(getKey) {
 27630        var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
 27631  
 27632        _classCallCheck(this, ElementTextureCacheLookup);
 27633  
 27634        this.idsByKey = new Map$1();
 27635        this.keyForId = new Map$1();
 27636        this.cachesByLvl = new Map$1();
 27637        this.lvls = [];
 27638        this.getKey = getKey;
 27639        this.doesEleInvalidateKey = doesEleInvalidateKey;
 27640      }
 27641  
 27642      _createClass(ElementTextureCacheLookup, [{
 27643        key: "getIdsFor",
 27644        value: function getIdsFor(key) {
 27645          if (key == null) {
 27646            error("Can not get id list for null key");
 27647          }
 27648  
 27649          var idsByKey = this.idsByKey;
 27650          var ids = this.idsByKey.get(key);
 27651  
 27652          if (!ids) {
 27653            ids = new Set$1();
 27654            idsByKey.set(key, ids);
 27655          }
 27656  
 27657          return ids;
 27658        }
 27659      }, {
 27660        key: "addIdForKey",
 27661        value: function addIdForKey(key, id) {
 27662          if (key != null) {
 27663            this.getIdsFor(key).add(id);
 27664          }
 27665        }
 27666      }, {
 27667        key: "deleteIdForKey",
 27668        value: function deleteIdForKey(key, id) {
 27669          if (key != null) {
 27670            this.getIdsFor(key)["delete"](id);
 27671          }
 27672        }
 27673      }, {
 27674        key: "getNumberOfIdsForKey",
 27675        value: function getNumberOfIdsForKey(key) {
 27676          if (key == null) {
 27677            return 0;
 27678          } else {
 27679            return this.getIdsFor(key).size;
 27680          }
 27681        }
 27682      }, {
 27683        key: "updateKeyMappingFor",
 27684        value: function updateKeyMappingFor(ele) {
 27685          var id = ele.id();
 27686          var prevKey = this.keyForId.get(id);
 27687          var currKey = this.getKey(ele);
 27688          this.deleteIdForKey(prevKey, id);
 27689          this.addIdForKey(currKey, id);
 27690          this.keyForId.set(id, currKey);
 27691        }
 27692      }, {
 27693        key: "deleteKeyMappingFor",
 27694        value: function deleteKeyMappingFor(ele) {
 27695          var id = ele.id();
 27696          var prevKey = this.keyForId.get(id);
 27697          this.deleteIdForKey(prevKey, id);
 27698          this.keyForId["delete"](id);
 27699        }
 27700      }, {
 27701        key: "keyHasChangedFor",
 27702        value: function keyHasChangedFor(ele) {
 27703          var id = ele.id();
 27704          var prevKey = this.keyForId.get(id);
 27705          var newKey = this.getKey(ele);
 27706          return prevKey !== newKey;
 27707        }
 27708      }, {
 27709        key: "isInvalid",
 27710        value: function isInvalid(ele) {
 27711          return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
 27712        }
 27713      }, {
 27714        key: "getCachesAt",
 27715        value: function getCachesAt(lvl) {
 27716          var cachesByLvl = this.cachesByLvl,
 27717              lvls = this.lvls;
 27718          var caches = cachesByLvl.get(lvl);
 27719  
 27720          if (!caches) {
 27721            caches = new Map$1();
 27722            cachesByLvl.set(lvl, caches);
 27723            lvls.push(lvl);
 27724          }
 27725  
 27726          return caches;
 27727        }
 27728      }, {
 27729        key: "getCache",
 27730        value: function getCache(key, lvl) {
 27731          return this.getCachesAt(lvl).get(key);
 27732        }
 27733      }, {
 27734        key: "get",
 27735        value: function get(ele, lvl) {
 27736          var key = this.getKey(ele);
 27737          var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
 27738  
 27739          if (cache != null) {
 27740            this.updateKeyMappingFor(ele);
 27741          }
 27742  
 27743          return cache;
 27744        }
 27745      }, {
 27746        key: "getForCachedKey",
 27747        value: function getForCachedKey(ele, lvl) {
 27748          var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
 27749  
 27750          var cache = this.getCache(key, lvl);
 27751          return cache;
 27752        }
 27753      }, {
 27754        key: "hasCache",
 27755        value: function hasCache(key, lvl) {
 27756          return this.getCachesAt(lvl).has(key);
 27757        }
 27758      }, {
 27759        key: "has",
 27760        value: function has(ele, lvl) {
 27761          var key = this.getKey(ele);
 27762          return this.hasCache(key, lvl);
 27763        }
 27764      }, {
 27765        key: "setCache",
 27766        value: function setCache(key, lvl, cache) {
 27767          cache.key = key;
 27768          this.getCachesAt(lvl).set(key, cache);
 27769        }
 27770      }, {
 27771        key: "set",
 27772        value: function set(ele, lvl, cache) {
 27773          var key = this.getKey(ele);
 27774          this.setCache(key, lvl, cache);
 27775          this.updateKeyMappingFor(ele);
 27776        }
 27777      }, {
 27778        key: "deleteCache",
 27779        value: function deleteCache(key, lvl) {
 27780          this.getCachesAt(lvl)["delete"](key);
 27781        }
 27782      }, {
 27783        key: "delete",
 27784        value: function _delete(ele, lvl) {
 27785          var key = this.getKey(ele);
 27786          this.deleteCache(key, lvl);
 27787        }
 27788      }, {
 27789        key: "invalidateKey",
 27790        value: function invalidateKey(key) {
 27791          var _this = this;
 27792  
 27793          this.lvls.forEach(function (lvl) {
 27794            return _this.deleteCache(key, lvl);
 27795          });
 27796        } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
 27797  
 27798      }, {
 27799        key: "invalidate",
 27800        value: function invalidate(ele) {
 27801          var id = ele.id();
 27802          var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
 27803  
 27804          this.deleteKeyMappingFor(ele);
 27805          var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
 27806  
 27807          if (entireKeyInvalidated) {
 27808            // clear mapping for current key
 27809            this.invalidateKey(key);
 27810          }
 27811  
 27812          return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
 27813        }
 27814      }]);
 27815  
 27816      return ElementTextureCacheLookup;
 27817    }();
 27818  
 27819    var minTxrH = 25; // the size of the texture cache for small height eles (special case)
 27820  
 27821    var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
 27822  
 27823    var minLvl = -4; // when scaling smaller than that we don't need to re-render
 27824  
 27825    var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
 27826  
 27827    var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
 27828  
 27829    var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
 27830  
 27831    var defTxrWidth = 1024; // default/minimum texture width
 27832  
 27833    var maxTxrW = 1024; // the maximum width of a texture
 27834  
 27835    var maxTxrH = 1024; // the maximum height of a texture
 27836  
 27837    var minUtility = 0.2; // if usage of texture is less than this, it is retired
 27838  
 27839    var maxFullness = 0.8; // fullness of texture after which queue removal is checked
 27840  
 27841    var maxFullnessChecks = 10; // dequeued after this many checks
 27842  
 27843    var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
 27844  
 27845    var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
 27846  
 27847    var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
 27848  
 27849    var deqFastCost = 0.9; // % of frame time to be used when >60fps
 27850  
 27851    var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
 27852  
 27853    var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
 27854  
 27855    var getTxrReasons = {
 27856      dequeue: 'dequeue',
 27857      downscale: 'downscale',
 27858      highQuality: 'highQuality'
 27859    };
 27860    var initDefaults = defaults({
 27861      getKey: null,
 27862      doesEleInvalidateKey: falsify,
 27863      drawElement: null,
 27864      getBoundingBox: null,
 27865      getRotationPoint: null,
 27866      getRotationOffset: null,
 27867      isVisible: trueify,
 27868      allowEdgeTxrCaching: true,
 27869      allowParentTxrCaching: true
 27870    });
 27871  
 27872    var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
 27873      var self = this;
 27874      self.renderer = renderer;
 27875      self.onDequeues = [];
 27876      var opts = initDefaults(initOptions);
 27877      extend(self, opts);
 27878      self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
 27879      self.setupDequeueing();
 27880    };
 27881  
 27882    var ETCp = ElementTextureCache.prototype;
 27883    ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
 27884  
 27885    ETCp.getTextureQueue = function (txrH) {
 27886      var self = this;
 27887      self.eleImgCaches = self.eleImgCaches || {};
 27888      return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
 27889    }; // the list of usused textures which can be recycled (in use in texture queue)
 27890  
 27891  
 27892    ETCp.getRetiredTextureQueue = function (txrH) {
 27893      var self = this;
 27894      var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
 27895      var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
 27896      return rtxtrQ;
 27897    }; // queue of element draw requests at different scale levels
 27898  
 27899  
 27900    ETCp.getElementQueue = function () {
 27901      var self = this;
 27902      var q = self.eleCacheQueue = self.eleCacheQueue || new heap$1(function (a, b) {
 27903        return b.reqs - a.reqs;
 27904      });
 27905      return q;
 27906    }; // queue of element draw requests at different scale levels (element id lookup)
 27907  
 27908  
 27909    ETCp.getElementKeyToQueue = function () {
 27910      var self = this;
 27911      var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
 27912      return k2q;
 27913    };
 27914  
 27915    ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
 27916      var self = this;
 27917      var r = this.renderer;
 27918      var zoom = r.cy.zoom();
 27919      var lookup = this.lookup;
 27920  
 27921      if (bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible()) {
 27922        return null;
 27923      }
 27924  
 27925      if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
 27926        return null;
 27927      }
 27928  
 27929      if (lvl == null) {
 27930        lvl = Math.ceil(log2(zoom * pxRatio));
 27931      }
 27932  
 27933      if (lvl < minLvl) {
 27934        lvl = minLvl;
 27935      } else if (zoom >= maxZoom || lvl > maxLvl) {
 27936        return null;
 27937      }
 27938  
 27939      var scale = Math.pow(2, lvl);
 27940      var eleScaledH = bb.h * scale;
 27941      var eleScaledW = bb.w * scale;
 27942      var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
 27943  
 27944      if (!this.isVisible(ele, scaledLabelShown)) {
 27945        return null;
 27946      }
 27947  
 27948      var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
 27949  
 27950      if (eleCache && eleCache.invalidated) {
 27951        eleCache.invalidated = false;
 27952        eleCache.texture.invalidatedWidth -= eleCache.width;
 27953      }
 27954  
 27955      if (eleCache) {
 27956        return eleCache;
 27957      }
 27958  
 27959      var txrH; // which texture height this ele belongs to
 27960  
 27961      if (eleScaledH <= minTxrH) {
 27962        txrH = minTxrH;
 27963      } else if (eleScaledH <= txrStepH) {
 27964        txrH = txrStepH;
 27965      } else {
 27966        txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
 27967      }
 27968  
 27969      if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
 27970        return null; // caching large elements is not efficient
 27971      }
 27972  
 27973      var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
 27974  
 27975      var txr = txrQ[txrQ.length - 2];
 27976  
 27977      var addNewTxr = function addNewTxr() {
 27978        return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
 27979      }; // try the last one if there is no second last one
 27980  
 27981  
 27982      if (!txr) {
 27983        txr = txrQ[txrQ.length - 1];
 27984      } // if the last one doesn't exist, we need a first one
 27985  
 27986  
 27987      if (!txr) {
 27988        txr = addNewTxr();
 27989      } // if there's no room in the current texture, we need a new one
 27990  
 27991  
 27992      if (txr.width - txr.usedWidth < eleScaledW) {
 27993        txr = addNewTxr();
 27994      }
 27995  
 27996      var scalableFrom = function scalableFrom(otherCache) {
 27997        return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
 27998      };
 27999  
 28000      var deqing = reason && reason === getTxrReasons.dequeue;
 28001      var highQualityReq = reason && reason === getTxrReasons.highQuality;
 28002      var downscaleReq = reason && reason === getTxrReasons.downscale;
 28003      var higherCache; // the nearest cache with a higher level
 28004  
 28005      for (var l = lvl + 1; l <= maxLvl; l++) {
 28006        var c = lookup.get(ele, l);
 28007  
 28008        if (c) {
 28009          higherCache = c;
 28010          break;
 28011        }
 28012      }
 28013  
 28014      var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
 28015  
 28016      var downscale = function downscale() {
 28017        txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
 28018      }; // reset ele area in texture
 28019  
 28020  
 28021      txr.context.setTransform(1, 0, 0, 1, 0, 0);
 28022      txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
 28023  
 28024      if (scalableFrom(oneUpCache)) {
 28025        // then we can relatively cheaply rescale the existing image w/o rerendering
 28026        downscale();
 28027      } else if (scalableFrom(higherCache)) {
 28028        // then use the higher cache for now and queue the next level down
 28029        // to cheaply scale towards the smaller level
 28030        if (highQualityReq) {
 28031          for (var _l = higherCache.level; _l > lvl; _l--) {
 28032            oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
 28033          }
 28034  
 28035          downscale();
 28036        } else {
 28037          self.queueElement(ele, higherCache.level - 1);
 28038          return higherCache;
 28039        }
 28040      } else {
 28041        var lowerCache; // the nearest cache with a lower level
 28042  
 28043        if (!deqing && !highQualityReq && !downscaleReq) {
 28044          for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
 28045            var _c = lookup.get(ele, _l2);
 28046  
 28047            if (_c) {
 28048              lowerCache = _c;
 28049              break;
 28050            }
 28051          }
 28052        }
 28053  
 28054        if (scalableFrom(lowerCache)) {
 28055          // then use the lower quality cache for now and queue the better one for later
 28056          self.queueElement(ele, lvl);
 28057          return lowerCache;
 28058        }
 28059  
 28060        txr.context.translate(txr.usedWidth, 0);
 28061        txr.context.scale(scale, scale);
 28062        this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
 28063        txr.context.scale(1 / scale, 1 / scale);
 28064        txr.context.translate(-txr.usedWidth, 0);
 28065      }
 28066  
 28067      eleCache = {
 28068        x: txr.usedWidth,
 28069        texture: txr,
 28070        level: lvl,
 28071        scale: scale,
 28072        width: eleScaledW,
 28073        height: eleScaledH,
 28074        scaledLabelShown: scaledLabelShown
 28075      };
 28076      txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
 28077      txr.eleCaches.push(eleCache);
 28078      lookup.set(ele, lvl, eleCache);
 28079      self.checkTextureFullness(txr);
 28080      return eleCache;
 28081    };
 28082  
 28083    ETCp.invalidateElements = function (eles) {
 28084      for (var i = 0; i < eles.length; i++) {
 28085        this.invalidateElement(eles[i]);
 28086      }
 28087    };
 28088  
 28089    ETCp.invalidateElement = function (ele) {
 28090      var self = this;
 28091      var lookup = self.lookup;
 28092      var caches = [];
 28093      var invalid = lookup.isInvalid(ele);
 28094  
 28095      if (!invalid) {
 28096        return; // override the invalidation request if the element key has not changed
 28097      }
 28098  
 28099      for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
 28100        var cache = lookup.getForCachedKey(ele, lvl);
 28101  
 28102        if (cache) {
 28103          caches.push(cache);
 28104        }
 28105      }
 28106  
 28107      var noOtherElesUseCache = lookup.invalidate(ele);
 28108  
 28109      if (noOtherElesUseCache) {
 28110        for (var i = 0; i < caches.length; i++) {
 28111          var _cache = caches[i];
 28112          var txr = _cache.texture; // remove space from the texture it belongs to
 28113  
 28114          txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
 28115  
 28116          _cache.invalidated = true; // retire the texture if its utility is low
 28117  
 28118          self.checkTextureUtility(txr);
 28119        }
 28120      } // remove from queue since the old req was for the old state
 28121  
 28122  
 28123      self.removeFromQueue(ele);
 28124    };
 28125  
 28126    ETCp.checkTextureUtility = function (txr) {
 28127      // invalidate all entries in the cache if the cache size is small
 28128      if (txr.invalidatedWidth >= minUtility * txr.width) {
 28129        this.retireTexture(txr);
 28130      }
 28131    };
 28132  
 28133    ETCp.checkTextureFullness = function (txr) {
 28134      // if texture has been mostly filled and passed over several times, remove
 28135      // it from the queue so we don't need to waste time looking at it to put new things
 28136      var self = this;
 28137      var txrQ = self.getTextureQueue(txr.height);
 28138  
 28139      if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
 28140        removeFromArray(txrQ, txr);
 28141      } else {
 28142        txr.fullnessChecks++;
 28143      }
 28144    };
 28145  
 28146    ETCp.retireTexture = function (txr) {
 28147      var self = this;
 28148      var txrH = txr.height;
 28149      var txrQ = self.getTextureQueue(txrH);
 28150      var lookup = this.lookup; // retire the texture from the active / searchable queue:
 28151  
 28152      removeFromArray(txrQ, txr);
 28153      txr.retired = true; // remove the refs from the eles to the caches:
 28154  
 28155      var eleCaches = txr.eleCaches;
 28156  
 28157      for (var i = 0; i < eleCaches.length; i++) {
 28158        var eleCache = eleCaches[i];
 28159        lookup.deleteCache(eleCache.key, eleCache.level);
 28160      }
 28161  
 28162      clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
 28163  
 28164      var rtxtrQ = self.getRetiredTextureQueue(txrH);
 28165      rtxtrQ.push(txr);
 28166    };
 28167  
 28168    ETCp.addTexture = function (txrH, minW) {
 28169      var self = this;
 28170      var txrQ = self.getTextureQueue(txrH);
 28171      var txr = {};
 28172      txrQ.push(txr);
 28173      txr.eleCaches = [];
 28174      txr.height = txrH;
 28175      txr.width = Math.max(defTxrWidth, minW);
 28176      txr.usedWidth = 0;
 28177      txr.invalidatedWidth = 0;
 28178      txr.fullnessChecks = 0;
 28179      txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
 28180      txr.context = txr.canvas.getContext('2d');
 28181      return txr;
 28182    };
 28183  
 28184    ETCp.recycleTexture = function (txrH, minW) {
 28185      var self = this;
 28186      var txrQ = self.getTextureQueue(txrH);
 28187      var rtxtrQ = self.getRetiredTextureQueue(txrH);
 28188  
 28189      for (var i = 0; i < rtxtrQ.length; i++) {
 28190        var txr = rtxtrQ[i];
 28191  
 28192        if (txr.width >= minW) {
 28193          txr.retired = false;
 28194          txr.usedWidth = 0;
 28195          txr.invalidatedWidth = 0;
 28196          txr.fullnessChecks = 0;
 28197          clearArray(txr.eleCaches);
 28198          txr.context.setTransform(1, 0, 0, 1, 0, 0);
 28199          txr.context.clearRect(0, 0, txr.width, txr.height);
 28200          removeFromArray(rtxtrQ, txr);
 28201          txrQ.push(txr);
 28202          return txr;
 28203        }
 28204      }
 28205    };
 28206  
 28207    ETCp.queueElement = function (ele, lvl) {
 28208      var self = this;
 28209      var q = self.getElementQueue();
 28210      var k2q = self.getElementKeyToQueue();
 28211      var key = this.getKey(ele);
 28212      var existingReq = k2q[key];
 28213  
 28214      if (existingReq) {
 28215        // use the max lvl b/c in between lvls are cheap to make
 28216        existingReq.level = Math.max(existingReq.level, lvl);
 28217        existingReq.eles.merge(ele);
 28218        existingReq.reqs++;
 28219        q.updateItem(existingReq);
 28220      } else {
 28221        var req = {
 28222          eles: ele.spawn().merge(ele),
 28223          level: lvl,
 28224          reqs: 1,
 28225          key: key
 28226        };
 28227        q.push(req);
 28228        k2q[key] = req;
 28229      }
 28230    };
 28231  
 28232    ETCp.dequeue = function (pxRatio
 28233    /*, extent*/
 28234    ) {
 28235      var self = this;
 28236      var q = self.getElementQueue();
 28237      var k2q = self.getElementKeyToQueue();
 28238      var dequeued = [];
 28239      var lookup = self.lookup;
 28240  
 28241      for (var i = 0; i < maxDeqSize; i++) {
 28242        if (q.size() > 0) {
 28243          var req = q.pop();
 28244          var key = req.key;
 28245          var ele = req.eles[0]; // all eles have the same key
 28246  
 28247          var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
 28248  
 28249          k2q[key] = null; // dequeueing isn't necessary with an existing cache
 28250  
 28251          if (cacheExists) {
 28252            continue;
 28253          }
 28254  
 28255          dequeued.push(req);
 28256          var bb = self.getBoundingBox(ele);
 28257          self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
 28258        } else {
 28259          break;
 28260        }
 28261      }
 28262  
 28263      return dequeued;
 28264    };
 28265  
 28266    ETCp.removeFromQueue = function (ele) {
 28267      var self = this;
 28268      var q = self.getElementQueue();
 28269      var k2q = self.getElementKeyToQueue();
 28270      var key = this.getKey(ele);
 28271      var req = k2q[key];
 28272  
 28273      if (req != null) {
 28274        if (req.eles.length === 1) {
 28275          // remove if last ele in the req
 28276          // bring to front of queue
 28277          req.reqs = MAX_INT;
 28278          q.updateItem(req);
 28279          q.pop(); // remove from queue
 28280  
 28281          k2q[key] = null; // remove from lookup map
 28282        } else {
 28283          // otherwise just remove ele from req
 28284          req.eles.unmerge(ele);
 28285        }
 28286      }
 28287    };
 28288  
 28289    ETCp.onDequeue = function (fn) {
 28290      this.onDequeues.push(fn);
 28291    };
 28292  
 28293    ETCp.offDequeue = function (fn) {
 28294      removeFromArray(this.onDequeues, fn);
 28295    };
 28296  
 28297    ETCp.setupDequeueing = defs.setupDequeueing({
 28298      deqRedrawThreshold: deqRedrawThreshold,
 28299      deqCost: deqCost,
 28300      deqAvgCost: deqAvgCost,
 28301      deqNoDrawCost: deqNoDrawCost,
 28302      deqFastCost: deqFastCost,
 28303      deq: function deq(self, pxRatio, extent) {
 28304        return self.dequeue(pxRatio, extent);
 28305      },
 28306      onDeqd: function onDeqd(self, deqd) {
 28307        for (var i = 0; i < self.onDequeues.length; i++) {
 28308          var fn = self.onDequeues[i];
 28309          fn(deqd);
 28310        }
 28311      },
 28312      shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
 28313        for (var i = 0; i < deqd.length; i++) {
 28314          var eles = deqd[i].eles;
 28315  
 28316          for (var j = 0; j < eles.length; j++) {
 28317            var bb = eles[j].boundingBox();
 28318  
 28319            if (boundingBoxesIntersect(bb, extent)) {
 28320              return true;
 28321            }
 28322          }
 28323        }
 28324  
 28325        return false;
 28326      },
 28327      priority: function priority(self) {
 28328        return self.renderer.beforeRenderPriorities.eleTxrDeq;
 28329      }
 28330    });
 28331  
 28332    var defNumLayers = 1; // default number of layers to use
 28333  
 28334    var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
 28335  
 28336    var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
 28337  
 28338    var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
 28339  
 28340    var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
 28341  
 28342    var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
 28343  
 28344    var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
 28345  
 28346    var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
 28347  
 28348    var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
 28349  
 28350    var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
 28351  
 28352    var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
 28353  
 28354    var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
 28355  
 28356    var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
 28357  
 28358    var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
 28359    // var log = function(){ console.log.apply( console, arguments ); };
 28360  
 28361    var LayeredTextureCache = function LayeredTextureCache(renderer) {
 28362      var self = this;
 28363      var r = self.renderer = renderer;
 28364      var cy = r.cy;
 28365      self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
 28366  
 28367      self.firstGet = true;
 28368      self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
 28369      self.skipping = false;
 28370      self.eleTxrDeqs = cy.collection();
 28371      self.scheduleElementRefinement = lodash_debounce(function () {
 28372        self.refineElementTextures(self.eleTxrDeqs);
 28373        self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
 28374      }, refineEleDebounceTime);
 28375      r.beforeRender(function (willDraw, now) {
 28376        if (now - self.lastInvalidationTime <= invalidThreshold) {
 28377          self.skipping = true;
 28378        } else {
 28379          self.skipping = false;
 28380        }
 28381      }, r.beforeRenderPriorities.lyrTxrSkip);
 28382  
 28383      var qSort = function qSort(a, b) {
 28384        return b.reqs - a.reqs;
 28385      };
 28386  
 28387      self.layersQueue = new heap$1(qSort);
 28388      self.setupDequeueing();
 28389    };
 28390  
 28391    var LTCp = LayeredTextureCache.prototype;
 28392    var layerIdPool = 0;
 28393    var MAX_INT$1 = Math.pow(2, 53) - 1;
 28394  
 28395    LTCp.makeLayer = function (bb, lvl) {
 28396      var scale = Math.pow(2, lvl);
 28397      var w = Math.ceil(bb.w * scale);
 28398      var h = Math.ceil(bb.h * scale);
 28399      var canvas = this.renderer.makeOffscreenCanvas(w, h);
 28400      var layer = {
 28401        id: layerIdPool = ++layerIdPool % MAX_INT$1,
 28402        bb: bb,
 28403        level: lvl,
 28404        width: w,
 28405        height: h,
 28406        canvas: canvas,
 28407        context: canvas.getContext('2d'),
 28408        eles: [],
 28409        elesQueue: [],
 28410        reqs: 0
 28411      }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
 28412  
 28413      var cxt = layer.context;
 28414      var dx = -layer.bb.x1;
 28415      var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
 28416  
 28417      cxt.scale(scale, scale);
 28418      cxt.translate(dx, dy);
 28419      return layer;
 28420    };
 28421  
 28422    LTCp.getLayers = function (eles, pxRatio, lvl) {
 28423      var self = this;
 28424      var r = self.renderer;
 28425      var cy = r.cy;
 28426      var zoom = cy.zoom();
 28427      var firstGet = self.firstGet;
 28428      self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
 28429      //log eles.map(function(ele){ return ele.id() }) );
 28430  
 28431      if (lvl == null) {
 28432        lvl = Math.ceil(log2(zoom * pxRatio));
 28433  
 28434        if (lvl < minLvl$1) {
 28435          lvl = minLvl$1;
 28436        } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
 28437          return null;
 28438        }
 28439      }
 28440  
 28441      self.validateLayersElesOrdering(lvl, eles);
 28442      var layersByLvl = self.layersByLevel;
 28443      var scale = Math.pow(2, lvl);
 28444      var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
 28445      var bb;
 28446      var lvlComplete = self.levelIsComplete(lvl, eles);
 28447      var tmpLayers;
 28448  
 28449      var checkTempLevels = function checkTempLevels() {
 28450        var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
 28451          self.validateLayersElesOrdering(l, eles);
 28452  
 28453          if (self.levelIsComplete(l, eles)) {
 28454            tmpLayers = layersByLvl[l];
 28455            return true;
 28456          }
 28457        };
 28458  
 28459        var checkLvls = function checkLvls(dir) {
 28460          if (tmpLayers) {
 28461            return;
 28462          }
 28463  
 28464          for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
 28465            if (canUseAsTmpLvl(l)) {
 28466              break;
 28467            }
 28468          }
 28469        };
 28470  
 28471        checkLvls(+1);
 28472        checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
 28473  
 28474        for (var i = layers.length - 1; i >= 0; i--) {
 28475          var layer = layers[i];
 28476  
 28477          if (layer.invalid) {
 28478            removeFromArray(layers, layer);
 28479          }
 28480        }
 28481      };
 28482  
 28483      if (!lvlComplete) {
 28484        // if the current level is incomplete, then use the closest, best quality layerset temporarily
 28485        // and later queue the current layerset so we can get the proper quality level soon
 28486        checkTempLevels();
 28487      } else {
 28488        // log('level complete, using existing layers\n--');
 28489        return layers;
 28490      }
 28491  
 28492      var getBb = function getBb() {
 28493        if (!bb) {
 28494          bb = makeBoundingBox();
 28495  
 28496          for (var i = 0; i < eles.length; i++) {
 28497            updateBoundingBox(bb, eles[i].boundingBox());
 28498          }
 28499        }
 28500  
 28501        return bb;
 28502      };
 28503  
 28504      var makeLayer = function makeLayer(opts) {
 28505        opts = opts || {};
 28506        var after = opts.after;
 28507        getBb();
 28508        var area = bb.w * scale * (bb.h * scale);
 28509  
 28510        if (area > maxLayerArea) {
 28511          return null;
 28512        }
 28513  
 28514        var layer = self.makeLayer(bb, lvl);
 28515  
 28516        if (after != null) {
 28517          var index = layers.indexOf(after) + 1;
 28518          layers.splice(index, 0, layer);
 28519        } else if (opts.insert === undefined || opts.insert) {
 28520          // no after specified => first layer made so put at start
 28521          layers.unshift(layer);
 28522        } // if( tmpLayers ){
 28523        //self.queueLayer( layer );
 28524        // }
 28525  
 28526  
 28527        return layer;
 28528      };
 28529  
 28530      if (self.skipping && !firstGet) {
 28531        // log('skip layers');
 28532        return null;
 28533      } // log('do layers');
 28534  
 28535  
 28536      var layer = null;
 28537      var maxElesPerLayer = eles.length / defNumLayers;
 28538      var allowLazyQueueing =  !firstGet;
 28539  
 28540      for (var i = 0; i < eles.length; i++) {
 28541        var ele = eles[i];
 28542        var rs = ele._private.rscratch;
 28543        var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
 28544  
 28545        var existingLayer = caches[lvl];
 28546  
 28547        if (existingLayer) {
 28548          // reuse layer for later eles
 28549          // log('reuse layer for', ele.id());
 28550          layer = existingLayer;
 28551          continue;
 28552        }
 28553  
 28554        if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
 28555          // log('make new layer for ele %s', ele.id());
 28556          layer = makeLayer({
 28557            insert: true,
 28558            after: layer
 28559          }); // if now layer can be built then we can't use layers at this level
 28560  
 28561          if (!layer) {
 28562            return null;
 28563          } // log('new layer with id %s', layer.id);
 28564  
 28565        }
 28566  
 28567        if (tmpLayers || allowLazyQueueing) {
 28568          // log('queue ele %s in layer %s', ele.id(), layer.id);
 28569          self.queueLayer(layer, ele);
 28570        } else {
 28571          // log('draw ele %s in layer %s', ele.id(), layer.id);
 28572          self.drawEleInLayer(layer, ele, lvl, pxRatio);
 28573        }
 28574  
 28575        layer.eles.push(ele);
 28576        caches[lvl] = layer;
 28577      } // log('--');
 28578  
 28579  
 28580      if (tmpLayers) {
 28581        // then we only queued the current layerset and can't draw it yet
 28582        return tmpLayers;
 28583      }
 28584  
 28585      if (allowLazyQueueing) {
 28586        // log('lazy queue level', lvl);
 28587        return null;
 28588      }
 28589  
 28590      return layers;
 28591    }; // a layer may want to use an ele cache of a higher level to avoid blurriness
 28592    // so the layer level might not equal the ele level
 28593  
 28594  
 28595    LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
 28596      return lvl;
 28597    };
 28598  
 28599    LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
 28600      var self = this;
 28601      var r = this.renderer;
 28602      var context = layer.context;
 28603      var bb = ele.boundingBox();
 28604  
 28605      if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
 28606        return;
 28607      }
 28608  
 28609      lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
 28610  
 28611      {
 28612        r.setImgSmoothing(context, false);
 28613      }
 28614  
 28615      {
 28616        r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
 28617      }
 28618  
 28619      {
 28620        r.setImgSmoothing(context, true);
 28621      }
 28622    };
 28623  
 28624    LTCp.levelIsComplete = function (lvl, eles) {
 28625      var self = this;
 28626      var layers = self.layersByLevel[lvl];
 28627  
 28628      if (!layers || layers.length === 0) {
 28629        return false;
 28630      }
 28631  
 28632      var numElesInLayers = 0;
 28633  
 28634      for (var i = 0; i < layers.length; i++) {
 28635        var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
 28636  
 28637        if (layer.reqs > 0) {
 28638          return false;
 28639        } // if the layer is invalid, the level is not complete
 28640  
 28641  
 28642        if (layer.invalid) {
 28643          return false;
 28644        }
 28645  
 28646        numElesInLayers += layer.eles.length;
 28647      } // we should have exactly the number of eles passed in to be complete
 28648  
 28649  
 28650      if (numElesInLayers !== eles.length) {
 28651        return false;
 28652      }
 28653  
 28654      return true;
 28655    };
 28656  
 28657    LTCp.validateLayersElesOrdering = function (lvl, eles) {
 28658      var layers = this.layersByLevel[lvl];
 28659  
 28660      if (!layers) {
 28661        return;
 28662      } // if in a layer the eles are not in the same order, then the layer is invalid
 28663      // (i.e. there is an ele in between the eles in the layer)
 28664  
 28665  
 28666      for (var i = 0; i < layers.length; i++) {
 28667        var layer = layers[i];
 28668        var offset = -1; // find the offset
 28669  
 28670        for (var j = 0; j < eles.length; j++) {
 28671          if (layer.eles[0] === eles[j]) {
 28672            offset = j;
 28673            break;
 28674          }
 28675        }
 28676  
 28677        if (offset < 0) {
 28678          // then the layer has nonexistant elements and is invalid
 28679          this.invalidateLayer(layer);
 28680          continue;
 28681        } // the eles in the layer must be in the same continuous order, else the layer is invalid
 28682  
 28683  
 28684        var o = offset;
 28685  
 28686        for (var j = 0; j < layer.eles.length; j++) {
 28687          if (layer.eles[j] !== eles[o + j]) {
 28688            // log('invalidate based on ordering', layer.id);
 28689            this.invalidateLayer(layer);
 28690            break;
 28691          }
 28692        }
 28693      }
 28694    };
 28695  
 28696    LTCp.updateElementsInLayers = function (eles, update) {
 28697      var self = this;
 28698      var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
 28699      // layer itself along the way
 28700  
 28701      for (var i = 0; i < eles.length; i++) {
 28702        var req = isEles ? null : eles[i];
 28703        var ele = isEles ? eles[i] : eles[i].ele;
 28704        var rs = ele._private.rscratch;
 28705        var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
 28706  
 28707        for (var l = minLvl$1; l <= maxLvl$1; l++) {
 28708          var layer = caches[l];
 28709  
 28710          if (!layer) {
 28711            continue;
 28712          } // if update is a request from the ele cache, then it affects only
 28713          // the matching level
 28714  
 28715  
 28716          if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
 28717            continue;
 28718          }
 28719  
 28720          update(layer, ele, req);
 28721        }
 28722      }
 28723    };
 28724  
 28725    LTCp.haveLayers = function () {
 28726      var self = this;
 28727      var haveLayers = false;
 28728  
 28729      for (var l = minLvl$1; l <= maxLvl$1; l++) {
 28730        var layers = self.layersByLevel[l];
 28731  
 28732        if (layers && layers.length > 0) {
 28733          haveLayers = true;
 28734          break;
 28735        }
 28736      }
 28737  
 28738      return haveLayers;
 28739    };
 28740  
 28741    LTCp.invalidateElements = function (eles) {
 28742      var self = this;
 28743  
 28744      if (eles.length === 0) {
 28745        return;
 28746      }
 28747  
 28748      self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
 28749  
 28750      if (eles.length === 0 || !self.haveLayers()) {
 28751        return;
 28752      }
 28753  
 28754      self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
 28755        self.invalidateLayer(layer);
 28756      });
 28757    };
 28758  
 28759    LTCp.invalidateLayer = function (layer) {
 28760      // log('update invalidate layer time');
 28761      this.lastInvalidationTime = performanceNow();
 28762  
 28763      if (layer.invalid) {
 28764        return;
 28765      } // save cycles
 28766  
 28767  
 28768      var lvl = layer.level;
 28769      var eles = layer.eles;
 28770      var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
 28771  
 28772      removeFromArray(layers, layer); // layer.eles = [];
 28773  
 28774      layer.elesQueue = [];
 28775      layer.invalid = true;
 28776  
 28777      if (layer.replacement) {
 28778        layer.replacement.invalid = true;
 28779      }
 28780  
 28781      for (var i = 0; i < eles.length; i++) {
 28782        var caches = eles[i]._private.rscratch.imgLayerCaches;
 28783  
 28784        if (caches) {
 28785          caches[lvl] = null;
 28786        }
 28787      }
 28788    };
 28789  
 28790    LTCp.refineElementTextures = function (eles) {
 28791      var self = this; // log('refine', eles.length);
 28792  
 28793      self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
 28794        var rLyr = layer.replacement;
 28795  
 28796        if (!rLyr) {
 28797          rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
 28798          rLyr.replaces = layer;
 28799          rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
 28800        }
 28801  
 28802        if (!rLyr.reqs) {
 28803          for (var i = 0; i < rLyr.eles.length; i++) {
 28804            self.queueLayer(rLyr, rLyr.eles[i]);
 28805          } // log('queue replacement layer refinement', rLyr.id);
 28806  
 28807        }
 28808      });
 28809    };
 28810  
 28811    LTCp.enqueueElementRefinement = function (ele) {
 28812  
 28813      this.eleTxrDeqs.merge(ele);
 28814      this.scheduleElementRefinement();
 28815    };
 28816  
 28817    LTCp.queueLayer = function (layer, ele) {
 28818      var self = this;
 28819      var q = self.layersQueue;
 28820      var elesQ = layer.elesQueue;
 28821      var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
 28822  
 28823      if (layer.replacement) {
 28824        return;
 28825      }
 28826  
 28827      if (ele) {
 28828        if (hasId[ele.id()]) {
 28829          return;
 28830        }
 28831  
 28832        elesQ.push(ele);
 28833        hasId[ele.id()] = true;
 28834      }
 28835  
 28836      if (layer.reqs) {
 28837        layer.reqs++;
 28838        q.updateItem(layer);
 28839      } else {
 28840        layer.reqs = 1;
 28841        q.push(layer);
 28842      }
 28843    };
 28844  
 28845    LTCp.dequeue = function (pxRatio) {
 28846      var self = this;
 28847      var q = self.layersQueue;
 28848      var deqd = [];
 28849      var eleDeqs = 0;
 28850  
 28851      while (eleDeqs < maxDeqSize$1) {
 28852        if (q.size() === 0) {
 28853          break;
 28854        }
 28855  
 28856        var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
 28857  
 28858        if (layer.replacement) {
 28859          // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
 28860          q.pop();
 28861          continue;
 28862        } // if this is a replacement layer that has been superceded, then forget it
 28863  
 28864  
 28865        if (layer.replaces && layer !== layer.replaces.replacement) {
 28866          // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
 28867          q.pop();
 28868          continue;
 28869        }
 28870  
 28871        if (layer.invalid) {
 28872          // log('replacement layer %s is invalid; dequeued', layer.id);
 28873          q.pop();
 28874          continue;
 28875        }
 28876  
 28877        var ele = layer.elesQueue.shift();
 28878  
 28879        if (ele) {
 28880          // log('dequeue layer %s', layer.id);
 28881          self.drawEleInLayer(layer, ele, layer.level, pxRatio);
 28882          eleDeqs++;
 28883        }
 28884  
 28885        if (deqd.length === 0) {
 28886          // we need only one entry in deqd to queue redrawing etc
 28887          deqd.push(true);
 28888        } // if the layer has all its eles done, then remove from the queue
 28889  
 28890  
 28891        if (layer.elesQueue.length === 0) {
 28892          q.pop();
 28893          layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
 28894          // when a replacement layer is dequeued, it replaces the old layer in the level
 28895  
 28896          if (layer.replaces) {
 28897            self.applyLayerReplacement(layer);
 28898          }
 28899  
 28900          self.requestRedraw();
 28901        }
 28902      }
 28903  
 28904      return deqd;
 28905    };
 28906  
 28907    LTCp.applyLayerReplacement = function (layer) {
 28908      var self = this;
 28909      var layersInLevel = self.layersByLevel[layer.level];
 28910      var replaced = layer.replaces;
 28911      var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
 28912      // refs would be a mistake (i.e. overwriting the true active layer)
 28913  
 28914      if (index < 0 || replaced.invalid) {
 28915        // log('replacement layer would have no effect', layer.id);
 28916        return;
 28917      }
 28918  
 28919      layersInLevel[index] = layer; // replace level ref
 28920      // replace refs in eles
 28921  
 28922      for (var i = 0; i < layer.eles.length; i++) {
 28923        var _p = layer.eles[i]._private;
 28924        var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
 28925  
 28926        if (cache) {
 28927          cache[layer.level] = layer;
 28928        }
 28929      } // log('apply replacement layer %s over %s', layer.id, replaced.id);
 28930  
 28931  
 28932      self.requestRedraw();
 28933    };
 28934  
 28935    LTCp.requestRedraw = lodash_debounce(function () {
 28936      var r = this.renderer;
 28937      r.redrawHint('eles', true);
 28938      r.redrawHint('drag', true);
 28939      r.redraw();
 28940    }, 100);
 28941    LTCp.setupDequeueing = defs.setupDequeueing({
 28942      deqRedrawThreshold: deqRedrawThreshold$1,
 28943      deqCost: deqCost$1,
 28944      deqAvgCost: deqAvgCost$1,
 28945      deqNoDrawCost: deqNoDrawCost$1,
 28946      deqFastCost: deqFastCost$1,
 28947      deq: function deq(self, pxRatio) {
 28948        return self.dequeue(pxRatio);
 28949      },
 28950      onDeqd: noop,
 28951      shouldRedraw: trueify,
 28952      priority: function priority(self) {
 28953        return self.renderer.beforeRenderPriorities.lyrTxrDeq;
 28954      }
 28955    });
 28956  
 28957    var CRp = {};
 28958    var impl;
 28959  
 28960    function polygon(context, points) {
 28961      for (var i = 0; i < points.length; i++) {
 28962        var pt = points[i];
 28963        context.lineTo(pt.x, pt.y);
 28964      }
 28965    }
 28966  
 28967    function triangleBackcurve(context, points, controlPoint) {
 28968      var firstPt;
 28969  
 28970      for (var i = 0; i < points.length; i++) {
 28971        var pt = points[i];
 28972  
 28973        if (i === 0) {
 28974          firstPt = pt;
 28975        }
 28976  
 28977        context.lineTo(pt.x, pt.y);
 28978      }
 28979  
 28980      context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
 28981    }
 28982  
 28983    function triangleTee(context, trianglePoints, teePoints) {
 28984      if (context.beginPath) {
 28985        context.beginPath();
 28986      }
 28987  
 28988      var triPts = trianglePoints;
 28989  
 28990      for (var i = 0; i < triPts.length; i++) {
 28991        var pt = triPts[i];
 28992        context.lineTo(pt.x, pt.y);
 28993      }
 28994  
 28995      var teePts = teePoints;
 28996      var firstTeePt = teePoints[0];
 28997      context.moveTo(firstTeePt.x, firstTeePt.y);
 28998  
 28999      for (var i = 1; i < teePts.length; i++) {
 29000        var pt = teePts[i];
 29001        context.lineTo(pt.x, pt.y);
 29002      }
 29003  
 29004      if (context.closePath) {
 29005        context.closePath();
 29006      }
 29007    }
 29008  
 29009    function circle(context, rx, ry, r) {
 29010      context.arc(rx, ry, r, 0, Math.PI * 2, false);
 29011    }
 29012  
 29013    CRp.arrowShapeImpl = function (name) {
 29014      return (impl || (impl = {
 29015        'polygon': polygon,
 29016        'triangle-backcurve': triangleBackcurve,
 29017        'triangle-tee': triangleTee,
 29018        'triangle-cross': triangleTee,
 29019        'circle': circle
 29020      }))[name];
 29021    };
 29022  
 29023    var CRp$1 = {};
 29024  
 29025    CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
 29026      var r = this;
 29027  
 29028      if (ele.isNode()) {
 29029        r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
 29030      } else {
 29031        r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
 29032      }
 29033    };
 29034  
 29035    CRp$1.drawElementOverlay = function (context, ele) {
 29036      var r = this;
 29037  
 29038      if (ele.isNode()) {
 29039        r.drawNodeOverlay(context, ele);
 29040      } else {
 29041        r.drawEdgeOverlay(context, ele);
 29042      }
 29043    };
 29044  
 29045    CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
 29046      var r = this;
 29047      var bb = eleTxrCache.getBoundingBox(ele);
 29048  
 29049      if (bb.w === 0 || bb.h === 0) {
 29050        return;
 29051      } // ignore zero size case
 29052  
 29053  
 29054      var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
 29055  
 29056      if (eleCache != null) {
 29057        var opacity = getOpacity(r, ele);
 29058  
 29059        if (opacity === 0) {
 29060          return;
 29061        }
 29062  
 29063        var theta = getRotation(r, ele);
 29064        var x1 = bb.x1,
 29065            y1 = bb.y1,
 29066            w = bb.w,
 29067            h = bb.h;
 29068        var x, y, sx, sy, smooth;
 29069  
 29070        if (theta !== 0) {
 29071          var rotPt = eleTxrCache.getRotationPoint(ele);
 29072          sx = rotPt.x;
 29073          sy = rotPt.y;
 29074          context.translate(sx, sy);
 29075          context.rotate(theta);
 29076          smooth = r.getImgSmoothing(context);
 29077  
 29078          if (!smooth) {
 29079            r.setImgSmoothing(context, true);
 29080          }
 29081  
 29082          var off = eleTxrCache.getRotationOffset(ele);
 29083          x = off.x;
 29084          y = off.y;
 29085        } else {
 29086          x = x1;
 29087          y = y1;
 29088        }
 29089  
 29090        var oldGlobalAlpha;
 29091  
 29092        if (opacity !== 1) {
 29093          oldGlobalAlpha = context.globalAlpha;
 29094          context.globalAlpha = oldGlobalAlpha * opacity;
 29095        }
 29096  
 29097        context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
 29098  
 29099        if (opacity !== 1) {
 29100          context.globalAlpha = oldGlobalAlpha;
 29101        }
 29102  
 29103        if (theta !== 0) {
 29104          context.rotate(-theta);
 29105          context.translate(-sx, -sy);
 29106  
 29107          if (!smooth) {
 29108            r.setImgSmoothing(context, false);
 29109          }
 29110        }
 29111      } else {
 29112        eleTxrCache.drawElement(context, ele); // direct draw fallback
 29113      }
 29114    };
 29115  
 29116    var getZeroRotation = function getZeroRotation() {
 29117      return 0;
 29118    };
 29119  
 29120    var getLabelRotation = function getLabelRotation(r, ele) {
 29121      return r.getTextAngle(ele, null);
 29122    };
 29123  
 29124    var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
 29125      return r.getTextAngle(ele, 'source');
 29126    };
 29127  
 29128    var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
 29129      return r.getTextAngle(ele, 'target');
 29130    };
 29131  
 29132    var getOpacity = function getOpacity(r, ele) {
 29133      return ele.effectiveOpacity();
 29134    };
 29135  
 29136    var getTextOpacity = function getTextOpacity(e, ele) {
 29137      return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
 29138    };
 29139  
 29140    CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
 29141      var r = this;
 29142      var _r$data = r.data,
 29143          eleTxrCache = _r$data.eleTxrCache,
 29144          lblTxrCache = _r$data.lblTxrCache,
 29145          slbTxrCache = _r$data.slbTxrCache,
 29146          tlbTxrCache = _r$data.tlbTxrCache;
 29147      var bb = ele.boundingBox();
 29148      var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
 29149  
 29150      if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
 29151        return;
 29152      }
 29153  
 29154      if (!extent || boundingBoxesIntersect(bb, extent)) {
 29155        var isEdge = ele.isEdge();
 29156  
 29157        var badLine = ele.element()._private.rscratch.badLine;
 29158  
 29159        r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
 29160  
 29161        if (!isEdge || !badLine) {
 29162          r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
 29163        }
 29164  
 29165        if (isEdge && !badLine) {
 29166          r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
 29167          r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
 29168        }
 29169  
 29170        r.drawElementOverlay(context, ele);
 29171      }
 29172    };
 29173  
 29174    CRp$1.drawElements = function (context, eles) {
 29175      var r = this;
 29176  
 29177      for (var i = 0; i < eles.length; i++) {
 29178        var ele = eles[i];
 29179        r.drawElement(context, ele);
 29180      }
 29181    };
 29182  
 29183    CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
 29184      var r = this;
 29185  
 29186      for (var i = 0; i < eles.length; i++) {
 29187        var ele = eles[i];
 29188        r.drawCachedElement(context, ele, pxRatio, extent);
 29189      }
 29190    };
 29191  
 29192    CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
 29193      var r = this;
 29194  
 29195      for (var i = 0; i < eles.length; i++) {
 29196        var ele = eles[i];
 29197  
 29198        if (!ele.isNode()) {
 29199          continue;
 29200        }
 29201  
 29202        r.drawCachedElement(context, ele, pxRatio, extent);
 29203      }
 29204    };
 29205  
 29206    CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
 29207      var r = this;
 29208      var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
 29209  
 29210      if (layers) {
 29211        for (var i = 0; i < layers.length; i++) {
 29212          var layer = layers[i];
 29213          var bb = layer.bb;
 29214  
 29215          if (bb.w === 0 || bb.h === 0) {
 29216            continue;
 29217          }
 29218  
 29219          context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
 29220        }
 29221      } else {
 29222        // fall back on plain caching if no layers
 29223        r.drawCachedElements(context, eles, pxRatio, extent);
 29224      }
 29225    };
 29226  
 29227    /* global Path2D */
 29228    var CRp$2 = {};
 29229  
 29230    CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
 29231      var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
 29232      var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
 29233      var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
 29234      var r = this;
 29235      var rs = edge._private.rscratch;
 29236  
 29237      if (shouldDrawOpacity && !edge.visible()) {
 29238        return;
 29239      } // if bezier ctrl pts can not be calculated, then die
 29240  
 29241  
 29242      if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
 29243        // isNaN in case edge is impossible and browser bugs (e.g. safari)
 29244        return;
 29245      }
 29246  
 29247      var bb;
 29248  
 29249      if (shiftToOriginWithBb) {
 29250        bb = shiftToOriginWithBb;
 29251        context.translate(-bb.x1, -bb.y1);
 29252      }
 29253  
 29254      var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
 29255      var lineStyle = edge.pstyle('line-style').value;
 29256      var edgeWidth = edge.pstyle('width').pfValue;
 29257      var lineCap = edge.pstyle('line-cap').value;
 29258  
 29259      var drawLine = function drawLine() {
 29260        var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
 29261        context.lineWidth = edgeWidth;
 29262        context.lineCap = lineCap;
 29263        r.eleStrokeStyle(context, edge, strokeOpacity);
 29264        r.drawEdgePath(edge, context, rs.allpts, lineStyle);
 29265        context.lineCap = 'butt'; // reset for other drawing functions
 29266      };
 29267  
 29268      var drawOverlay = function drawOverlay() {
 29269        if (!shouldDrawOverlay) {
 29270          return;
 29271        }
 29272  
 29273        r.drawEdgeOverlay(context, edge);
 29274      };
 29275  
 29276      var drawArrows = function drawArrows() {
 29277        var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
 29278        r.drawArrowheads(context, edge, arrowOpacity);
 29279      };
 29280  
 29281      var drawText = function drawText() {
 29282        r.drawElementText(context, edge, null, drawLabel);
 29283      };
 29284  
 29285      context.lineJoin = 'round';
 29286      var ghost = edge.pstyle('ghost').value === 'yes';
 29287  
 29288      if (ghost) {
 29289        var gx = edge.pstyle('ghost-offset-x').pfValue;
 29290        var gy = edge.pstyle('ghost-offset-y').pfValue;
 29291        var ghostOpacity = edge.pstyle('ghost-opacity').value;
 29292        var effectiveGhostOpacity = opacity * ghostOpacity;
 29293        context.translate(gx, gy);
 29294        drawLine(effectiveGhostOpacity);
 29295        drawArrows(effectiveGhostOpacity);
 29296        context.translate(-gx, -gy);
 29297      }
 29298  
 29299      drawLine();
 29300      drawArrows();
 29301      drawOverlay();
 29302      drawText();
 29303  
 29304      if (shiftToOriginWithBb) {
 29305        context.translate(bb.x1, bb.y1);
 29306      }
 29307    };
 29308  
 29309    CRp$2.drawEdgeOverlay = function (context, edge) {
 29310      if (!edge.visible()) {
 29311        return;
 29312      }
 29313  
 29314      var overlayOpacity = edge.pstyle('overlay-opacity').value;
 29315  
 29316      if (overlayOpacity === 0) {
 29317        return;
 29318      }
 29319  
 29320      var r = this;
 29321      var usePaths = r.usePaths();
 29322      var rs = edge._private.rscratch;
 29323      var overlayPadding = edge.pstyle('overlay-padding').pfValue;
 29324      var overlayWidth = 2 * overlayPadding;
 29325      var overlayColor = edge.pstyle('overlay-color').value;
 29326      context.lineWidth = overlayWidth;
 29327  
 29328      if (rs.edgeType === 'self' && !usePaths) {
 29329        context.lineCap = 'butt';
 29330      } else {
 29331        context.lineCap = 'round';
 29332      }
 29333  
 29334      r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
 29335      r.drawEdgePath(edge, context, rs.allpts, 'solid');
 29336    };
 29337  
 29338    CRp$2.drawEdgePath = function (edge, context, pts, type) {
 29339      var rs = edge._private.rscratch;
 29340      var canvasCxt = context;
 29341      var path;
 29342      var pathCacheHit = false;
 29343      var usePaths = this.usePaths();
 29344      var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
 29345      var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
 29346  
 29347      if (usePaths) {
 29348        var pathCacheKey = pts.join('$');
 29349        var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
 29350  
 29351        if (keyMatches) {
 29352          path = context = rs.pathCache;
 29353          pathCacheHit = true;
 29354        } else {
 29355          path = context = new Path2D();
 29356          rs.pathCacheKey = pathCacheKey;
 29357          rs.pathCache = path;
 29358        }
 29359      }
 29360  
 29361      if (canvasCxt.setLineDash) {
 29362        // for very outofdate browsers
 29363        switch (type) {
 29364          case 'dotted':
 29365            canvasCxt.setLineDash([1, 1]);
 29366            break;
 29367  
 29368          case 'dashed':
 29369            canvasCxt.setLineDash(lineDashPattern);
 29370            canvasCxt.lineDashOffset = lineDashOffset;
 29371            break;
 29372  
 29373          case 'solid':
 29374            canvasCxt.setLineDash([]);
 29375            break;
 29376        }
 29377      }
 29378  
 29379      if (!pathCacheHit && !rs.badLine) {
 29380        if (context.beginPath) {
 29381          context.beginPath();
 29382        }
 29383  
 29384        context.moveTo(pts[0], pts[1]);
 29385  
 29386        switch (rs.edgeType) {
 29387          case 'bezier':
 29388          case 'self':
 29389          case 'compound':
 29390          case 'multibezier':
 29391            for (var i = 2; i + 3 < pts.length; i += 4) {
 29392              context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
 29393            }
 29394  
 29395            break;
 29396  
 29397          case 'straight':
 29398          case 'segments':
 29399          case 'haystack':
 29400            for (var _i = 2; _i + 1 < pts.length; _i += 2) {
 29401              context.lineTo(pts[_i], pts[_i + 1]);
 29402            }
 29403  
 29404            break;
 29405        }
 29406      }
 29407  
 29408      context = canvasCxt;
 29409  
 29410      if (usePaths) {
 29411        context.stroke(path);
 29412      } else {
 29413        context.stroke();
 29414      } // reset any line dashes
 29415  
 29416  
 29417      if (context.setLineDash) {
 29418        // for very outofdate browsers
 29419        context.setLineDash([]);
 29420      }
 29421    };
 29422  
 29423    CRp$2.drawArrowheads = function (context, edge, opacity) {
 29424      var rs = edge._private.rscratch;
 29425      var isHaystack = rs.edgeType === 'haystack';
 29426  
 29427      if (!isHaystack) {
 29428        this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
 29429      }
 29430  
 29431      this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
 29432      this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
 29433  
 29434      if (!isHaystack) {
 29435        this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
 29436      }
 29437    };
 29438  
 29439    CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
 29440      if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
 29441        return;
 29442      }
 29443  
 29444      var self = this;
 29445      var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
 29446  
 29447      if (arrowShape === 'none') {
 29448        return;
 29449      }
 29450  
 29451      var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
 29452      var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
 29453      var edgeWidth = edge.pstyle('width').pfValue;
 29454      var edgeOpacity = edge.pstyle('opacity').value;
 29455  
 29456      if (opacity === undefined) {
 29457        opacity = edgeOpacity;
 29458      }
 29459  
 29460      var gco = context.globalCompositeOperation;
 29461  
 29462      if (opacity !== 1 || arrowFill === 'hollow') {
 29463        // then extra clear is needed
 29464        context.globalCompositeOperation = 'destination-out';
 29465        self.colorFillStyle(context, 255, 255, 255, 1);
 29466        self.colorStrokeStyle(context, 255, 255, 255, 1);
 29467        self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
 29468        context.globalCompositeOperation = gco;
 29469      } // otherwise, the opaque arrow clears it for free :)
 29470  
 29471  
 29472      var color = edge.pstyle(prefix + '-arrow-color').value;
 29473      self.colorFillStyle(context, color[0], color[1], color[2], opacity);
 29474      self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
 29475      self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
 29476    };
 29477  
 29478    CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
 29479      var r = this;
 29480      var usePaths = this.usePaths() && shape !== 'triangle-cross';
 29481      var pathCacheHit = false;
 29482      var path;
 29483      var canvasContext = context;
 29484      var translation = {
 29485        x: x,
 29486        y: y
 29487      };
 29488      var scale = edge.pstyle('arrow-scale').value;
 29489      var size = this.getArrowWidth(edgeWidth, scale);
 29490      var shapeImpl = r.arrowShapes[shape];
 29491  
 29492      if (usePaths) {
 29493        var cache = r.arrowPathCache = r.arrowPathCache || [];
 29494        var key = hashString(shape);
 29495        var cachedPath = cache[key];
 29496  
 29497        if (cachedPath != null) {
 29498          path = context = cachedPath;
 29499          pathCacheHit = true;
 29500        } else {
 29501          path = context = new Path2D();
 29502          cache[key] = path;
 29503        }
 29504      }
 29505  
 29506      if (!pathCacheHit) {
 29507        if (context.beginPath) {
 29508          context.beginPath();
 29509        }
 29510  
 29511        if (usePaths) {
 29512          // store in the path cache with values easily manipulated later
 29513          shapeImpl.draw(context, 1, 0, {
 29514            x: 0,
 29515            y: 0
 29516          }, 1);
 29517        } else {
 29518          shapeImpl.draw(context, size, angle, translation, edgeWidth);
 29519        }
 29520  
 29521        if (context.closePath) {
 29522          context.closePath();
 29523        }
 29524      }
 29525  
 29526      context = canvasContext;
 29527  
 29528      if (usePaths) {
 29529        // set transform to arrow position/orientation
 29530        context.translate(x, y);
 29531        context.rotate(angle);
 29532        context.scale(size, size);
 29533      }
 29534  
 29535      if (fill === 'filled' || fill === 'both') {
 29536        if (usePaths) {
 29537          context.fill(path);
 29538        } else {
 29539          context.fill();
 29540        }
 29541      }
 29542  
 29543      if (fill === 'hollow' || fill === 'both') {
 29544        context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
 29545        context.lineJoin = 'miter';
 29546  
 29547        if (usePaths) {
 29548          context.stroke(path);
 29549        } else {
 29550          context.stroke();
 29551        }
 29552      }
 29553  
 29554      if (usePaths) {
 29555        // reset transform by applying inverse
 29556        context.scale(1 / size, 1 / size);
 29557        context.rotate(-angle);
 29558        context.translate(-x, -y);
 29559      }
 29560    };
 29561  
 29562    var CRp$3 = {};
 29563  
 29564    CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
 29565      // detect problematic cases for old browsers with bad images (cheaper than try-catch)
 29566      if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
 29567        return;
 29568      }
 29569  
 29570      context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
 29571    };
 29572  
 29573    CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
 29574      var r = this;
 29575      var pos = node.position();
 29576      var nodeX = pos.x;
 29577      var nodeY = pos.y;
 29578      var styleObj = node.cy().style();
 29579      var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
 29580      var fit = getIndexedStyle(node, 'background-fit', 'value', index);
 29581      var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
 29582      var nodeW = node.width();
 29583      var nodeH = node.height();
 29584      var paddingX2 = node.padding() * 2;
 29585      var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
 29586      var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
 29587      var rs = node._private.rscratch;
 29588      var clip = getIndexedStyle(node, 'background-clip', 'value', index);
 29589      var shouldClip = clip === 'node';
 29590      var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
 29591      var imgW = img.width || img.cachedW;
 29592      var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
 29593  
 29594      if (null == imgW || null == imgH) {
 29595        document.body.appendChild(img); // eslint-disable-line no-undef
 29596  
 29597        imgW = img.cachedW = img.width || img.offsetWidth;
 29598        imgH = img.cachedH = img.height || img.offsetHeight;
 29599        document.body.removeChild(img); // eslint-disable-line no-undef
 29600      }
 29601  
 29602      var w = imgW;
 29603      var h = imgH;
 29604  
 29605      if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
 29606        if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
 29607          w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
 29608        } else {
 29609          w = getIndexedStyle(node, 'background-width', 'pfValue', index);
 29610        }
 29611      }
 29612  
 29613      if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
 29614        if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
 29615          h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
 29616        } else {
 29617          h = getIndexedStyle(node, 'background-height', 'pfValue', index);
 29618        }
 29619      }
 29620  
 29621      if (w === 0 || h === 0) {
 29622        return; // no point in drawing empty image (and chrome is broken in this case)
 29623      }
 29624  
 29625      if (fit === 'contain') {
 29626        var scale = Math.min(nodeTW / w, nodeTH / h);
 29627        w *= scale;
 29628        h *= scale;
 29629      } else if (fit === 'cover') {
 29630        var scale = Math.max(nodeTW / w, nodeTH / h);
 29631        w *= scale;
 29632        h *= scale;
 29633      }
 29634  
 29635      var x = nodeX - nodeTW / 2; // left
 29636  
 29637      var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
 29638      var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
 29639  
 29640      if (posXUnits === '%') {
 29641        x += (nodeTW - w) * posXPfVal;
 29642      } else {
 29643        x += posXPfVal;
 29644      }
 29645  
 29646      var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
 29647      var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
 29648  
 29649      if (offXUnits === '%') {
 29650        x += (nodeTW - w) * offXPfVal;
 29651      } else {
 29652        x += offXPfVal;
 29653      }
 29654  
 29655      var y = nodeY - nodeTH / 2; // top
 29656  
 29657      var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
 29658      var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
 29659  
 29660      if (posYUnits === '%') {
 29661        y += (nodeTH - h) * posYPfVal;
 29662      } else {
 29663        y += posYPfVal;
 29664      }
 29665  
 29666      var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
 29667      var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
 29668  
 29669      if (offYUnits === '%') {
 29670        y += (nodeTH - h) * offYPfVal;
 29671      } else {
 29672        y += offYPfVal;
 29673      }
 29674  
 29675      if (rs.pathCache) {
 29676        x -= nodeX;
 29677        y -= nodeY;
 29678        nodeX = 0;
 29679        nodeY = 0;
 29680      }
 29681  
 29682      var gAlpha = context.globalAlpha;
 29683      context.globalAlpha = imgOpacity;
 29684  
 29685      if (repeat === 'no-repeat') {
 29686        if (shouldClip) {
 29687          context.save();
 29688  
 29689          if (rs.pathCache) {
 29690            context.clip(rs.pathCache);
 29691          } else {
 29692            r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
 29693            context.clip();
 29694          }
 29695        }
 29696  
 29697        r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
 29698  
 29699        if (shouldClip) {
 29700          context.restore();
 29701        }
 29702      } else {
 29703        var pattern = context.createPattern(img, repeat);
 29704        context.fillStyle = pattern;
 29705        r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
 29706        context.translate(x, y);
 29707        context.fill();
 29708        context.translate(-x, -y);
 29709      }
 29710  
 29711      context.globalAlpha = gAlpha;
 29712    };
 29713  
 29714    var CRp$4 = {};
 29715  
 29716    CRp$4.eleTextBiggerThanMin = function (ele, scale) {
 29717      if (!scale) {
 29718        var zoom = ele.cy().zoom();
 29719        var pxRatio = this.getPixelRatio();
 29720        var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
 29721  
 29722        scale = Math.pow(2, lvl);
 29723      }
 29724  
 29725      var computedSize = ele.pstyle('font-size').pfValue * scale;
 29726      var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
 29727  
 29728      if (computedSize < minSize) {
 29729        return false;
 29730      }
 29731  
 29732      return true;
 29733    };
 29734  
 29735    CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
 29736      var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
 29737      var r = this;
 29738  
 29739      if (force == null) {
 29740        if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
 29741          return;
 29742        }
 29743      } else if (force === false) {
 29744        return;
 29745      }
 29746  
 29747      if (ele.isNode()) {
 29748        var label = ele.pstyle('label');
 29749  
 29750        if (!label || !label.value) {
 29751          return;
 29752        }
 29753  
 29754        var justification = r.getLabelJustification(ele);
 29755        context.textAlign = justification;
 29756        context.textBaseline = 'bottom';
 29757      } else {
 29758        var badLine = ele.element()._private.rscratch.badLine;
 29759  
 29760        var _label = ele.pstyle('label');
 29761  
 29762        var srcLabel = ele.pstyle('source-label');
 29763        var tgtLabel = ele.pstyle('target-label');
 29764  
 29765        if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
 29766          return;
 29767        }
 29768  
 29769        context.textAlign = 'center';
 29770        context.textBaseline = 'bottom';
 29771      }
 29772  
 29773      var applyRotation = !shiftToOriginWithBb;
 29774      var bb;
 29775  
 29776      if (shiftToOriginWithBb) {
 29777        bb = shiftToOriginWithBb;
 29778        context.translate(-bb.x1, -bb.y1);
 29779      }
 29780  
 29781      if (prefix == null) {
 29782        r.drawText(context, ele, null, applyRotation, useEleOpacity);
 29783  
 29784        if (ele.isEdge()) {
 29785          r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
 29786          r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
 29787        }
 29788      } else {
 29789        r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
 29790      }
 29791  
 29792      if (shiftToOriginWithBb) {
 29793        context.translate(bb.x1, bb.y1);
 29794      }
 29795    };
 29796  
 29797    CRp$4.getFontCache = function (context) {
 29798      var cache;
 29799      this.fontCaches = this.fontCaches || [];
 29800  
 29801      for (var i = 0; i < this.fontCaches.length; i++) {
 29802        cache = this.fontCaches[i];
 29803  
 29804        if (cache.context === context) {
 29805          return cache;
 29806        }
 29807      }
 29808  
 29809      cache = {
 29810        context: context
 29811      };
 29812      this.fontCaches.push(cache);
 29813      return cache;
 29814    }; // set up canvas context with font
 29815    // returns transformed text string
 29816  
 29817  
 29818    CRp$4.setupTextStyle = function (context, ele) {
 29819      var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
 29820      // Font style
 29821      var labelStyle = ele.pstyle('font-style').strValue;
 29822      var labelSize = ele.pstyle('font-size').pfValue + 'px';
 29823      var labelFamily = ele.pstyle('font-family').strValue;
 29824      var labelWeight = ele.pstyle('font-weight').strValue;
 29825      var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
 29826      var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
 29827      var color = ele.pstyle('color').value;
 29828      var outlineColor = ele.pstyle('text-outline-color').value;
 29829      context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
 29830      context.lineJoin = 'round'; // so text outlines aren't jagged
 29831  
 29832      this.colorFillStyle(context, color[0], color[1], color[2], opacity);
 29833      this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
 29834    }; // TODO ensure re-used
 29835  
 29836  
 29837    function roundRect(ctx, x, y, width, height) {
 29838      var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
 29839      ctx.beginPath();
 29840      ctx.moveTo(x + radius, y);
 29841      ctx.lineTo(x + width - radius, y);
 29842      ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
 29843      ctx.lineTo(x + width, y + height - radius);
 29844      ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
 29845      ctx.lineTo(x + radius, y + height);
 29846      ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
 29847      ctx.lineTo(x, y + radius);
 29848      ctx.quadraticCurveTo(x, y, x + radius, y);
 29849      ctx.closePath();
 29850      ctx.fill();
 29851    }
 29852  
 29853    CRp$4.getTextAngle = function (ele, prefix) {
 29854      var theta;
 29855      var _p = ele._private;
 29856      var rscratch = _p.rscratch;
 29857      var pdash = prefix ? prefix + '-' : '';
 29858      var rotation = ele.pstyle(pdash + 'text-rotation');
 29859      var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
 29860  
 29861      if (rotation.strValue === 'autorotate') {
 29862        theta = ele.isEdge() ? textAngle : 0;
 29863      } else if (rotation.strValue === 'none') {
 29864        theta = 0;
 29865      } else {
 29866        theta = rotation.pfValue;
 29867      }
 29868  
 29869      return theta;
 29870    };
 29871  
 29872    CRp$4.drawText = function (context, ele, prefix) {
 29873      var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
 29874      var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
 29875      var _p = ele._private;
 29876      var rscratch = _p.rscratch;
 29877      var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
 29878  
 29879      if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
 29880        return;
 29881      } // use 'main' as an alias for the main label (i.e. null prefix)
 29882  
 29883  
 29884      if (prefix === 'main') {
 29885        prefix = null;
 29886      }
 29887  
 29888      var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
 29889      var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
 29890      var orgTextX, orgTextY; // used for rotation
 29891  
 29892      var text = this.getLabelText(ele, prefix);
 29893  
 29894      if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
 29895        this.setupTextStyle(context, ele, useEleOpacity);
 29896        var pdash = prefix ? prefix + '-' : '';
 29897        var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
 29898        var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
 29899        var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
 29900        var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
 29901        var isEdge = ele.isEdge();
 29902        var halign = ele.pstyle('text-halign').value;
 29903        var valign = ele.pstyle('text-valign').value;
 29904  
 29905        if (isEdge) {
 29906          halign = 'center';
 29907          valign = 'center';
 29908        }
 29909  
 29910        textX += marginX;
 29911        textY += marginY;
 29912        var theta;
 29913  
 29914        if (!applyRotation) {
 29915          theta = 0;
 29916        } else {
 29917          theta = this.getTextAngle(ele, prefix);
 29918        }
 29919  
 29920        if (theta !== 0) {
 29921          orgTextX = textX;
 29922          orgTextY = textY;
 29923          context.translate(orgTextX, orgTextY);
 29924          context.rotate(theta);
 29925          textX = 0;
 29926          textY = 0;
 29927        }
 29928  
 29929        switch (valign) {
 29930          case 'top':
 29931            break;
 29932  
 29933          case 'center':
 29934            textY += textH / 2;
 29935            break;
 29936  
 29937          case 'bottom':
 29938            textY += textH;
 29939            break;
 29940        }
 29941  
 29942        var backgroundOpacity = ele.pstyle('text-background-opacity').value;
 29943        var borderOpacity = ele.pstyle('text-border-opacity').value;
 29944        var textBorderWidth = ele.pstyle('text-border-width').pfValue;
 29945        var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
 29946  
 29947        if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
 29948          var bgX = textX - backgroundPadding;
 29949  
 29950          switch (halign) {
 29951            case 'left':
 29952              bgX -= textW;
 29953              break;
 29954  
 29955            case 'center':
 29956              bgX -= textW / 2;
 29957              break;
 29958          }
 29959  
 29960          var bgY = textY - textH - backgroundPadding;
 29961          var bgW = textW + 2 * backgroundPadding;
 29962          var bgH = textH + 2 * backgroundPadding;
 29963  
 29964          if (backgroundOpacity > 0) {
 29965            var textFill = context.fillStyle;
 29966            var textBackgroundColor = ele.pstyle('text-background-color').value;
 29967            context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
 29968            var styleShape = ele.pstyle('text-background-shape').strValue;
 29969  
 29970            if (styleShape.indexOf('round') === 0) {
 29971              roundRect(context, bgX, bgY, bgW, bgH, 2);
 29972            } else {
 29973              context.fillRect(bgX, bgY, bgW, bgH);
 29974            }
 29975  
 29976            context.fillStyle = textFill;
 29977          }
 29978  
 29979          if (textBorderWidth > 0 && borderOpacity > 0) {
 29980            var textStroke = context.strokeStyle;
 29981            var textLineWidth = context.lineWidth;
 29982            var textBorderColor = ele.pstyle('text-border-color').value;
 29983            var textBorderStyle = ele.pstyle('text-border-style').value;
 29984            context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
 29985            context.lineWidth = textBorderWidth;
 29986  
 29987            if (context.setLineDash) {
 29988              // for very outofdate browsers
 29989              switch (textBorderStyle) {
 29990                case 'dotted':
 29991                  context.setLineDash([1, 1]);
 29992                  break;
 29993  
 29994                case 'dashed':
 29995                  context.setLineDash([4, 2]);
 29996                  break;
 29997  
 29998                case 'double':
 29999                  context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
 30000  
 30001                  context.setLineDash([]);
 30002                  break;
 30003  
 30004                case 'solid':
 30005                  context.setLineDash([]);
 30006                  break;
 30007              }
 30008            }
 30009  
 30010            context.strokeRect(bgX, bgY, bgW, bgH);
 30011  
 30012            if (textBorderStyle === 'double') {
 30013              var whiteWidth = textBorderWidth / 2;
 30014              context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
 30015            }
 30016  
 30017            if (context.setLineDash) {
 30018              // for very outofdate browsers
 30019              context.setLineDash([]);
 30020            }
 30021  
 30022            context.lineWidth = textLineWidth;
 30023            context.strokeStyle = textStroke;
 30024          }
 30025        }
 30026  
 30027        var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
 30028  
 30029        if (lineWidth > 0) {
 30030          context.lineWidth = lineWidth;
 30031        }
 30032  
 30033        if (ele.pstyle('text-wrap').value === 'wrap') {
 30034          var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
 30035          var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
 30036          var halfTextW = textW / 2;
 30037          var justification = this.getLabelJustification(ele);
 30038  
 30039          if (justification === 'auto') ; else if (halign === 'left') {
 30040            // auto justification : right
 30041            if (justification === 'left') {
 30042              textX += -textW;
 30043            } else if (justification === 'center') {
 30044              textX += -halfTextW;
 30045            } // else same as auto
 30046  
 30047          } else if (halign === 'center') {
 30048            // auto justfication : center
 30049            if (justification === 'left') {
 30050              textX += -halfTextW;
 30051            } else if (justification === 'right') {
 30052              textX += halfTextW;
 30053            } // else same as auto
 30054  
 30055          } else if (halign === 'right') {
 30056            // auto justification : left
 30057            if (justification === 'center') {
 30058              textX += halfTextW;
 30059            } else if (justification === 'right') {
 30060              textX += textW;
 30061            } // else same as auto
 30062  
 30063          }
 30064  
 30065          switch (valign) {
 30066            case 'top':
 30067              textY -= (lines.length - 1) * lineHeight;
 30068              break;
 30069  
 30070            case 'center':
 30071            case 'bottom':
 30072              textY -= (lines.length - 1) * lineHeight;
 30073              break;
 30074          }
 30075  
 30076          for (var l = 0; l < lines.length; l++) {
 30077            if (lineWidth > 0) {
 30078              context.strokeText(lines[l], textX, textY);
 30079            }
 30080  
 30081            context.fillText(lines[l], textX, textY);
 30082            textY += lineHeight;
 30083          }
 30084        } else {
 30085          if (lineWidth > 0) {
 30086            context.strokeText(text, textX, textY);
 30087          }
 30088  
 30089          context.fillText(text, textX, textY);
 30090        }
 30091  
 30092        if (theta !== 0) {
 30093          context.rotate(-theta);
 30094          context.translate(-orgTextX, -orgTextY);
 30095        }
 30096      }
 30097    };
 30098  
 30099    /* global Path2D */
 30100    var CRp$5 = {};
 30101  
 30102    CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
 30103      var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
 30104      var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
 30105      var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
 30106      var r = this;
 30107      var nodeWidth, nodeHeight;
 30108      var _p = node._private;
 30109      var rs = _p.rscratch;
 30110      var pos = node.position();
 30111  
 30112      if (!number(pos.x) || !number(pos.y)) {
 30113        return; // can't draw node with undefined position
 30114      }
 30115  
 30116      if (shouldDrawOpacity && !node.visible()) {
 30117        return;
 30118      }
 30119  
 30120      var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
 30121      var usePaths = r.usePaths();
 30122      var path;
 30123      var pathCacheHit = false;
 30124      var padding = node.padding();
 30125      nodeWidth = node.width() + 2 * padding;
 30126      nodeHeight = node.height() + 2 * padding; //
 30127      // setup shift
 30128  
 30129      var bb;
 30130  
 30131      if (shiftToOriginWithBb) {
 30132        bb = shiftToOriginWithBb;
 30133        context.translate(-bb.x1, -bb.y1);
 30134      } //
 30135      // load bg image
 30136  
 30137  
 30138      var bgImgProp = node.pstyle('background-image');
 30139      var urls = bgImgProp.value;
 30140      var urlDefined = new Array(urls.length);
 30141      var image = new Array(urls.length);
 30142      var numImages = 0;
 30143  
 30144      for (var i = 0; i < urls.length; i++) {
 30145        var url = urls[i];
 30146        var defd = urlDefined[i] = url != null && url !== 'none';
 30147  
 30148        if (defd) {
 30149          var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
 30150          numImages++; // get image, and if not loaded then ask to redraw when later loaded
 30151  
 30152          image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
 30153            _p.backgroundTimestamp = Date.now();
 30154            node.emitAndNotify('background');
 30155          });
 30156        }
 30157      } //
 30158      // setup styles
 30159  
 30160  
 30161      var darkness = node.pstyle('background-blacken').value;
 30162      var borderWidth = node.pstyle('border-width').pfValue;
 30163      var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
 30164      var borderColor = node.pstyle('border-color').value;
 30165      var borderStyle = node.pstyle('border-style').value;
 30166      var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
 30167      context.lineJoin = 'miter'; // so borders are square with the node shape
 30168  
 30169      var setupShapeColor = function setupShapeColor() {
 30170        var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
 30171        r.eleFillStyle(context, node, bgOpy);
 30172      };
 30173  
 30174      var setupBorderColor = function setupBorderColor() {
 30175        var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
 30176        r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
 30177      }; //
 30178      // setup shape
 30179  
 30180  
 30181      var styleShape = node.pstyle('shape').strValue;
 30182      var shapePts = node.pstyle('shape-polygon-points').pfValue;
 30183  
 30184      if (usePaths) {
 30185        context.translate(pos.x, pos.y);
 30186        var pathCache = r.nodePathCache = r.nodePathCache || [];
 30187        var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
 30188        var cachedPath = pathCache[key];
 30189  
 30190        if (cachedPath != null) {
 30191          path = cachedPath;
 30192          pathCacheHit = true;
 30193          rs.pathCache = path;
 30194        } else {
 30195          path = new Path2D();
 30196          pathCache[key] = rs.pathCache = path;
 30197        }
 30198      }
 30199  
 30200      var drawShape = function drawShape() {
 30201        if (!pathCacheHit) {
 30202          var npos = pos;
 30203  
 30204          if (usePaths) {
 30205            npos = {
 30206              x: 0,
 30207              y: 0
 30208            };
 30209          }
 30210  
 30211          r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
 30212        }
 30213  
 30214        if (usePaths) {
 30215          context.fill(path);
 30216        } else {
 30217          context.fill();
 30218        }
 30219      };
 30220  
 30221      var drawImages = function drawImages() {
 30222        var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
 30223        var prevBging = _p.backgrounding;
 30224        var totalCompleted = 0;
 30225  
 30226        for (var _i = 0; _i < image.length; _i++) {
 30227          if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
 30228            totalCompleted++;
 30229            r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
 30230          }
 30231        }
 30232  
 30233        _p.backgrounding = !(totalCompleted === numImages);
 30234  
 30235        if (prevBging !== _p.backgrounding) {
 30236          // update style b/c :backgrounding state changed
 30237          node.updateStyle(false);
 30238        }
 30239      };
 30240  
 30241      var drawPie = function drawPie() {
 30242        var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
 30243        var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
 30244  
 30245        if (r.hasPie(node)) {
 30246          r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
 30247  
 30248          if (redrawShape) {
 30249            if (!usePaths) {
 30250              r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
 30251            }
 30252          }
 30253        }
 30254      };
 30255  
 30256      var darken = function darken() {
 30257        var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
 30258        var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
 30259        var c = darkness > 0 ? 0 : 255;
 30260  
 30261        if (darkness !== 0) {
 30262          r.colorFillStyle(context, c, c, c, opacity);
 30263  
 30264          if (usePaths) {
 30265            context.fill(path);
 30266          } else {
 30267            context.fill();
 30268          }
 30269        }
 30270      };
 30271  
 30272      var drawBorder = function drawBorder() {
 30273        if (borderWidth > 0) {
 30274          context.lineWidth = borderWidth;
 30275          context.lineCap = 'butt';
 30276  
 30277          if (context.setLineDash) {
 30278            // for very outofdate browsers
 30279            switch (borderStyle) {
 30280              case 'dotted':
 30281                context.setLineDash([1, 1]);
 30282                break;
 30283  
 30284              case 'dashed':
 30285                context.setLineDash([4, 2]);
 30286                break;
 30287  
 30288              case 'solid':
 30289              case 'double':
 30290                context.setLineDash([]);
 30291                break;
 30292            }
 30293          }
 30294  
 30295          if (usePaths) {
 30296            context.stroke(path);
 30297          } else {
 30298            context.stroke();
 30299          }
 30300  
 30301          if (borderStyle === 'double') {
 30302            context.lineWidth = borderWidth / 3;
 30303            var gco = context.globalCompositeOperation;
 30304            context.globalCompositeOperation = 'destination-out';
 30305  
 30306            if (usePaths) {
 30307              context.stroke(path);
 30308            } else {
 30309              context.stroke();
 30310            }
 30311  
 30312            context.globalCompositeOperation = gco;
 30313          } // reset in case we changed the border style
 30314  
 30315  
 30316          if (context.setLineDash) {
 30317            // for very outofdate browsers
 30318            context.setLineDash([]);
 30319          }
 30320        }
 30321      };
 30322  
 30323      var drawOverlay = function drawOverlay() {
 30324        if (shouldDrawOverlay) {
 30325          r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
 30326        }
 30327      };
 30328  
 30329      var drawText = function drawText() {
 30330        r.drawElementText(context, node, null, drawLabel);
 30331      };
 30332  
 30333      var ghost = node.pstyle('ghost').value === 'yes';
 30334  
 30335      if (ghost) {
 30336        var gx = node.pstyle('ghost-offset-x').pfValue;
 30337        var gy = node.pstyle('ghost-offset-y').pfValue;
 30338        var ghostOpacity = node.pstyle('ghost-opacity').value;
 30339        var effGhostOpacity = ghostOpacity * eleOpacity;
 30340        context.translate(gx, gy);
 30341        setupShapeColor(ghostOpacity * bgOpacity);
 30342        drawShape();
 30343        drawImages(effGhostOpacity);
 30344        drawPie(darkness !== 0 || borderWidth !== 0);
 30345        darken(effGhostOpacity);
 30346        setupBorderColor(ghostOpacity * borderOpacity);
 30347        drawBorder();
 30348        context.translate(-gx, -gy);
 30349      }
 30350  
 30351      setupShapeColor();
 30352      drawShape();
 30353      drawImages();
 30354      drawPie(darkness !== 0 || borderWidth !== 0);
 30355      darken();
 30356      setupBorderColor();
 30357      drawBorder();
 30358  
 30359      if (usePaths) {
 30360        context.translate(-pos.x, -pos.y);
 30361      }
 30362  
 30363      drawText();
 30364      drawOverlay(); //
 30365      // clean up shift
 30366  
 30367      if (shiftToOriginWithBb) {
 30368        context.translate(bb.x1, bb.y1);
 30369      }
 30370    };
 30371  
 30372    CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
 30373      var r = this;
 30374  
 30375      if (!node.visible()) {
 30376        return;
 30377      }
 30378  
 30379      var overlayPadding = node.pstyle('overlay-padding').pfValue;
 30380      var overlayOpacity = node.pstyle('overlay-opacity').value;
 30381      var overlayColor = node.pstyle('overlay-color').value;
 30382  
 30383      if (overlayOpacity > 0) {
 30384        pos = pos || node.position();
 30385  
 30386        if (nodeWidth == null || nodeHeight == null) {
 30387          var padding = node.padding();
 30388          nodeWidth = node.width() + 2 * padding;
 30389          nodeHeight = node.height() + 2 * padding;
 30390        }
 30391  
 30392        r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
 30393        r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
 30394        context.fill();
 30395      }
 30396    }; // does the node have at least one pie piece?
 30397  
 30398  
 30399    CRp$5.hasPie = function (node) {
 30400      node = node[0]; // ensure ele ref
 30401  
 30402      return node._private.hasPie;
 30403    };
 30404  
 30405    CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
 30406      node = node[0]; // ensure ele ref
 30407  
 30408      pos = pos || node.position();
 30409      var cyStyle = node.cy().style();
 30410      var pieSize = node.pstyle('pie-size');
 30411      var x = pos.x;
 30412      var y = pos.y;
 30413      var nodeW = node.width();
 30414      var nodeH = node.height();
 30415      var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
 30416  
 30417      var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
 30418  
 30419      var usePaths = this.usePaths();
 30420  
 30421      if (usePaths) {
 30422        x = 0;
 30423        y = 0;
 30424      }
 30425  
 30426      if (pieSize.units === '%') {
 30427        radius = radius * pieSize.pfValue;
 30428      } else if (pieSize.pfValue !== undefined) {
 30429        radius = pieSize.pfValue / 2;
 30430      }
 30431  
 30432      for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
 30433        // 1..N
 30434        var size = node.pstyle('pie-' + i + '-background-size').value;
 30435        var color = node.pstyle('pie-' + i + '-background-color').value;
 30436        var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
 30437        var percent = size / 100; // map integer range [0, 100] to [0, 1]
 30438        // percent can't push beyond 1
 30439  
 30440        if (percent + lastPercent > 1) {
 30441          percent = 1 - lastPercent;
 30442        }
 30443  
 30444        var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
 30445  
 30446        var angleDelta = 2 * Math.PI * percent;
 30447        var angleEnd = angleStart + angleDelta; // ignore if
 30448        // - zero size
 30449        // - we're already beyond the full circle
 30450        // - adding the current slice would go beyond the full circle
 30451  
 30452        if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
 30453          continue;
 30454        }
 30455  
 30456        context.beginPath();
 30457        context.moveTo(x, y);
 30458        context.arc(x, y, radius, angleStart, angleEnd);
 30459        context.closePath();
 30460        this.colorFillStyle(context, color[0], color[1], color[2], opacity);
 30461        context.fill();
 30462        lastPercent += percent;
 30463      }
 30464    };
 30465  
 30466    var CRp$6 = {};
 30467    var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
 30468  
 30469    CRp$6.getPixelRatio = function () {
 30470      var context = this.data.contexts[0];
 30471  
 30472      if (this.forcedPixelRatio != null) {
 30473        return this.forcedPixelRatio;
 30474      }
 30475  
 30476      var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
 30477      return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
 30478    };
 30479  
 30480    CRp$6.paintCache = function (context) {
 30481      var caches = this.paintCaches = this.paintCaches || [];
 30482      var needToCreateCache = true;
 30483      var cache;
 30484  
 30485      for (var i = 0; i < caches.length; i++) {
 30486        cache = caches[i];
 30487  
 30488        if (cache.context === context) {
 30489          needToCreateCache = false;
 30490          break;
 30491        }
 30492      }
 30493  
 30494      if (needToCreateCache) {
 30495        cache = {
 30496          context: context
 30497        };
 30498        caches.push(cache);
 30499      }
 30500  
 30501      return cache;
 30502    };
 30503  
 30504    CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
 30505      var gradientStyle;
 30506      var usePaths = this.usePaths();
 30507      var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
 30508          positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
 30509  
 30510      if (fill === 'radial-gradient') {
 30511        if (ele.isEdge()) {
 30512          var start = ele.sourceEndpoint(),
 30513              end = ele.targetEndpoint(),
 30514              mid = ele.midpoint();
 30515          var d1 = dist(start, mid);
 30516          var d2 = dist(end, mid);
 30517          gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
 30518        } else {
 30519          var pos = usePaths ? {
 30520            x: 0,
 30521            y: 0
 30522          } : ele.position(),
 30523              width = ele.paddedWidth(),
 30524              height = ele.paddedHeight();
 30525          gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
 30526        }
 30527      } else {
 30528        if (ele.isEdge()) {
 30529          var _start = ele.sourceEndpoint(),
 30530              _end = ele.targetEndpoint();
 30531  
 30532          gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
 30533        } else {
 30534          var _pos = usePaths ? {
 30535            x: 0,
 30536            y: 0
 30537          } : ele.position(),
 30538              _width = ele.paddedWidth(),
 30539              _height = ele.paddedHeight(),
 30540              halfWidth = _width / 2,
 30541              halfHeight = _height / 2;
 30542  
 30543          var direction = ele.pstyle('background-gradient-direction').value;
 30544  
 30545          switch (direction) {
 30546            case 'to-bottom':
 30547              gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
 30548              break;
 30549  
 30550            case 'to-top':
 30551              gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
 30552              break;
 30553  
 30554            case 'to-left':
 30555              gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
 30556              break;
 30557  
 30558            case 'to-right':
 30559              gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
 30560              break;
 30561  
 30562            case 'to-bottom-right':
 30563            case 'to-right-bottom':
 30564              gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
 30565              break;
 30566  
 30567            case 'to-top-right':
 30568            case 'to-right-top':
 30569              gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
 30570              break;
 30571  
 30572            case 'to-bottom-left':
 30573            case 'to-left-bottom':
 30574              gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
 30575              break;
 30576  
 30577            case 'to-top-left':
 30578            case 'to-left-top':
 30579              gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
 30580              break;
 30581          }
 30582        }
 30583      }
 30584  
 30585      if (!gradientStyle) return null; // invalid gradient style
 30586  
 30587      var hasPositions = positions.length === colors.length;
 30588      var length = colors.length;
 30589  
 30590      for (var i = 0; i < length; i++) {
 30591        gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
 30592      }
 30593  
 30594      return gradientStyle;
 30595    };
 30596  
 30597    CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
 30598      var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
 30599      if (!gradientStyle) return null; // error
 30600  
 30601      context.fillStyle = gradientStyle;
 30602    };
 30603  
 30604    CRp$6.colorFillStyle = function (context, r, g, b, a) {
 30605      context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
 30606      // var cache = this.paintCache(context);
 30607      // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
 30608      // if( cache.fillStyle !== fillStyle ){
 30609      //   context.fillStyle = cache.fillStyle = fillStyle;
 30610      // }
 30611    };
 30612  
 30613    CRp$6.eleFillStyle = function (context, ele, opacity) {
 30614      var backgroundFill = ele.pstyle('background-fill').value;
 30615  
 30616      if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
 30617        this.gradientFillStyle(context, ele, backgroundFill, opacity);
 30618      } else {
 30619        var backgroundColor = ele.pstyle('background-color').value;
 30620        this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
 30621      }
 30622    };
 30623  
 30624    CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
 30625      var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
 30626      if (!gradientStyle) return null; // error
 30627  
 30628      context.strokeStyle = gradientStyle;
 30629    };
 30630  
 30631    CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
 30632      context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
 30633      // var cache = this.paintCache(context);
 30634      // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
 30635      // if( cache.strokeStyle !== strokeStyle ){
 30636      //   context.strokeStyle = cache.strokeStyle = strokeStyle;
 30637      // }
 30638    };
 30639  
 30640    CRp$6.eleStrokeStyle = function (context, ele, opacity) {
 30641      var lineFill = ele.pstyle('line-fill').value;
 30642  
 30643      if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
 30644        this.gradientStrokeStyle(context, ele, lineFill, opacity);
 30645      } else {
 30646        var lineColor = ele.pstyle('line-color').value;
 30647        this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
 30648      }
 30649    }; // Resize canvas
 30650  
 30651  
 30652    CRp$6.matchCanvasSize = function (container) {
 30653      var r = this;
 30654      var data = r.data;
 30655      var bb = r.findContainerClientCoords();
 30656      var width = bb[2];
 30657      var height = bb[3];
 30658      var pixelRatio = r.getPixelRatio();
 30659      var mbPxRatio = r.motionBlurPxRatio;
 30660  
 30661      if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
 30662        pixelRatio = mbPxRatio;
 30663      }
 30664  
 30665      var canvasWidth = width * pixelRatio;
 30666      var canvasHeight = height * pixelRatio;
 30667      var canvas;
 30668  
 30669      if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
 30670        return; // save cycles if same
 30671      }
 30672  
 30673      r.fontCaches = null; // resizing resets the style
 30674  
 30675      var canvasContainer = data.canvasContainer;
 30676      canvasContainer.style.width = width + 'px';
 30677      canvasContainer.style.height = height + 'px';
 30678  
 30679      for (var i = 0; i < r.CANVAS_LAYERS; i++) {
 30680        canvas = data.canvases[i];
 30681        canvas.width = canvasWidth;
 30682        canvas.height = canvasHeight;
 30683        canvas.style.width = width + 'px';
 30684        canvas.style.height = height + 'px';
 30685      }
 30686  
 30687      for (var i = 0; i < r.BUFFER_COUNT; i++) {
 30688        canvas = data.bufferCanvases[i];
 30689        canvas.width = canvasWidth;
 30690        canvas.height = canvasHeight;
 30691        canvas.style.width = width + 'px';
 30692        canvas.style.height = height + 'px';
 30693      }
 30694  
 30695      r.textureMult = 1;
 30696  
 30697      if (pixelRatio <= 1) {
 30698        canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
 30699        r.textureMult = 2;
 30700        canvas.width = canvasWidth * r.textureMult;
 30701        canvas.height = canvasHeight * r.textureMult;
 30702      }
 30703  
 30704      r.canvasWidth = canvasWidth;
 30705      r.canvasHeight = canvasHeight;
 30706    };
 30707  
 30708    CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
 30709      this.render({
 30710        forcedContext: cxt,
 30711        forcedZoom: zoom,
 30712        forcedPan: pan,
 30713        drawAllLayers: true,
 30714        forcedPxRatio: pxRatio
 30715      });
 30716    };
 30717  
 30718    CRp$6.render = function (options) {
 30719      options = options || staticEmptyObject();
 30720      var forcedContext = options.forcedContext;
 30721      var drawAllLayers = options.drawAllLayers;
 30722      var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
 30723      var forcedZoom = options.forcedZoom;
 30724      var forcedPan = options.forcedPan;
 30725      var r = this;
 30726      var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
 30727      var cy = r.cy;
 30728      var data = r.data;
 30729      var needDraw = data.canvasNeedsRedraw;
 30730      var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
 30731      var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
 30732      var mbPxRatio = r.motionBlurPxRatio;
 30733      var hasCompoundNodes = cy.hasCompoundNodes();
 30734      var inNodeDragGesture = r.hoverData.draggingEles;
 30735      var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
 30736      motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
 30737      var motionBlurFadeEffect = motionBlur;
 30738  
 30739      if (!forcedContext) {
 30740        if (r.prevPxRatio !== pixelRatio) {
 30741          r.invalidateContainerClientCoordsCache();
 30742          r.matchCanvasSize(r.container);
 30743          r.redrawHint('eles', true);
 30744          r.redrawHint('drag', true);
 30745        }
 30746  
 30747        r.prevPxRatio = pixelRatio;
 30748      }
 30749  
 30750      if (!forcedContext && r.motionBlurTimeout) {
 30751        clearTimeout(r.motionBlurTimeout);
 30752      }
 30753  
 30754      if (motionBlur) {
 30755        if (r.mbFrames == null) {
 30756          r.mbFrames = 0;
 30757        }
 30758  
 30759        r.mbFrames++;
 30760  
 30761        if (r.mbFrames < 3) {
 30762          // need several frames before even high quality motionblur
 30763          motionBlurFadeEffect = false;
 30764        } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
 30765  
 30766  
 30767        if (r.mbFrames > r.minMbLowQualFrames) {
 30768          //r.fullQualityMb = false;
 30769          r.motionBlurPxRatio = r.mbPxRBlurry;
 30770        }
 30771      }
 30772  
 30773      if (r.clearingMotionBlur) {
 30774        r.motionBlurPxRatio = 1;
 30775      } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
 30776      // because a rogue async texture frame would clear needDraw
 30777  
 30778  
 30779      if (r.textureDrawLastFrame && !textureDraw) {
 30780        needDraw[r.NODE] = true;
 30781        needDraw[r.SELECT_BOX] = true;
 30782      }
 30783  
 30784      var style = cy.style();
 30785      var zoom = cy.zoom();
 30786      var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
 30787      var pan = cy.pan();
 30788      var effectivePan = {
 30789        x: pan.x,
 30790        y: pan.y
 30791      };
 30792      var vp = {
 30793        zoom: zoom,
 30794        pan: {
 30795          x: pan.x,
 30796          y: pan.y
 30797        }
 30798      };
 30799      var prevVp = r.prevViewport;
 30800      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)
 30801  
 30802      if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
 30803        r.motionBlurPxRatio = 1;
 30804      }
 30805  
 30806      if (forcedPan) {
 30807        effectivePan = forcedPan;
 30808      } // apply pixel ratio
 30809  
 30810  
 30811      effectiveZoom *= pixelRatio;
 30812      effectivePan.x *= pixelRatio;
 30813      effectivePan.y *= pixelRatio;
 30814      var eles = r.getCachedZSortedEles();
 30815  
 30816      function mbclear(context, x, y, w, h) {
 30817        var gco = context.globalCompositeOperation;
 30818        context.globalCompositeOperation = 'destination-out';
 30819        r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
 30820        context.fillRect(x, y, w, h);
 30821        context.globalCompositeOperation = gco;
 30822      }
 30823  
 30824      function setContextTransform(context, clear) {
 30825        var ePan, eZoom, w, h;
 30826  
 30827        if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
 30828          ePan = {
 30829            x: pan.x * mbPxRatio,
 30830            y: pan.y * mbPxRatio
 30831          };
 30832          eZoom = zoom * mbPxRatio;
 30833          w = r.canvasWidth * mbPxRatio;
 30834          h = r.canvasHeight * mbPxRatio;
 30835        } else {
 30836          ePan = effectivePan;
 30837          eZoom = effectiveZoom;
 30838          w = r.canvasWidth;
 30839          h = r.canvasHeight;
 30840        }
 30841  
 30842        context.setTransform(1, 0, 0, 1, 0, 0);
 30843  
 30844        if (clear === 'motionBlur') {
 30845          mbclear(context, 0, 0, w, h);
 30846        } else if (!forcedContext && (clear === undefined || clear)) {
 30847          context.clearRect(0, 0, w, h);
 30848        }
 30849  
 30850        if (!drawAllLayers) {
 30851          context.translate(ePan.x, ePan.y);
 30852          context.scale(eZoom, eZoom);
 30853        }
 30854  
 30855        if (forcedPan) {
 30856          context.translate(forcedPan.x, forcedPan.y);
 30857        }
 30858  
 30859        if (forcedZoom) {
 30860          context.scale(forcedZoom, forcedZoom);
 30861        }
 30862      }
 30863  
 30864      if (!textureDraw) {
 30865        r.textureDrawLastFrame = false;
 30866      }
 30867  
 30868      if (textureDraw) {
 30869        r.textureDrawLastFrame = true;
 30870  
 30871        if (!r.textureCache) {
 30872          r.textureCache = {};
 30873          r.textureCache.bb = cy.mutableElements().boundingBox();
 30874          r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
 30875          var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
 30876          cxt.setTransform(1, 0, 0, 1, 0, 0);
 30877          cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
 30878          r.render({
 30879            forcedContext: cxt,
 30880            drawOnlyNodeLayer: true,
 30881            forcedPxRatio: pixelRatio * r.textureMult
 30882          });
 30883          var vp = r.textureCache.viewport = {
 30884            zoom: cy.zoom(),
 30885            pan: cy.pan(),
 30886            width: r.canvasWidth,
 30887            height: r.canvasHeight
 30888          };
 30889          vp.mpan = {
 30890            x: (0 - vp.pan.x) / vp.zoom,
 30891            y: (0 - vp.pan.y) / vp.zoom
 30892          };
 30893        }
 30894  
 30895        needDraw[r.DRAG] = false;
 30896        needDraw[r.NODE] = false;
 30897        var context = data.contexts[r.NODE];
 30898        var texture = r.textureCache.texture;
 30899        var vp = r.textureCache.viewport;
 30900        context.setTransform(1, 0, 0, 1, 0, 0);
 30901  
 30902        if (motionBlur) {
 30903          mbclear(context, 0, 0, vp.width, vp.height);
 30904        } else {
 30905          context.clearRect(0, 0, vp.width, vp.height);
 30906        }
 30907  
 30908        var outsideBgColor = style.core('outside-texture-bg-color').value;
 30909        var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
 30910        r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
 30911        context.fillRect(0, 0, vp.width, vp.height);
 30912        var zoom = cy.zoom();
 30913        setContextTransform(context, false);
 30914        context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
 30915        context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
 30916      } else if (r.textureOnViewport && !forcedContext) {
 30917        // clear the cache since we don't need it
 30918        r.textureCache = null;
 30919      }
 30920  
 30921      var extent = cy.extent();
 30922      var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
 30923      var hideEdges = r.hideEdgesOnViewport && vpManip;
 30924      var needMbClear = [];
 30925      needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
 30926  
 30927      if (needMbClear[r.NODE]) {
 30928        r.clearedForMotionBlur[r.NODE] = true;
 30929      }
 30930  
 30931      needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
 30932  
 30933      if (needMbClear[r.DRAG]) {
 30934        r.clearedForMotionBlur[r.DRAG] = true;
 30935      }
 30936  
 30937      if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
 30938        var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
 30939        var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
 30940        var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
 30941        setContextTransform(context, clear);
 30942  
 30943        if (hideEdges) {
 30944          r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
 30945        } else {
 30946          r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
 30947        }
 30948  
 30949        if (r.debug) {
 30950          r.drawDebugPoints(context, eles.nondrag);
 30951        }
 30952  
 30953        if (!drawAllLayers && !motionBlur) {
 30954          needDraw[r.NODE] = false;
 30955        }
 30956      }
 30957  
 30958      if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
 30959        var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
 30960        var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
 30961        setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
 30962  
 30963        if (hideEdges) {
 30964          r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
 30965        } else {
 30966          r.drawCachedElements(context, eles.drag, pixelRatio, extent);
 30967        }
 30968  
 30969        if (r.debug) {
 30970          r.drawDebugPoints(context, eles.drag);
 30971        }
 30972  
 30973        if (!drawAllLayers && !motionBlur) {
 30974          needDraw[r.DRAG] = false;
 30975        }
 30976      }
 30977  
 30978      if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
 30979        var context = forcedContext || data.contexts[r.SELECT_BOX];
 30980        setContextTransform(context);
 30981  
 30982        if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
 30983          var zoom = r.cy.zoom();
 30984          var borderWidth = style.core('selection-box-border-width').value / zoom;
 30985          context.lineWidth = borderWidth;
 30986          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 + ')';
 30987          context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
 30988  
 30989          if (borderWidth > 0) {
 30990            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 + ')';
 30991            context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
 30992          }
 30993        }
 30994  
 30995        if (data.bgActivePosistion && !r.hoverData.selecting) {
 30996          var zoom = r.cy.zoom();
 30997          var pos = data.bgActivePosistion;
 30998          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 + ')';
 30999          context.beginPath();
 31000          context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
 31001          context.fill();
 31002        }
 31003  
 31004        var timeToRender = r.lastRedrawTime;
 31005  
 31006        if (r.showFps && timeToRender) {
 31007          timeToRender = Math.round(timeToRender);
 31008          var fps = Math.round(1000 / timeToRender);
 31009          context.setTransform(1, 0, 0, 1, 0, 0);
 31010          context.fillStyle = 'rgba(255, 0, 0, 0.75)';
 31011          context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
 31012          context.lineWidth = 1;
 31013          context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
 31014          var maxFps = 60;
 31015          context.strokeRect(0, 30, 250, 20);
 31016          context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
 31017        }
 31018  
 31019        if (!drawAllLayers) {
 31020          needDraw[r.SELECT_BOX] = false;
 31021        }
 31022      } // motionblur: blit rendered blurry frames
 31023  
 31024  
 31025      if (motionBlur && mbPxRatio !== 1) {
 31026        var cxtNode = data.contexts[r.NODE];
 31027        var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
 31028        var cxtDrag = data.contexts[r.DRAG];
 31029        var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
 31030  
 31031        var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
 31032          cxt.setTransform(1, 0, 0, 1, 0, 0);
 31033  
 31034          if (needClear || !motionBlurFadeEffect) {
 31035            cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
 31036          } else {
 31037            mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
 31038          }
 31039  
 31040          var pxr = mbPxRatio;
 31041          cxt.drawImage(txt, // img
 31042          0, 0, // sx, sy
 31043          r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
 31044          0, 0, // x, y
 31045          r.canvasWidth, r.canvasHeight // w, h
 31046          );
 31047        };
 31048  
 31049        if (needDraw[r.NODE] || needMbClear[r.NODE]) {
 31050          drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
 31051          needDraw[r.NODE] = false;
 31052        }
 31053  
 31054        if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
 31055          drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
 31056          needDraw[r.DRAG] = false;
 31057        }
 31058      }
 31059  
 31060      r.prevViewport = vp;
 31061  
 31062      if (r.clearingMotionBlur) {
 31063        r.clearingMotionBlur = false;
 31064        r.motionBlurCleared = true;
 31065        r.motionBlur = true;
 31066      }
 31067  
 31068      if (motionBlur) {
 31069        r.motionBlurTimeout = setTimeout(function () {
 31070          r.motionBlurTimeout = null;
 31071          r.clearedForMotionBlur[r.NODE] = false;
 31072          r.clearedForMotionBlur[r.DRAG] = false;
 31073          r.motionBlur = false;
 31074          r.clearingMotionBlur = !textureDraw;
 31075          r.mbFrames = 0;
 31076          needDraw[r.NODE] = true;
 31077          needDraw[r.DRAG] = true;
 31078          r.redraw();
 31079        }, motionBlurDelay);
 31080      }
 31081  
 31082      if (!forcedContext) {
 31083        cy.emit('render');
 31084      }
 31085    };
 31086  
 31087    var CRp$7 = {}; // @O Polygon drawing
 31088  
 31089    CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
 31090      var halfW = width / 2;
 31091      var halfH = height / 2;
 31092  
 31093      if (context.beginPath) {
 31094        context.beginPath();
 31095      }
 31096  
 31097      context.moveTo(x + halfW * points[0], y + halfH * points[1]);
 31098  
 31099      for (var i = 1; i < points.length / 2; i++) {
 31100        context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
 31101      }
 31102  
 31103      context.closePath();
 31104    };
 31105  
 31106    CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
 31107      var halfW = width / 2;
 31108      var halfH = height / 2;
 31109      var cornerRadius = getRoundPolygonRadius(width, height);
 31110  
 31111      if (context.beginPath) {
 31112        context.beginPath();
 31113      }
 31114  
 31115      for (var _i = 0; _i < points.length / 4; _i++) {
 31116        var sourceUv = void 0,
 31117            destUv = void 0;
 31118  
 31119        if (_i === 0) {
 31120          sourceUv = points.length - 2;
 31121        } else {
 31122          sourceUv = _i * 4 - 2;
 31123        }
 31124  
 31125        destUv = _i * 4 + 2;
 31126        var px = x + halfW * points[_i * 4];
 31127        var py = y + halfH * points[_i * 4 + 1];
 31128        var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
 31129        var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
 31130        var cp0x = px - offset * points[sourceUv];
 31131        var cp0y = py - offset * points[sourceUv + 1];
 31132        var cp1x = px + offset * points[destUv];
 31133        var cp1y = py + offset * points[destUv + 1];
 31134  
 31135        if (_i === 0) {
 31136          context.moveTo(cp0x, cp0y);
 31137        } else {
 31138          context.lineTo(cp0x, cp0y);
 31139        }
 31140  
 31141        context.arcTo(px, py, cp1x, cp1y, cornerRadius);
 31142      }
 31143  
 31144      context.closePath();
 31145    }; // Round rectangle drawing
 31146  
 31147  
 31148    CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
 31149      var halfWidth = width / 2;
 31150      var halfHeight = height / 2;
 31151      var cornerRadius = getRoundRectangleRadius(width, height);
 31152  
 31153      if (context.beginPath) {
 31154        context.beginPath();
 31155      } // Start at top middle
 31156  
 31157  
 31158      context.moveTo(x, y - halfHeight); // Arc from middle top to right side
 31159  
 31160      context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
 31161  
 31162      context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
 31163  
 31164      context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
 31165  
 31166      context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
 31167  
 31168      context.lineTo(x, y - halfHeight);
 31169      context.closePath();
 31170    };
 31171  
 31172    CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
 31173      var halfWidth = width / 2;
 31174      var halfHeight = height / 2;
 31175      var cornerRadius = getRoundRectangleRadius(width, height);
 31176  
 31177      if (context.beginPath) {
 31178        context.beginPath();
 31179      } // Start at top middle
 31180  
 31181  
 31182      context.moveTo(x, y - halfHeight);
 31183      context.lineTo(x + halfWidth, y - halfHeight);
 31184      context.lineTo(x + halfWidth, y);
 31185      context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
 31186      context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
 31187      context.lineTo(x - halfWidth, y - halfHeight);
 31188      context.lineTo(x, y - halfHeight);
 31189      context.closePath();
 31190    };
 31191  
 31192    CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
 31193      var halfWidth = width / 2;
 31194      var halfHeight = height / 2;
 31195      var cornerLength = getCutRectangleCornerLength();
 31196  
 31197      if (context.beginPath) {
 31198        context.beginPath();
 31199      }
 31200  
 31201      context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
 31202      context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
 31203      context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
 31204      context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
 31205      context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
 31206      context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
 31207      context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
 31208      context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
 31209      context.closePath();
 31210    };
 31211  
 31212    CRp$7.drawBarrelPath = function (context, x, y, width, height) {
 31213      var halfWidth = width / 2;
 31214      var halfHeight = height / 2;
 31215      var xBegin = x - halfWidth;
 31216      var xEnd = x + halfWidth;
 31217      var yBegin = y - halfHeight;
 31218      var yEnd = y + halfHeight;
 31219      var barrelCurveConstants = getBarrelCurveConstants(width, height);
 31220      var wOffset = barrelCurveConstants.widthOffset;
 31221      var hOffset = barrelCurveConstants.heightOffset;
 31222      var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
 31223  
 31224      if (context.beginPath) {
 31225        context.beginPath();
 31226      }
 31227  
 31228      context.moveTo(xBegin, yBegin + hOffset);
 31229      context.lineTo(xBegin, yEnd - hOffset);
 31230      context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
 31231      context.lineTo(xEnd - wOffset, yEnd);
 31232      context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
 31233      context.lineTo(xEnd, yBegin + hOffset);
 31234      context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
 31235      context.lineTo(xBegin + wOffset, yBegin);
 31236      context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
 31237      context.closePath();
 31238    };
 31239  
 31240    var sin0 = Math.sin(0);
 31241    var cos0 = Math.cos(0);
 31242    var sin = {};
 31243    var cos = {};
 31244    var ellipseStepSize = Math.PI / 40;
 31245  
 31246    for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
 31247      sin[i] = Math.sin(i);
 31248      cos[i] = Math.cos(i);
 31249    }
 31250  
 31251    CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
 31252      if (context.beginPath) {
 31253        context.beginPath();
 31254      }
 31255  
 31256      if (context.ellipse) {
 31257        context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
 31258      } else {
 31259        var xPos, yPos;
 31260        var rw = width / 2;
 31261        var rh = height / 2;
 31262  
 31263        for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
 31264          xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
 31265          yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
 31266  
 31267          if (i === 0) {
 31268            context.moveTo(xPos, yPos);
 31269          } else {
 31270            context.lineTo(xPos, yPos);
 31271          }
 31272        }
 31273      }
 31274  
 31275      context.closePath();
 31276    };
 31277  
 31278    /* global atob, ArrayBuffer, Uint8Array, Blob */
 31279    var CRp$8 = {};
 31280  
 31281    CRp$8.createBuffer = function (w, h) {
 31282      var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
 31283  
 31284      buffer.width = w;
 31285      buffer.height = h;
 31286      return [buffer, buffer.getContext('2d')];
 31287    };
 31288  
 31289    CRp$8.bufferCanvasImage = function (options) {
 31290      var cy = this.cy;
 31291      var eles = cy.mutableElements();
 31292      var bb = eles.boundingBox();
 31293      var ctrRect = this.findContainerClientCoords();
 31294      var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
 31295      var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
 31296      var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
 31297      var pxRatio = this.getPixelRatio();
 31298      var scale = 1;
 31299  
 31300      if (options.scale !== undefined) {
 31301        width *= options.scale;
 31302        height *= options.scale;
 31303        scale = options.scale;
 31304      } else if (specdMaxDims) {
 31305        var maxScaleW = Infinity;
 31306        var maxScaleH = Infinity;
 31307  
 31308        if (number(options.maxWidth)) {
 31309          maxScaleW = scale * options.maxWidth / width;
 31310        }
 31311  
 31312        if (number(options.maxHeight)) {
 31313          maxScaleH = scale * options.maxHeight / height;
 31314        }
 31315  
 31316        scale = Math.min(maxScaleW, maxScaleH);
 31317        width *= scale;
 31318        height *= scale;
 31319      }
 31320  
 31321      if (!specdMaxDims) {
 31322        width *= pxRatio;
 31323        height *= pxRatio;
 31324        scale *= pxRatio;
 31325      }
 31326  
 31327      var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
 31328  
 31329      buffCanvas.width = width;
 31330      buffCanvas.height = height;
 31331      buffCanvas.style.width = width + 'px';
 31332      buffCanvas.style.height = height + 'px';
 31333      var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
 31334  
 31335      if (width > 0 && height > 0) {
 31336        buffCxt.clearRect(0, 0, width, height);
 31337        buffCxt.globalCompositeOperation = 'source-over';
 31338        var zsortedEles = this.getCachedZSortedEles();
 31339  
 31340        if (options.full) {
 31341          // draw the full bounds of the graph
 31342          buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
 31343          buffCxt.scale(scale, scale);
 31344          this.drawElements(buffCxt, zsortedEles);
 31345          buffCxt.scale(1 / scale, 1 / scale);
 31346          buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
 31347        } else {
 31348          // draw the current view
 31349          var pan = cy.pan();
 31350          var translation = {
 31351            x: pan.x * scale,
 31352            y: pan.y * scale
 31353          };
 31354          scale *= cy.zoom();
 31355          buffCxt.translate(translation.x, translation.y);
 31356          buffCxt.scale(scale, scale);
 31357          this.drawElements(buffCxt, zsortedEles);
 31358          buffCxt.scale(1 / scale, 1 / scale);
 31359          buffCxt.translate(-translation.x, -translation.y);
 31360        } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
 31361  
 31362  
 31363        if (options.bg) {
 31364          buffCxt.globalCompositeOperation = 'destination-over';
 31365          buffCxt.fillStyle = options.bg;
 31366          buffCxt.rect(0, 0, width, height);
 31367          buffCxt.fill();
 31368        }
 31369      }
 31370  
 31371      return buffCanvas;
 31372    };
 31373  
 31374    function b64ToBlob(b64, mimeType) {
 31375      var bytes = atob(b64);
 31376      var buff = new ArrayBuffer(bytes.length);
 31377      var buffUint8 = new Uint8Array(buff);
 31378  
 31379      for (var i = 0; i < bytes.length; i++) {
 31380        buffUint8[i] = bytes.charCodeAt(i);
 31381      }
 31382  
 31383      return new Blob([buff], {
 31384        type: mimeType
 31385      });
 31386    }
 31387  
 31388    function b64UriToB64(b64uri) {
 31389      var i = b64uri.indexOf(',');
 31390      return b64uri.substr(i + 1);
 31391    }
 31392  
 31393    function output(options, canvas, mimeType) {
 31394      var getB64Uri = function getB64Uri() {
 31395        return canvas.toDataURL(mimeType, options.quality);
 31396      };
 31397  
 31398      switch (options.output) {
 31399        case 'blob-promise':
 31400          return new Promise$1(function (resolve, reject) {
 31401            try {
 31402              canvas.toBlob(function (blob) {
 31403                if (blob != null) {
 31404                  resolve(blob);
 31405                } else {
 31406                  reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
 31407                }
 31408              }, mimeType, options.quality);
 31409            } catch (err) {
 31410              reject(err);
 31411            }
 31412          });
 31413  
 31414        case 'blob':
 31415          return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
 31416  
 31417        case 'base64':
 31418          return b64UriToB64(getB64Uri());
 31419  
 31420        case 'base64uri':
 31421        default:
 31422          return getB64Uri();
 31423      }
 31424    }
 31425  
 31426    CRp$8.png = function (options) {
 31427      return output(options, this.bufferCanvasImage(options), 'image/png');
 31428    };
 31429  
 31430    CRp$8.jpg = function (options) {
 31431      return output(options, this.bufferCanvasImage(options), 'image/jpeg');
 31432    };
 31433  
 31434    var CRp$9 = {};
 31435  
 31436    CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
 31437      switch (name) {
 31438        case 'ellipse':
 31439          return this.drawEllipsePath(context, centerX, centerY, width, height);
 31440  
 31441        case 'polygon':
 31442          return this.drawPolygonPath(context, centerX, centerY, width, height, points);
 31443  
 31444        case 'round-polygon':
 31445          return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
 31446  
 31447        case 'roundrectangle':
 31448        case 'round-rectangle':
 31449          return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
 31450  
 31451        case 'cutrectangle':
 31452        case 'cut-rectangle':
 31453          return this.drawCutRectanglePath(context, centerX, centerY, width, height);
 31454  
 31455        case 'bottomroundrectangle':
 31456        case 'bottom-round-rectangle':
 31457          return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
 31458  
 31459        case 'barrel':
 31460          return this.drawBarrelPath(context, centerX, centerY, width, height);
 31461      }
 31462    };
 31463  
 31464    var CR = CanvasRenderer;
 31465    var CRp$a = CanvasRenderer.prototype;
 31466    CRp$a.CANVAS_LAYERS = 3; //
 31467  
 31468    CRp$a.SELECT_BOX = 0;
 31469    CRp$a.DRAG = 1;
 31470    CRp$a.NODE = 2;
 31471    CRp$a.BUFFER_COUNT = 3; //
 31472  
 31473    CRp$a.TEXTURE_BUFFER = 0;
 31474    CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
 31475    CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
 31476  
 31477    function CanvasRenderer(options) {
 31478      var r = this;
 31479      r.data = {
 31480        canvases: new Array(CRp$a.CANVAS_LAYERS),
 31481        contexts: new Array(CRp$a.CANVAS_LAYERS),
 31482        canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
 31483        bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
 31484        bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
 31485      };
 31486      var tapHlOffAttr = '-webkit-tap-highlight-color';
 31487      var tapHlOffStyle = 'rgba(0,0,0,0)';
 31488      r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
 31489  
 31490      var containerStyle = r.data.canvasContainer.style;
 31491      r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
 31492      containerStyle.position = 'relative';
 31493      containerStyle.zIndex = '0';
 31494      containerStyle.overflow = 'hidden';
 31495      var container = options.cy.container();
 31496      container.appendChild(r.data.canvasContainer);
 31497      container.style[tapHlOffAttr] = tapHlOffStyle;
 31498      var styleMap = {
 31499        '-webkit-user-select': 'none',
 31500        '-moz-user-select': '-moz-none',
 31501        'user-select': 'none',
 31502        '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
 31503        'outline-style': 'none'
 31504      };
 31505  
 31506      if (ms()) {
 31507        styleMap['-ms-touch-action'] = 'none';
 31508        styleMap['touch-action'] = 'none';
 31509      }
 31510  
 31511      for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
 31512        var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
 31513  
 31514        r.data.contexts[i] = canvas.getContext('2d');
 31515        Object.keys(styleMap).forEach(function (k) {
 31516          canvas.style[k] = styleMap[k];
 31517        });
 31518        canvas.style.position = 'absolute';
 31519        canvas.setAttribute('data-id', 'layer' + i);
 31520        canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
 31521        r.data.canvasContainer.appendChild(canvas);
 31522        r.data.canvasNeedsRedraw[i] = false;
 31523      }
 31524  
 31525      r.data.topCanvas = r.data.canvases[0];
 31526      r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
 31527      r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
 31528      r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
 31529  
 31530      for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
 31531        r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
 31532  
 31533        r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
 31534        r.data.bufferCanvases[i].style.position = 'absolute';
 31535        r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
 31536        r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
 31537        r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
 31538      }
 31539  
 31540      r.pathsEnabled = true;
 31541      var emptyBb = makeBoundingBox();
 31542  
 31543      var getBoxCenter = function getBoxCenter(bb) {
 31544        return {
 31545          x: (bb.x1 + bb.x2) / 2,
 31546          y: (bb.y1 + bb.y2) / 2
 31547        };
 31548      };
 31549  
 31550      var getCenterOffset = function getCenterOffset(bb) {
 31551        return {
 31552          x: -bb.w / 2,
 31553          y: -bb.h / 2
 31554        };
 31555      };
 31556  
 31557      var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
 31558        var _p = ele[0]._private;
 31559        var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
 31560        return !same;
 31561      };
 31562  
 31563      var getStyleKey = function getStyleKey(ele) {
 31564        return ele[0]._private.nodeKey;
 31565      };
 31566  
 31567      var getLabelKey = function getLabelKey(ele) {
 31568        return ele[0]._private.labelStyleKey;
 31569      };
 31570  
 31571      var getSourceLabelKey = function getSourceLabelKey(ele) {
 31572        return ele[0]._private.sourceLabelStyleKey;
 31573      };
 31574  
 31575      var getTargetLabelKey = function getTargetLabelKey(ele) {
 31576        return ele[0]._private.targetLabelStyleKey;
 31577      };
 31578  
 31579      var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
 31580        return r.drawElement(context, ele, bb, false, false, useEleOpacity);
 31581      };
 31582  
 31583      var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
 31584        return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
 31585      };
 31586  
 31587      var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
 31588        return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
 31589      };
 31590  
 31591      var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
 31592        return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
 31593      };
 31594  
 31595      var getElementBox = function getElementBox(ele) {
 31596        ele.boundingBox();
 31597        return ele[0]._private.bodyBounds;
 31598      };
 31599  
 31600      var getLabelBox = function getLabelBox(ele) {
 31601        ele.boundingBox();
 31602        return ele[0]._private.labelBounds.main || emptyBb;
 31603      };
 31604  
 31605      var getSourceLabelBox = function getSourceLabelBox(ele) {
 31606        ele.boundingBox();
 31607        return ele[0]._private.labelBounds.source || emptyBb;
 31608      };
 31609  
 31610      var getTargetLabelBox = function getTargetLabelBox(ele) {
 31611        ele.boundingBox();
 31612        return ele[0]._private.labelBounds.target || emptyBb;
 31613      };
 31614  
 31615      var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
 31616        return scaledLabelShown;
 31617      };
 31618  
 31619      var getElementRotationPoint = function getElementRotationPoint(ele) {
 31620        return getBoxCenter(getElementBox(ele));
 31621      };
 31622  
 31623      var addTextMargin = function addTextMargin(prefix, pt, ele) {
 31624        var pre = prefix ? prefix + '-' : '';
 31625        return {
 31626          x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
 31627          y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
 31628        };
 31629      };
 31630  
 31631      var getRsPt = function getRsPt(ele, x, y) {
 31632        var rs = ele[0]._private.rscratch;
 31633        return {
 31634          x: rs[x],
 31635          y: rs[y]
 31636        };
 31637      };
 31638  
 31639      var getLabelRotationPoint = function getLabelRotationPoint(ele) {
 31640        return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
 31641      };
 31642  
 31643      var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
 31644        return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
 31645      };
 31646  
 31647      var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
 31648        return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
 31649      };
 31650  
 31651      var getElementRotationOffset = function getElementRotationOffset(ele) {
 31652        return getCenterOffset(getElementBox(ele));
 31653      };
 31654  
 31655      var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
 31656        return getCenterOffset(getSourceLabelBox(ele));
 31657      };
 31658  
 31659      var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
 31660        return getCenterOffset(getTargetLabelBox(ele));
 31661      };
 31662  
 31663      var getLabelRotationOffset = function getLabelRotationOffset(ele) {
 31664        var bb = getLabelBox(ele);
 31665        var p = getCenterOffset(getLabelBox(ele));
 31666  
 31667        if (ele.isNode()) {
 31668          switch (ele.pstyle('text-halign').value) {
 31669            case 'left':
 31670              p.x = -bb.w;
 31671              break;
 31672  
 31673            case 'right':
 31674              p.x = 0;
 31675              break;
 31676          }
 31677  
 31678          switch (ele.pstyle('text-valign').value) {
 31679            case 'top':
 31680              p.y = -bb.h;
 31681              break;
 31682  
 31683            case 'bottom':
 31684              p.y = 0;
 31685              break;
 31686          }
 31687        }
 31688  
 31689        return p;
 31690      };
 31691  
 31692      var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
 31693        getKey: getStyleKey,
 31694        doesEleInvalidateKey: backgroundTimestampHasChanged,
 31695        drawElement: drawElement,
 31696        getBoundingBox: getElementBox,
 31697        getRotationPoint: getElementRotationPoint,
 31698        getRotationOffset: getElementRotationOffset,
 31699        allowEdgeTxrCaching: false,
 31700        allowParentTxrCaching: false
 31701      });
 31702      var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
 31703        getKey: getLabelKey,
 31704        drawElement: drawLabel,
 31705        getBoundingBox: getLabelBox,
 31706        getRotationPoint: getLabelRotationPoint,
 31707        getRotationOffset: getLabelRotationOffset,
 31708        isVisible: isLabelVisibleAtScale
 31709      });
 31710      var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
 31711        getKey: getSourceLabelKey,
 31712        drawElement: drawSourceLabel,
 31713        getBoundingBox: getSourceLabelBox,
 31714        getRotationPoint: getSourceLabelRotationPoint,
 31715        getRotationOffset: getSourceLabelRotationOffset,
 31716        isVisible: isLabelVisibleAtScale
 31717      });
 31718      var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
 31719        getKey: getTargetLabelKey,
 31720        drawElement: drawTargetLabel,
 31721        getBoundingBox: getTargetLabelBox,
 31722        getRotationPoint: getTargetLabelRotationPoint,
 31723        getRotationOffset: getTargetLabelRotationOffset,
 31724        isVisible: isLabelVisibleAtScale
 31725      });
 31726      var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
 31727      r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
 31728        // each cache should check for sub-key diff to see that the update affects that cache particularly
 31729        eleTxrCache.invalidateElements(eles);
 31730        lblTxrCache.invalidateElements(eles);
 31731        slbTxrCache.invalidateElements(eles);
 31732        tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
 31733  
 31734        lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
 31735  
 31736        for (var _i = 0; _i < eles.length; _i++) {
 31737          var _p = eles[_i]._private;
 31738          _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
 31739        }
 31740      });
 31741  
 31742      var refineInLayers = function refineInLayers(reqs) {
 31743        for (var i = 0; i < reqs.length; i++) {
 31744          lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
 31745        }
 31746      };
 31747  
 31748      eleTxrCache.onDequeue(refineInLayers);
 31749      lblTxrCache.onDequeue(refineInLayers);
 31750      slbTxrCache.onDequeue(refineInLayers);
 31751      tlbTxrCache.onDequeue(refineInLayers);
 31752    }
 31753  
 31754    CRp$a.redrawHint = function (group, bool) {
 31755      var r = this;
 31756  
 31757      switch (group) {
 31758        case 'eles':
 31759          r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
 31760          break;
 31761  
 31762        case 'drag':
 31763          r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
 31764          break;
 31765  
 31766        case 'select':
 31767          r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
 31768          break;
 31769      }
 31770    }; // whether to use Path2D caching for drawing
 31771  
 31772  
 31773    var pathsImpld = typeof Path2D !== 'undefined';
 31774  
 31775    CRp$a.path2dEnabled = function (on) {
 31776      if (on === undefined) {
 31777        return this.pathsEnabled;
 31778      }
 31779  
 31780      this.pathsEnabled = on ? true : false;
 31781    };
 31782  
 31783    CRp$a.usePaths = function () {
 31784      return pathsImpld && this.pathsEnabled;
 31785    };
 31786  
 31787    CRp$a.setImgSmoothing = function (context, bool) {
 31788      if (context.imageSmoothingEnabled != null) {
 31789        context.imageSmoothingEnabled = bool;
 31790      } else {
 31791        context.webkitImageSmoothingEnabled = bool;
 31792        context.mozImageSmoothingEnabled = bool;
 31793        context.msImageSmoothingEnabled = bool;
 31794      }
 31795    };
 31796  
 31797    CRp$a.getImgSmoothing = function (context) {
 31798      if (context.imageSmoothingEnabled != null) {
 31799        return context.imageSmoothingEnabled;
 31800      } else {
 31801        return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
 31802      }
 31803    };
 31804  
 31805    CRp$a.makeOffscreenCanvas = function (width, height) {
 31806      var canvas;
 31807  
 31808      if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
 31809        canvas = new OffscreenCanvas(width, height);
 31810      } else {
 31811        canvas = document.createElement('canvas'); // eslint-disable-line no-undef
 31812  
 31813        canvas.width = width;
 31814        canvas.height = height;
 31815      }
 31816  
 31817      return canvas;
 31818    };
 31819  
 31820    [CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
 31821      extend(CRp$a, props);
 31822    });
 31823  
 31824    var renderer = [{
 31825      name: 'null',
 31826      impl: NullRenderer
 31827    }, {
 31828      name: 'base',
 31829      impl: BR
 31830    }, {
 31831      name: 'canvas',
 31832      impl: CR
 31833    }];
 31834  
 31835    var incExts = [{
 31836      type: 'layout',
 31837      extensions: layout
 31838    }, {
 31839      type: 'renderer',
 31840      extensions: renderer
 31841    }];
 31842  
 31843    var extensions = {}; // registered modules for extensions, indexed by name
 31844  
 31845    var modules = {};
 31846  
 31847    function setExtension(type, name, registrant) {
 31848      var ext = registrant;
 31849  
 31850      var overrideErr = function overrideErr(field) {
 31851        error('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
 31852      };
 31853  
 31854      if (type === 'core') {
 31855        if (Core.prototype[name]) {
 31856          return overrideErr(name);
 31857        } else {
 31858          Core.prototype[name] = registrant;
 31859        }
 31860      } else if (type === 'collection') {
 31861        if (Collection.prototype[name]) {
 31862          return overrideErr(name);
 31863        } else {
 31864          Collection.prototype[name] = registrant;
 31865        }
 31866      } else if (type === 'layout') {
 31867        // fill in missing layout functions in the prototype
 31868        var Layout = function Layout(options) {
 31869          this.options = options;
 31870          registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
 31871  
 31872          if (!plainObject(this._private)) {
 31873            this._private = {};
 31874          }
 31875  
 31876          this._private.cy = options.cy;
 31877          this._private.listeners = [];
 31878          this.createEmitter();
 31879        };
 31880  
 31881        var layoutProto = Layout.prototype = Object.create(registrant.prototype);
 31882        var optLayoutFns = [];
 31883  
 31884        for (var i = 0; i < optLayoutFns.length; i++) {
 31885          var fnName = optLayoutFns[i];
 31886  
 31887          layoutProto[fnName] = layoutProto[fnName] || function () {
 31888            return this;
 31889          };
 31890        } // either .start() or .run() is defined, so autogen the other
 31891  
 31892  
 31893        if (layoutProto.start && !layoutProto.run) {
 31894          layoutProto.run = function () {
 31895            this.start();
 31896            return this;
 31897          };
 31898        } else if (!layoutProto.start && layoutProto.run) {
 31899          layoutProto.start = function () {
 31900            this.run();
 31901            return this;
 31902          };
 31903        }
 31904  
 31905        var regStop = registrant.prototype.stop;
 31906  
 31907        layoutProto.stop = function () {
 31908          var opts = this.options;
 31909  
 31910          if (opts && opts.animate) {
 31911            var anis = this.animations;
 31912  
 31913            if (anis) {
 31914              for (var _i = 0; _i < anis.length; _i++) {
 31915                anis[_i].stop();
 31916              }
 31917            }
 31918          }
 31919  
 31920          if (regStop) {
 31921            regStop.call(this);
 31922          } else {
 31923            this.emit('layoutstop');
 31924          }
 31925  
 31926          return this;
 31927        };
 31928  
 31929        if (!layoutProto.destroy) {
 31930          layoutProto.destroy = function () {
 31931            return this;
 31932          };
 31933        }
 31934  
 31935        layoutProto.cy = function () {
 31936          return this._private.cy;
 31937        };
 31938  
 31939        var getCy = function getCy(layout) {
 31940          return layout._private.cy;
 31941        };
 31942  
 31943        var emitterOpts = {
 31944          addEventFields: function addEventFields(layout, evt) {
 31945            evt.layout = layout;
 31946            evt.cy = getCy(layout);
 31947            evt.target = layout;
 31948          },
 31949          bubble: function bubble() {
 31950            return true;
 31951          },
 31952          parent: function parent(layout) {
 31953            return getCy(layout);
 31954          }
 31955        };
 31956        extend(layoutProto, {
 31957          createEmitter: function createEmitter() {
 31958            this._private.emitter = new Emitter(emitterOpts, this);
 31959            return this;
 31960          },
 31961          emitter: function emitter() {
 31962            return this._private.emitter;
 31963          },
 31964          on: function on(evt, cb) {
 31965            this.emitter().on(evt, cb);
 31966            return this;
 31967          },
 31968          one: function one(evt, cb) {
 31969            this.emitter().one(evt, cb);
 31970            return this;
 31971          },
 31972          once: function once(evt, cb) {
 31973            this.emitter().one(evt, cb);
 31974            return this;
 31975          },
 31976          removeListener: function removeListener(evt, cb) {
 31977            this.emitter().removeListener(evt, cb);
 31978            return this;
 31979          },
 31980          removeAllListeners: function removeAllListeners() {
 31981            this.emitter().removeAllListeners();
 31982            return this;
 31983          },
 31984          emit: function emit(evt, params) {
 31985            this.emitter().emit(evt, params);
 31986            return this;
 31987          }
 31988        });
 31989        define$3.eventAliasesOn(layoutProto);
 31990        ext = Layout; // replace with our wrapped layout
 31991      } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
 31992        // user registered renderers inherit from base
 31993        var BaseRenderer = getExtension('renderer', 'base');
 31994        var bProto = BaseRenderer.prototype;
 31995        var RegistrantRenderer = registrant;
 31996        var rProto = registrant.prototype;
 31997  
 31998        var Renderer = function Renderer() {
 31999          BaseRenderer.apply(this, arguments);
 32000          RegistrantRenderer.apply(this, arguments);
 32001        };
 32002  
 32003        var proto = Renderer.prototype;
 32004  
 32005        for (var pName in bProto) {
 32006          var pVal = bProto[pName];
 32007          var existsInR = rProto[pName] != null;
 32008  
 32009          if (existsInR) {
 32010            return overrideErr(pName);
 32011          }
 32012  
 32013          proto[pName] = pVal; // take impl from base
 32014        }
 32015  
 32016        for (var _pName in rProto) {
 32017          proto[_pName] = rProto[_pName]; // take impl from registrant
 32018        }
 32019  
 32020        bProto.clientFunctions.forEach(function (name) {
 32021          proto[name] = proto[name] || function () {
 32022            error('Renderer does not implement `renderer.' + name + '()` on its prototype');
 32023          };
 32024        });
 32025        ext = Renderer;
 32026      }
 32027  
 32028      return setMap({
 32029        map: extensions,
 32030        keys: [type, name],
 32031        value: ext
 32032      });
 32033    }
 32034  
 32035    function getExtension(type, name) {
 32036      return getMap({
 32037        map: extensions,
 32038        keys: [type, name]
 32039      });
 32040    }
 32041  
 32042    function setModule(type, name, moduleType, moduleName, registrant) {
 32043      return setMap({
 32044        map: modules,
 32045        keys: [type, name, moduleType, moduleName],
 32046        value: registrant
 32047      });
 32048    }
 32049  
 32050    function getModule(type, name, moduleType, moduleName) {
 32051      return getMap({
 32052        map: modules,
 32053        keys: [type, name, moduleType, moduleName]
 32054      });
 32055    }
 32056  
 32057    var extension = function extension() {
 32058      // e.g. extension('renderer', 'svg')
 32059      if (arguments.length === 2) {
 32060        return getExtension.apply(null, arguments);
 32061      } // e.g. extension('renderer', 'svg', { ... })
 32062      else if (arguments.length === 3) {
 32063          return setExtension.apply(null, arguments);
 32064        } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
 32065        else if (arguments.length === 4) {
 32066            return getModule.apply(null, arguments);
 32067          } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
 32068          else if (arguments.length === 5) {
 32069              return setModule.apply(null, arguments);
 32070            } else {
 32071              error('Invalid extension access syntax');
 32072            }
 32073    }; // allows a core instance to access extensions internally
 32074  
 32075  
 32076    Core.prototype.extension = extension; // included extensions
 32077  
 32078    incExts.forEach(function (group) {
 32079      group.extensions.forEach(function (ext) {
 32080        setExtension(group.type, ext.name, ext.impl);
 32081      });
 32082    });
 32083  
 32084    // (useful for init)
 32085  
 32086    var Stylesheet = function Stylesheet() {
 32087      if (!(this instanceof Stylesheet)) {
 32088        return new Stylesheet();
 32089      }
 32090  
 32091      this.length = 0;
 32092    };
 32093  
 32094    var sheetfn = Stylesheet.prototype;
 32095  
 32096    sheetfn.instanceString = function () {
 32097      return 'stylesheet';
 32098    }; // just store the selector to be parsed later
 32099  
 32100  
 32101    sheetfn.selector = function (selector) {
 32102      var i = this.length++;
 32103      this[i] = {
 32104        selector: selector,
 32105        properties: []
 32106      };
 32107      return this; // chaining
 32108    }; // just store the property to be parsed later
 32109  
 32110  
 32111    sheetfn.css = function (name, value) {
 32112      var i = this.length - 1;
 32113  
 32114      if (string(name)) {
 32115        this[i].properties.push({
 32116          name: name,
 32117          value: value
 32118        });
 32119      } else if (plainObject(name)) {
 32120        var map = name;
 32121        var propNames = Object.keys(map);
 32122  
 32123        for (var j = 0; j < propNames.length; j++) {
 32124          var key = propNames[j];
 32125          var mapVal = map[key];
 32126  
 32127          if (mapVal == null) {
 32128            continue;
 32129          }
 32130  
 32131          var prop = Style.properties[key] || Style.properties[dash2camel(key)];
 32132  
 32133          if (prop == null) {
 32134            continue;
 32135          }
 32136  
 32137          var _name = prop.name;
 32138          var _value = mapVal;
 32139          this[i].properties.push({
 32140            name: _name,
 32141            value: _value
 32142          });
 32143        }
 32144      }
 32145  
 32146      return this; // chaining
 32147    };
 32148  
 32149    sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
 32150  
 32151    sheetfn.generateStyle = function (cy) {
 32152      var style = new Style(cy);
 32153      return this.appendToStyle(style);
 32154    }; // append a dummy stylesheet object on a real style object
 32155  
 32156  
 32157    sheetfn.appendToStyle = function (style) {
 32158      for (var i = 0; i < this.length; i++) {
 32159        var context = this[i];
 32160        var selector = context.selector;
 32161        var props = context.properties;
 32162        style.selector(selector); // apply selector
 32163  
 32164        for (var j = 0; j < props.length; j++) {
 32165          var prop = props[j];
 32166          style.css(prop.name, prop.value); // apply property
 32167        }
 32168      }
 32169  
 32170      return style;
 32171    };
 32172  
 32173    var version = "3.13.3";
 32174  
 32175    var cytoscape = function cytoscape(options) {
 32176      // if no options specified, use default
 32177      if (options === undefined) {
 32178        options = {};
 32179      } // create instance
 32180  
 32181  
 32182      if (plainObject(options)) {
 32183        return new Core(options);
 32184      } // allow for registration of extensions
 32185      else if (string(options)) {
 32186          return extension.apply(extension, arguments);
 32187        }
 32188    }; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
 32189  
 32190  
 32191    cytoscape.use = function (ext) {
 32192      var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
 32193  
 32194      args.unshift(cytoscape); // cytoscape is first arg to ext
 32195  
 32196      ext.apply(null, args);
 32197      return this;
 32198    };
 32199  
 32200    cytoscape.warnings = function (bool) {
 32201      return warnings(bool);
 32202    }; // replaced by build system
 32203  
 32204  
 32205    cytoscape.version = version; // expose public apis (mostly for extensions)
 32206  
 32207    cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
 32208  
 32209    return cytoscape;
 32210  
 32211  })));