(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.igv = factory());
})(this, (function () { 'use strict';

    /*!
     * jQuery JavaScript Library v3.3.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/parseXML,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-event/ajax,-effects,-effects/Tween,-effects/animatedSelector
     * https://jquery.com/
     *
     * Includes Sizzle.js
     * https://sizzlejs.com/
     *
     * Copyright JS Foundation and other contributors
     * Released under the MIT license
     * https://jquery.org/license
     *
     * Date: 2018-01-20T17:24Z
     */

    var arr = [];
    var document$2 = window.document;
    var getProto = Object.getPrototypeOf;
    var slice = arr.slice;
    var concat$1 = arr.concat;
    var push$1 = arr.push;
    var indexOf$1 = arr.indexOf;
    var class2type = {};
    var toString$3 = class2type.toString;
    var hasOwn = class2type.hasOwnProperty;
    var fnToString = hasOwn.toString;
    var ObjectFunctionString = fnToString.call(Object);
    var support = {};

    var isFunction = function isFunction(obj) {
      // Support: Chrome <=57, Firefox <=52
      // In some browsers, typeof returns "function" for HTML <object> elements
      // (i.e., `typeof document.createElement( "object" ) === "function"`).
      // We don't want to classify *any* DOM node as a function.
      return typeof obj === "function" && typeof obj.nodeType !== "number";
    };

    var isWindow = function isWindow(obj) {
      return obj != null && obj === obj.window;
    };

    var preservedScriptAttributes = {
      type: true,
      src: true,
      noModule: true
    };

    function DOMEval(code, doc, node) {
      doc = doc || document$2;
      var i,
          script = doc.createElement("script");
      script.text = code;

      if (node) {
        for (i in preservedScriptAttributes) {
          if (node[i]) {
            script[i] = node[i];
          }
        }
      }

      doc.head.appendChild(script).parentNode.removeChild(script);
    }

    function toType(obj) {
      if (obj == null) {
        return obj + "";
      } // Support: Android <=2.3 only (functionish RegExp)


      return typeof obj === "object" || typeof obj === "function" ? class2type[toString$3.call(obj)] || "object" : typeof obj;
    } // global Symbol
    // Defining this global in .eslintrc.json would create a danger of using the global
    // unguarded in another place, it seems safer to define global only for this module


    var version$2 = "3.3.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/parseXML,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-event/ajax,-effects,-effects/Tween,-effects/animatedSelector",
        // Define a local copy of jQuery
    jQuery = function (selector, context) {
      // The jQuery object is actually just the init constructor 'enhanced'
      // Need init if jQuery is called (just allow error to be thrown if not included)
      return new jQuery.fn.init(selector, context);
    },
        // Support: Android <=4.0 only
    // Make sure we trim BOM and NBSP
    rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;

    jQuery.fn = jQuery.prototype = {
      // The current version of jQuery being used
      jquery: version$2,
      constructor: jQuery,
      // The default length of a jQuery object is 0
      length: 0,
      toArray: function () {
        return slice.call(this);
      },
      // Get the Nth element in the matched element set OR
      // Get the whole matched element set as a clean array
      get: function (num) {
        // Return all the elements in a clean array
        if (num == null) {
          return slice.call(this);
        } // Return just the one element from the set


        return num < 0 ? this[num + this.length] : this[num];
      },
      // Take an array of elements and push it onto the stack
      // (returning the new matched element set)
      pushStack: function (elems) {
        // Build a new jQuery matched element set
        var ret = jQuery.merge(this.constructor(), elems); // Add the old object onto the stack (as a reference)

        ret.prevObject = this; // Return the newly-formed element set

        return ret;
      },
      // Execute a callback for every element in the matched set.
      each: function (callback) {
        return jQuery.each(this, callback);
      },
      map: function (callback) {
        return this.pushStack(jQuery.map(this, function (elem, i) {
          return callback.call(elem, i, elem);
        }));
      },
      slice: function () {
        return this.pushStack(slice.apply(this, arguments));
      },
      first: function () {
        return this.eq(0);
      },
      last: function () {
        return this.eq(-1);
      },
      eq: function (i) {
        var len = this.length,
            j = +i + (i < 0 ? len : 0);
        return this.pushStack(j >= 0 && j < len ? [this[j]] : []);
      },
      end: function () {
        return this.prevObject || this.constructor();
      },
      // For internal use only.
      // Behaves like an Array's method, not like a jQuery method.
      push: push$1,
      sort: arr.sort,
      splice: arr.splice
    };

    jQuery.extend = jQuery.fn.extend = function () {
      var options,
          name,
          src,
          copy,
          copyIsArray,
          clone,
          target = arguments[0] || {},
          i = 1,
          length = arguments.length,
          deep = false; // Handle a deep copy situation

      if (typeof target === "boolean") {
        deep = target; // Skip the boolean and the target

        target = arguments[i] || {};
        i++;
      } // Handle case when target is a string or something (possible in deep copy)


      if (typeof target !== "object" && !isFunction(target)) {
        target = {};
      } // Extend jQuery itself if only one argument is passed


      if (i === length) {
        target = this;
        i--;
      }

      for (; i < length; i++) {
        // Only deal with non-null/undefined values
        if ((options = arguments[i]) != null) {
          // Extend the base object
          for (name in options) {
            src = target[name];
            copy = options[name]; // Prevent never-ending loop

            if (target === copy) {
              continue;
            } // Recurse if we're merging plain objects or arrays


            if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
              if (copyIsArray) {
                copyIsArray = false;
                clone = src && Array.isArray(src) ? src : [];
              } else {
                clone = src && jQuery.isPlainObject(src) ? src : {};
              } // Never move original objects, clone them


              target[name] = jQuery.extend(deep, clone, copy); // Don't bring in undefined values
            } else if (copy !== undefined) {
              target[name] = copy;
            }
          }
        }
      } // Return the modified object


      return target;
    };

    jQuery.extend({
      // Unique for each copy of jQuery on the page
      expando: "jQuery" + (version$2 + Math.random()).replace(/\D/g, ""),
      // Assume jQuery is ready without the ready module
      isReady: true,
      error: function (msg) {
        throw new Error(msg);
      },
      noop: function () {},
      isPlainObject: function (obj) {
        var proto, Ctor; // Detect obvious negatives
        // Use toString instead of jQuery.type to catch host objects

        if (!obj || toString$3.call(obj) !== "[object Object]") {
          return false;
        }

        proto = getProto(obj); // Objects with no prototype (e.g., `Object.create( null )`) are plain

        if (!proto) {
          return true;
        } // Objects with prototype are plain iff they were constructed by a global Object function


        Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
        return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
      },
      isEmptyObject: function (obj) {
        /* eslint-disable no-unused-vars */
        // See https://github.com/eslint/eslint/issues/6125
        var name;

        for (name in obj) {
          return false;
        }

        return true;
      },
      // Evaluates a script in a global context
      globalEval: function (code) {
        DOMEval(code);
      },
      each: function (obj, callback) {
        var length,
            i = 0;

        if (isArrayLike(obj)) {
          length = obj.length;

          for (; i < length; i++) {
            if (callback.call(obj[i], i, obj[i]) === false) {
              break;
            }
          }
        } else {
          for (i in obj) {
            if (callback.call(obj[i], i, obj[i]) === false) {
              break;
            }
          }
        }

        return obj;
      },
      // Support: Android <=4.0 only
      trim: function (text) {
        return text == null ? "" : (text + "").replace(rtrim, "");
      },
      // results is for internal usage only
      makeArray: function (arr, results) {
        var ret = results || [];

        if (arr != null) {
          if (isArrayLike(Object(arr))) {
            jQuery.merge(ret, typeof arr === "string" ? [arr] : arr);
          } else {
            push$1.call(ret, arr);
          }
        }

        return ret;
      },
      inArray: function (elem, arr, i) {
        return arr == null ? -1 : indexOf$1.call(arr, elem, i);
      },
      // Support: Android <=4.0 only, PhantomJS 1 only
      // push.apply(_, arraylike) throws on ancient WebKit
      merge: function (first, second) {
        var len = +second.length,
            j = 0,
            i = first.length;

        for (; j < len; j++) {
          first[i++] = second[j];
        }

        first.length = i;
        return first;
      },
      grep: function (elems, callback, invert) {
        var callbackInverse,
            matches = [],
            i = 0,
            length = elems.length,
            callbackExpect = !invert; // Go through the array, only saving the items
        // that pass the validator function

        for (; i < length; i++) {
          callbackInverse = !callback(elems[i], i);

          if (callbackInverse !== callbackExpect) {
            matches.push(elems[i]);
          }
        }

        return matches;
      },
      // arg is for internal usage only
      map: function (elems, callback, arg) {
        var length,
            value,
            i = 0,
            ret = []; // Go through the array, translating each of the items to their new values

        if (isArrayLike(elems)) {
          length = elems.length;

          for (; i < length; i++) {
            value = callback(elems[i], i, arg);

            if (value != null) {
              ret.push(value);
            }
          } // Go through every key on the object,

        } else {
          for (i in elems) {
            value = callback(elems[i], i, arg);

            if (value != null) {
              ret.push(value);
            }
          }
        } // Flatten any nested arrays


        return concat$1.apply([], ret);
      },
      // A global GUID counter for objects
      guid: 1,
      // jQuery.support is not used in Core but other projects attach their
      // properties to it so it needs to exist.
      support: support
    });

    if (typeof Symbol === "function") {
      jQuery.fn[Symbol.iterator] = arr[Symbol.iterator];
    } // Populate the class2type map


    jQuery.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "), function (i, name) {
      class2type["[object " + name + "]"] = name.toLowerCase();
    });

    function isArrayLike(obj) {
      // Support: real iOS 8.2 only (not reproducible in simulator)
      // `in` check used to prevent JIT error (gh-2145)
      // hasOwn isn't used here due to false negatives
      // regarding Nodelist length in IE
      var length = !!obj && "length" in obj && obj.length,
          type = toType(obj);

      if (isFunction(obj) || isWindow(obj)) {
        return false;
      }

      return type === "array" || length === 0 || typeof length === "number" && length > 0 && length - 1 in obj;
    }

    var Sizzle =
    /*!
    * Sizzle CSS Selector Engine v2.3.3
    * https://sizzlejs.com/
    *
    * Copyright jQuery Foundation and other contributors
    * Released under the MIT license
    * http://jquery.org/license
    *
    * Date: 2016-08-08
    */
    function (window) {
      var i,
          support,
          Expr,
          getText,
          isXML,
          tokenize,
          compile,
          select,
          outermostContext,
          sortInput,
          hasDuplicate,
          // Local document vars
      setDocument,
          document,
          docElem,
          documentIsHTML,
          rbuggyQSA,
          rbuggyMatches,
          matches,
          contains,
          // Instance-specific data
      expando = "sizzle" + 1 * new Date(),
          preferredDoc = window.document,
          dirruns = 0,
          done = 0,
          classCache = createCache(),
          tokenCache = createCache(),
          compilerCache = createCache(),
          sortOrder = function (a, b) {
        if (a === b) {
          hasDuplicate = true;
        }

        return 0;
      },
          // Instance methods
      hasOwn = {}.hasOwnProperty,
          arr = [],
          pop = arr.pop,
          push_native = arr.push,
          push = arr.push,
          slice = arr.slice,
          // Use a stripped-down indexOf as it's faster than native
      // https://jsperf.com/thor-indexof-vs-for/5
      indexOf = function (list, elem) {
        var i = 0,
            len = list.length;

        for (; i < len; i++) {
          if (list[i] === elem) {
            return i;
          }
        }

        return -1;
      },
          booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
          // Regular expressions
      // http://www.w3.org/TR/css3-selectors/#whitespace
      whitespace = "[\\x20\\t\\r\\n\\f]",
          // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
      identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+",
          // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
      attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + // Operator (capture 2)
      "*([*^$|!~]?=)" + whitespace + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
      "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + "*\\]",
          pseudos = ":(" + identifier + ")(?:\\((" + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
      // 1. quoted (capture 3; capture 4 or capture 5)
      "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + // 2. simple (capture 6)
      "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + // 3. anything else (capture 2)
      ".*" + ")\\)|)",
          // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
      rwhitespace = new RegExp(whitespace + "+", "g"),
          rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g"),
          rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*"),
          rcombinators = new RegExp("^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"),
          rattributeQuotes = new RegExp("=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g"),
          rpseudo = new RegExp(pseudos),
          ridentifier = new RegExp("^" + identifier + "$"),
          matchExpr = {
        "ID": new RegExp("^#(" + identifier + ")"),
        "CLASS": new RegExp("^\\.(" + identifier + ")"),
        "TAG": new RegExp("^(" + identifier + "|[*])"),
        "ATTR": new RegExp("^" + attributes),
        "PSEUDO": new RegExp("^" + pseudos),
        "CHILD": new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i"),
        "bool": new RegExp("^(?:" + booleans + ")$", "i"),
        // For use in libraries implementing .is()
        // We use this for POS matching in `select`
        "needsContext": new RegExp("^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i")
      },
          rinputs = /^(?:input|select|textarea|button)$/i,
          rheader = /^h\d$/i,
          rnative = /^[^{]+\{\s*\[native \w/,
          // Easily-parseable/retrievable ID or TAG or CLASS selectors
      rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
          rsibling = /[+~]/,
          // CSS escapes
      // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
      runescape = new RegExp("\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig"),
          funescape = function (_, escaped, escapedWhitespace) {
        var high = "0x" + escaped - 0x10000; // NaN means non-codepoint
        // Support: Firefox<24
        // Workaround erroneous numeric interpretation of +"0x"

        return high !== high || escapedWhitespace ? escaped : high < 0 ? // BMP codepoint
        String.fromCharCode(high + 0x10000) : // Supplemental Plane codepoint (surrogate pair)
        String.fromCharCode(high >> 10 | 0xD800, high & 0x3FF | 0xDC00);
      },
          // CSS string/identifier serialization
      // https://drafts.csswg.org/cssom/#common-serializing-idioms
      // eslint-disable-next-line no-control-regex
      rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,
          fcssescape = function (ch, asCodePoint) {
        if (asCodePoint) {
          // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
          if (ch === "\0") {
            return "\uFFFD";
          } // Control characters and (dependent upon position) numbers get escaped as code points


          return ch.slice(0, -1) + "\\" + ch.charCodeAt(ch.length - 1).toString(16) + " ";
        } // Other potentially-special ASCII characters get backslash-escaped


        return "\\" + ch;
      },
          // Used for iframes
      // See setDocument()
      // Removing the function wrapper causes a "Permission Denied"
      // error in IE
      unloadHandler = function () {
        setDocument();
      },
          disabledAncestor = addCombinator(function (elem) {
        return elem.disabled === true && ("form" in elem || "label" in elem);
      }, {
        dir: "parentNode",
        next: "legend"
      }); // Optimize for push.apply( _, NodeList )


      try {
        push.apply(arr = slice.call(preferredDoc.childNodes), preferredDoc.childNodes); // Support: Android<4.0
        // Detect silently failing push.apply

        arr[preferredDoc.childNodes.length].nodeType;
      } catch (e) {
        push = {
          apply: arr.length ? // Leverage slice if possible
          function (target, els) {
            push_native.apply(target, slice.call(els));
          } : // Support: IE<9
          // Otherwise append directly
          function (target, els) {
            var j = target.length,
                i = 0; // Can't trust NodeList.length

            while (target[j++] = els[i++]) {}

            target.length = j - 1;
          }
        };
      }

      function Sizzle(selector, context, results, seed) {
        var m,
            i,
            elem,
            nid,
            match,
            groups,
            newSelector,
            newContext = context && context.ownerDocument,
            // nodeType defaults to 9, since context defaults to document
        nodeType = context ? context.nodeType : 9;
        results = results || []; // Return early from calls with invalid selector or context

        if (typeof selector !== "string" || !selector || nodeType !== 1 && nodeType !== 9 && nodeType !== 11) {
          return results;
        } // Try to shortcut find operations (as opposed to filters) in HTML documents


        if (!seed) {
          if ((context ? context.ownerDocument || context : preferredDoc) !== document) {
            setDocument(context);
          }

          context = context || document;

          if (documentIsHTML) {
            // If the selector is sufficiently simple, try using a "get*By*" DOM method
            // (excepting DocumentFragment context, where the methods don't exist)
            if (nodeType !== 11 && (match = rquickExpr.exec(selector))) {
              // ID selector
              if (m = match[1]) {
                // Document context
                if (nodeType === 9) {
                  if (elem = context.getElementById(m)) {
                    // Support: IE, Opera, Webkit
                    // TODO: identify versions
                    // getElementById can match elements by name instead of ID
                    if (elem.id === m) {
                      results.push(elem);
                      return results;
                    }
                  } else {
                    return results;
                  } // Element context

                } else {
                  // Support: IE, Opera, Webkit
                  // TODO: identify versions
                  // getElementById can match elements by name instead of ID
                  if (newContext && (elem = newContext.getElementById(m)) && contains(context, elem) && elem.id === m) {
                    results.push(elem);
                    return results;
                  }
                } // Type selector

              } else if (match[2]) {
                push.apply(results, context.getElementsByTagName(selector));
                return results; // Class selector
              } else if ((m = match[3]) && support.getElementsByClassName && context.getElementsByClassName) {
                push.apply(results, context.getElementsByClassName(m));
                return results;
              }
            } // Take advantage of querySelectorAll


            if (support.qsa && !compilerCache[selector + " "] && (!rbuggyQSA || !rbuggyQSA.test(selector))) {
              if (nodeType !== 1) {
                newContext = context;
                newSelector = selector; // qSA looks outside Element context, which is not what we want
                // Thanks to Andrew Dupont for this workaround technique
                // Support: IE <=8
                // Exclude object elements
              } else if (context.nodeName.toLowerCase() !== "object") {
                // Capture the context ID, setting it first if necessary
                if (nid = context.getAttribute("id")) {
                  nid = nid.replace(rcssescape, fcssescape);
                } else {
                  context.setAttribute("id", nid = expando);
                } // Prefix every selector in the list


                groups = tokenize(selector);
                i = groups.length;

                while (i--) {
                  groups[i] = "#" + nid + " " + toSelector(groups[i]);
                }

                newSelector = groups.join(","); // Expand context for sibling selectors

                newContext = rsibling.test(selector) && testContext(context.parentNode) || context;
              }

              if (newSelector) {
                try {
                  push.apply(results, newContext.querySelectorAll(newSelector));
                  return results;
                } catch (qsaError) {} finally {
                  if (nid === expando) {
                    context.removeAttribute("id");
                  }
                }
              }
            }
          }
        } // All others


        return select(selector.replace(rtrim, "$1"), context, results, seed);
      }
      /**
       * Create key-value caches of limited size
       * @returns {function(string, object)} Returns the Object data after storing it on itself with
       *    property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
       *    deleting the oldest entry
       */


      function createCache() {
        var keys = [];

        function cache(key, value) {
          // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
          if (keys.push(key + " ") > Expr.cacheLength) {
            // Only keep the most recent entries
            delete cache[keys.shift()];
          }

          return cache[key + " "] = value;
        }

        return cache;
      }
      /**
       * Mark a function for special use by Sizzle
       * @param {Function} fn The function to mark
       */


      function markFunction(fn) {
        fn[expando] = true;
        return fn;
      }
      /**
       * Support testing using an element
       * @param {Function} fn Passed the created element and returns a boolean result
       */


      function assert(fn) {
        var el = document.createElement("fieldset");

        try {
          return !!fn(el);
        } catch (e) {
          return false;
        } finally {
          // Remove from its parent by default
          if (el.parentNode) {
            el.parentNode.removeChild(el);
          } // release memory in IE


          el = null;
        }
      }
      /**
       * Checks document order of two siblings
       * @param {Element} a
       * @param {Element} b
       * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
       */


      function siblingCheck(a, b) {
        var cur = b && a,
            diff = cur && a.nodeType === 1 && b.nodeType === 1 && a.sourceIndex - b.sourceIndex; // Use IE sourceIndex if available on both nodes

        if (diff) {
          return diff;
        } // Check if b follows a


        if (cur) {
          while (cur = cur.nextSibling) {
            if (cur === b) {
              return -1;
            }
          }
        }

        return a ? 1 : -1;
      }
      /**
       * Returns a function to use in pseudos for input types
       * @param {String} type
       */


      function createInputPseudo(type) {
        return function (elem) {
          var name = elem.nodeName.toLowerCase();
          return name === "input" && elem.type === type;
        };
      }
      /**
       * Returns a function to use in pseudos for buttons
       * @param {String} type
       */


      function createButtonPseudo(type) {
        return function (elem) {
          var name = elem.nodeName.toLowerCase();
          return (name === "input" || name === "button") && elem.type === type;
        };
      }
      /**
       * Returns a function to use in pseudos for :enabled/:disabled
       * @param {Boolean} disabled true for :disabled; false for :enabled
       */


      function createDisabledPseudo(disabled) {
        // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
        return function (elem) {
          // Only certain elements can match :enabled or :disabled
          // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
          // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
          if ("form" in elem) {
            // Check for inherited disabledness on relevant non-disabled elements:
            // * listed form-associated elements in a disabled fieldset
            //   https://html.spec.whatwg.org/multipage/forms.html#category-listed
            //   https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
            // * option elements in a disabled optgroup
            //   https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
            // All such elements have a "form" property.
            if (elem.parentNode && elem.disabled === false) {
              // Option elements defer to a parent optgroup if present
              if ("label" in elem) {
                if ("label" in elem.parentNode) {
                  return elem.parentNode.disabled === disabled;
                } else {
                  return elem.disabled === disabled;
                }
              } // Support: IE 6 - 11
              // Use the isDisabled shortcut property to check for disabled fieldset ancestors


              return elem.isDisabled === disabled || // Where there is no isDisabled, check manually

              /* jshint -W018 */
              elem.isDisabled !== !disabled && disabledAncestor(elem) === disabled;
            }

            return elem.disabled === disabled; // Try to winnow out elements that can't be disabled before trusting the disabled property.
            // Some victims get caught in our net (label, legend, menu, track), but it shouldn't
            // even exist on them, let alone have a boolean value.
          } else if ("label" in elem) {
            return elem.disabled === disabled;
          } // Remaining elements are neither :enabled nor :disabled


          return false;
        };
      }
      /**
       * Returns a function to use in pseudos for positionals
       * @param {Function} fn
       */


      function createPositionalPseudo(fn) {
        return markFunction(function (argument) {
          argument = +argument;
          return markFunction(function (seed, matches) {
            var j,
                matchIndexes = fn([], seed.length, argument),
                i = matchIndexes.length; // Match elements found at the specified indexes

            while (i--) {
              if (seed[j = matchIndexes[i]]) {
                seed[j] = !(matches[j] = seed[j]);
              }
            }
          });
        });
      }
      /**
       * Checks a node for validity as a Sizzle context
       * @param {Element|Object=} context
       * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
       */


      function testContext(context) {
        return context && typeof context.getElementsByTagName !== "undefined" && context;
      } // Expose support vars for convenience


      support = Sizzle.support = {};
      /**
       * Detects XML nodes
       * @param {Element|Object} elem An element or a document
       * @returns {Boolean} True iff elem is a non-HTML XML node
       */

      isXML = Sizzle.isXML = function (elem) {
        // documentElement is verified for cases where it doesn't yet exist
        // (such as loading iframes in IE - #4833)
        var documentElement = elem && (elem.ownerDocument || elem).documentElement;
        return documentElement ? documentElement.nodeName !== "HTML" : false;
      };
      /**
       * Sets document-related variables once based on the current document
       * @param {Element|Object} [doc] An element or document object to use to set the document
       * @returns {Object} Returns the current document
       */


      setDocument = Sizzle.setDocument = function (node) {
        var hasCompare,
            subWindow,
            doc = node ? node.ownerDocument || node : preferredDoc; // Return early if doc is invalid or already selected

        if (doc === document || doc.nodeType !== 9 || !doc.documentElement) {
          return document;
        } // Update global variables


        document = doc;
        docElem = document.documentElement;
        documentIsHTML = !isXML(document); // Support: IE 9-11, Edge
        // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)

        if (preferredDoc !== document && (subWindow = document.defaultView) && subWindow.top !== subWindow) {
          // Support: IE 11, Edge
          if (subWindow.addEventListener) {
            subWindow.addEventListener("unload", unloadHandler, false); // Support: IE 9 - 10 only
          } else if (subWindow.attachEvent) {
            subWindow.attachEvent("onunload", unloadHandler);
          }
        }
        /* Attributes
        ---------------------------------------------------------------------- */
        // Support: IE<8
        // Verify that getAttribute really returns attributes and not properties
        // (excepting IE8 booleans)


        support.attributes = assert(function (el) {
          el.className = "i";
          return !el.getAttribute("className");
        });
        /* getElement(s)By*
        ---------------------------------------------------------------------- */
        // Check if getElementsByTagName("*") returns only elements

        support.getElementsByTagName = assert(function (el) {
          el.appendChild(document.createComment(""));
          return !el.getElementsByTagName("*").length;
        }); // Support: IE<9

        support.getElementsByClassName = rnative.test(document.getElementsByClassName); // Support: IE<10
        // Check if getElementById returns elements by name
        // The broken getElementById methods don't pick up programmatically-set names,
        // so use a roundabout getElementsByName test

        support.getById = assert(function (el) {
          docElem.appendChild(el).id = expando;
          return !document.getElementsByName || !document.getElementsByName(expando).length;
        }); // ID filter and find

        if (support.getById) {
          Expr.filter["ID"] = function (id) {
            var attrId = id.replace(runescape, funescape);
            return function (elem) {
              return elem.getAttribute("id") === attrId;
            };
          };

          Expr.find["ID"] = function (id, context) {
            if (typeof context.getElementById !== "undefined" && documentIsHTML) {
              var elem = context.getElementById(id);
              return elem ? [elem] : [];
            }
          };
        } else {
          Expr.filter["ID"] = function (id) {
            var attrId = id.replace(runescape, funescape);
            return function (elem) {
              var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
              return node && node.value === attrId;
            };
          }; // Support: IE 6 - 7 only
          // getElementById is not reliable as a find shortcut


          Expr.find["ID"] = function (id, context) {
            if (typeof context.getElementById !== "undefined" && documentIsHTML) {
              var node,
                  i,
                  elems,
                  elem = context.getElementById(id);

              if (elem) {
                // Verify the id attribute
                node = elem.getAttributeNode("id");

                if (node && node.value === id) {
                  return [elem];
                } // Fall back on getElementsByName


                elems = context.getElementsByName(id);
                i = 0;

                while (elem = elems[i++]) {
                  node = elem.getAttributeNode("id");

                  if (node && node.value === id) {
                    return [elem];
                  }
                }
              }

              return [];
            }
          };
        } // Tag


        Expr.find["TAG"] = support.getElementsByTagName ? function (tag, context) {
          if (typeof context.getElementsByTagName !== "undefined") {
            return context.getElementsByTagName(tag); // DocumentFragment nodes don't have gEBTN
          } else if (support.qsa) {
            return context.querySelectorAll(tag);
          }
        } : function (tag, context) {
          var elem,
              tmp = [],
              i = 0,
              // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
          results = context.getElementsByTagName(tag); // Filter out possible comments

          if (tag === "*") {
            while (elem = results[i++]) {
              if (elem.nodeType === 1) {
                tmp.push(elem);
              }
            }

            return tmp;
          }

          return results;
        }; // Class

        Expr.find["CLASS"] = support.getElementsByClassName && function (className, context) {
          if (typeof context.getElementsByClassName !== "undefined" && documentIsHTML) {
            return context.getElementsByClassName(className);
          }
        };
        /* QSA/matchesSelector
        ---------------------------------------------------------------------- */
        // QSA and matchesSelector support
        // matchesSelector(:active) reports false when true (IE9/Opera 11.5)


        rbuggyMatches = []; // qSa(:focus) reports false when true (Chrome 21)
        // We allow this because of a bug in IE8/9 that throws an error
        // whenever `document.activeElement` is accessed on an iframe
        // So, we allow :focus to pass through QSA all the time to avoid the IE error
        // See https://bugs.jquery.com/ticket/13378

        rbuggyQSA = [];

        if (support.qsa = rnative.test(document.querySelectorAll)) {
          // Build QSA regex
          // Regex strategy adopted from Diego Perini
          assert(function (el) {
            // Select is set to empty string on purpose
            // This is to test IE's treatment of not explicitly
            // setting a boolean content attribute,
            // since its presence should be enough
            // https://bugs.jquery.com/ticket/12359
            docElem.appendChild(el).innerHTML = "<a id='" + expando + "'></a>" + "<select id='" + expando + "-\r\\' msallowcapture=''>" + "<option selected=''></option></select>"; // Support: IE8, Opera 11-12.16
            // Nothing should be selected when empty strings follow ^= or $= or *=
            // The test attribute must be unknown in Opera but "safe" for WinRT
            // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section

            if (el.querySelectorAll("[msallowcapture^='']").length) {
              rbuggyQSA.push("[*^$]=" + whitespace + "*(?:''|\"\")");
            } // Support: IE8
            // Boolean attributes and "value" are not treated correctly


            if (!el.querySelectorAll("[selected]").length) {
              rbuggyQSA.push("\\[" + whitespace + "*(?:value|" + booleans + ")");
            } // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+


            if (!el.querySelectorAll("[id~=" + expando + "-]").length) {
              rbuggyQSA.push("~=");
            } // Webkit/Opera - :checked should return selected option elements
            // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
            // IE8 throws error here and will not see later tests


            if (!el.querySelectorAll(":checked").length) {
              rbuggyQSA.push(":checked");
            } // Support: Safari 8+, iOS 8+
            // https://bugs.webkit.org/show_bug.cgi?id=136851
            // In-page `selector#id sibling-combinator selector` fails


            if (!el.querySelectorAll("a#" + expando + "+*").length) {
              rbuggyQSA.push(".#.+[+~]");
            }
          });
          assert(function (el) {
            el.innerHTML = "<a href='' disabled='disabled'></a>" + "<select disabled='disabled'><option/></select>"; // Support: Windows 8 Native Apps
            // The type and name attributes are restricted during .innerHTML assignment

            var input = document.createElement("input");
            input.setAttribute("type", "hidden");
            el.appendChild(input).setAttribute("name", "D"); // Support: IE8
            // Enforce case-sensitivity of name attribute

            if (el.querySelectorAll("[name=d]").length) {
              rbuggyQSA.push("name" + whitespace + "*[*^$|!~]?=");
            } // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
            // IE8 throws error here and will not see later tests


            if (el.querySelectorAll(":enabled").length !== 2) {
              rbuggyQSA.push(":enabled", ":disabled");
            } // Support: IE9-11+
            // IE's :disabled selector does not pick up the children of disabled fieldsets


            docElem.appendChild(el).disabled = true;

            if (el.querySelectorAll(":disabled").length !== 2) {
              rbuggyQSA.push(":enabled", ":disabled");
            } // Opera 10-11 does not throw on post-comma invalid pseudos


            el.querySelectorAll("*,:x");
            rbuggyQSA.push(",.*:");
          });
        }

        if (support.matchesSelector = rnative.test(matches = docElem.matches || docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector)) {
          assert(function (el) {
            // Check to see if it's possible to do matchesSelector
            // on a disconnected node (IE 9)
            support.disconnectedMatch = matches.call(el, "*"); // This should fail with an exception
            // Gecko does not error, returns false instead

            matches.call(el, "[s!='']:x");
            rbuggyMatches.push("!=", pseudos);
          });
        }

        rbuggyQSA = rbuggyQSA.length && new RegExp(rbuggyQSA.join("|"));
        rbuggyMatches = rbuggyMatches.length && new RegExp(rbuggyMatches.join("|"));
        /* Contains
        ---------------------------------------------------------------------- */

        hasCompare = rnative.test(docElem.compareDocumentPosition); // Element contains another
        // Purposefully self-exclusive
        // As in, an element does not contain itself

        contains = hasCompare || rnative.test(docElem.contains) ? function (a, b) {
          var adown = a.nodeType === 9 ? a.documentElement : a,
              bup = b && b.parentNode;
          return a === bup || !!(bup && bup.nodeType === 1 && (adown.contains ? adown.contains(bup) : a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16));
        } : function (a, b) {
          if (b) {
            while (b = b.parentNode) {
              if (b === a) {
                return true;
              }
            }
          }

          return false;
        };
        /* Sorting
        ---------------------------------------------------------------------- */
        // Document order sorting

        sortOrder = hasCompare ? function (a, b) {
          // Flag for duplicate removal
          if (a === b) {
            hasDuplicate = true;
            return 0;
          } // Sort on method existence if only one input has compareDocumentPosition


          var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;

          if (compare) {
            return compare;
          } // Calculate position if both inputs belong to the same document


          compare = (a.ownerDocument || a) === (b.ownerDocument || b) ? a.compareDocumentPosition(b) : // Otherwise we know they are disconnected
          1; // Disconnected nodes

          if (compare & 1 || !support.sortDetached && b.compareDocumentPosition(a) === compare) {
            // Choose the first element that is related to our preferred document
            if (a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a)) {
              return -1;
            }

            if (b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b)) {
              return 1;
            } // Maintain original order


            return sortInput ? indexOf(sortInput, a) - indexOf(sortInput, b) : 0;
          }

          return compare & 4 ? -1 : 1;
        } : function (a, b) {
          // Exit early if the nodes are identical
          if (a === b) {
            hasDuplicate = true;
            return 0;
          }

          var cur,
              i = 0,
              aup = a.parentNode,
              bup = b.parentNode,
              ap = [a],
              bp = [b]; // Parentless nodes are either documents or disconnected

          if (!aup || !bup) {
            return a === document ? -1 : b === document ? 1 : aup ? -1 : bup ? 1 : sortInput ? indexOf(sortInput, a) - indexOf(sortInput, b) : 0; // If the nodes are siblings, we can do a quick check
          } else if (aup === bup) {
            return siblingCheck(a, b);
          } // Otherwise we need full lists of their ancestors for comparison


          cur = a;

          while (cur = cur.parentNode) {
            ap.unshift(cur);
          }

          cur = b;

          while (cur = cur.parentNode) {
            bp.unshift(cur);
          } // Walk down the tree looking for a discrepancy


          while (ap[i] === bp[i]) {
            i++;
          }

          return i ? // Do a sibling check if the nodes have a common ancestor
          siblingCheck(ap[i], bp[i]) : // Otherwise nodes in our document sort first
          ap[i] === preferredDoc ? -1 : bp[i] === preferredDoc ? 1 : 0;
        };
        return document;
      };

      Sizzle.matches = function (expr, elements) {
        return Sizzle(expr, null, null, elements);
      };

      Sizzle.matchesSelector = function (elem, expr) {
        // Set document vars if needed
        if ((elem.ownerDocument || elem) !== document) {
          setDocument(elem);
        } // Make sure that attribute selectors are quoted


        expr = expr.replace(rattributeQuotes, "='$1']");

        if (support.matchesSelector && documentIsHTML && !compilerCache[expr + " "] && (!rbuggyMatches || !rbuggyMatches.test(expr)) && (!rbuggyQSA || !rbuggyQSA.test(expr))) {
          try {
            var ret = matches.call(elem, expr); // IE 9's matchesSelector returns false on disconnected nodes

            if (ret || support.disconnectedMatch || // As well, disconnected nodes are said to be in a document
            // fragment in IE 9
            elem.document && elem.document.nodeType !== 11) {
              return ret;
            }
          } catch (e) {}
        }

        return Sizzle(expr, document, null, [elem]).length > 0;
      };

      Sizzle.contains = function (context, elem) {
        // Set document vars if needed
        if ((context.ownerDocument || context) !== document) {
          setDocument(context);
        }

        return contains(context, elem);
      };

      Sizzle.attr = function (elem, name) {
        // Set document vars if needed
        if ((elem.ownerDocument || elem) !== document) {
          setDocument(elem);
        }

        var fn = Expr.attrHandle[name.toLowerCase()],
            // Don't get fooled by Object.prototype properties (jQuery #13807)
        val = fn && hasOwn.call(Expr.attrHandle, name.toLowerCase()) ? fn(elem, name, !documentIsHTML) : undefined;
        return val !== undefined ? val : support.attributes || !documentIsHTML ? elem.getAttribute(name) : (val = elem.getAttributeNode(name)) && val.specified ? val.value : null;
      };

      Sizzle.escape = function (sel) {
        return (sel + "").replace(rcssescape, fcssescape);
      };

      Sizzle.error = function (msg) {
        throw new Error("Syntax error, unrecognized expression: " + msg);
      };
      /**
       * Document sorting and removing duplicates
       * @param {ArrayLike} results
       */


      Sizzle.uniqueSort = function (results) {
        var elem,
            duplicates = [],
            j = 0,
            i = 0; // Unless we *know* we can detect duplicates, assume their presence

        hasDuplicate = !support.detectDuplicates;
        sortInput = !support.sortStable && results.slice(0);
        results.sort(sortOrder);

        if (hasDuplicate) {
          while (elem = results[i++]) {
            if (elem === results[i]) {
              j = duplicates.push(i);
            }
          }

          while (j--) {
            results.splice(duplicates[j], 1);
          }
        } // Clear input after sorting to release objects
        // See https://github.com/jquery/sizzle/pull/225


        sortInput = null;
        return results;
      };
      /**
       * Utility function for retrieving the text value of an array of DOM nodes
       * @param {Array|Element} elem
       */


      getText = Sizzle.getText = function (elem) {
        var node,
            ret = "",
            i = 0,
            nodeType = elem.nodeType;

        if (!nodeType) {
          // If no nodeType, this is expected to be an array
          while (node = elem[i++]) {
            // Do not traverse comment nodes
            ret += getText(node);
          }
        } else if (nodeType === 1 || nodeType === 9 || nodeType === 11) {
          // Use textContent for elements
          // innerText usage removed for consistency of new lines (jQuery #11153)
          if (typeof elem.textContent === "string") {
            return elem.textContent;
          } else {
            // Traverse its children
            for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
              ret += getText(elem);
            }
          }
        } else if (nodeType === 3 || nodeType === 4) {
          return elem.nodeValue;
        } // Do not include comment or processing instruction nodes


        return ret;
      };

      Expr = Sizzle.selectors = {
        // Can be adjusted by the user
        cacheLength: 50,
        createPseudo: markFunction,
        match: matchExpr,
        attrHandle: {},
        find: {},
        relative: {
          ">": {
            dir: "parentNode",
            first: true
          },
          " ": {
            dir: "parentNode"
          },
          "+": {
            dir: "previousSibling",
            first: true
          },
          "~": {
            dir: "previousSibling"
          }
        },
        preFilter: {
          "ATTR": function (match) {
            match[1] = match[1].replace(runescape, funescape); // Move the given value to match[3] whether quoted or unquoted

            match[3] = (match[3] || match[4] || match[5] || "").replace(runescape, funescape);

            if (match[2] === "~=") {
              match[3] = " " + match[3] + " ";
            }

            return match.slice(0, 4);
          },
          "CHILD": function (match) {
            /* matches from matchExpr["CHILD"]
            1 type (only|nth|...)
            2 what (child|of-type)
            3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
            4 xn-component of xn+y argument ([+-]?\d*n|)
            5 sign of xn-component
            6 x of xn-component
            7 sign of y-component
            8 y of y-component
            */
            match[1] = match[1].toLowerCase();

            if (match[1].slice(0, 3) === "nth") {
              // nth-* requires argument
              if (!match[3]) {
                Sizzle.error(match[0]);
              } // numeric x and y parameters for Expr.filter.CHILD
              // remember that false/true cast respectively to 0/1


              match[4] = +(match[4] ? match[5] + (match[6] || 1) : 2 * (match[3] === "even" || match[3] === "odd"));
              match[5] = +(match[7] + match[8] || match[3] === "odd"); // other types prohibit arguments
            } else if (match[3]) {
              Sizzle.error(match[0]);
            }

            return match;
          },
          "PSEUDO": function (match) {
            var excess,
                unquoted = !match[6] && match[2];

            if (matchExpr["CHILD"].test(match[0])) {
              return null;
            } // Accept quoted arguments as-is


            if (match[3]) {
              match[2] = match[4] || match[5] || ""; // Strip excess characters from unquoted arguments
            } else if (unquoted && rpseudo.test(unquoted) && ( // Get excess from tokenize (recursively)
            excess = tokenize(unquoted, true)) && ( // advance to the next closing parenthesis
            excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length)) {
              // excess is a negative index
              match[0] = match[0].slice(0, excess);
              match[2] = unquoted.slice(0, excess);
            } // Return only captures needed by the pseudo filter method (type and argument)


            return match.slice(0, 3);
          }
        },
        filter: {
          "TAG": function (nodeNameSelector) {
            var nodeName = nodeNameSelector.replace(runescape, funescape).toLowerCase();
            return nodeNameSelector === "*" ? function () {
              return true;
            } : function (elem) {
              return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
            };
          },
          "CLASS": function (className) {
            var pattern = classCache[className + " "];
            return pattern || (pattern = new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)")) && classCache(className, function (elem) {
              return pattern.test(typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "");
            });
          },
          "ATTR": function (name, operator, check) {
            return function (elem) {
              var result = Sizzle.attr(elem, name);

              if (result == null) {
                return operator === "!=";
              }

              if (!operator) {
                return true;
              }

              result += "";
              return operator === "=" ? result === check : operator === "!=" ? result !== check : operator === "^=" ? check && result.indexOf(check) === 0 : operator === "*=" ? check && result.indexOf(check) > -1 : operator === "$=" ? check && result.slice(-check.length) === check : operator === "~=" ? (" " + result.replace(rwhitespace, " ") + " ").indexOf(check) > -1 : operator === "|=" ? result === check || result.slice(0, check.length + 1) === check + "-" : false;
            };
          },
          "CHILD": function (type, what, argument, first, last) {
            var simple = type.slice(0, 3) !== "nth",
                forward = type.slice(-4) !== "last",
                ofType = what === "of-type";
            return first === 1 && last === 0 ? // Shortcut for :nth-*(n)
            function (elem) {
              return !!elem.parentNode;
            } : function (elem, context, xml) {
              var cache,
                  uniqueCache,
                  outerCache,
                  node,
                  nodeIndex,
                  start,
                  dir = simple !== forward ? "nextSibling" : "previousSibling",
                  parent = elem.parentNode,
                  name = ofType && elem.nodeName.toLowerCase(),
                  useCache = !xml && !ofType,
                  diff = false;

              if (parent) {
                // :(first|last|only)-(child|of-type)
                if (simple) {
                  while (dir) {
                    node = elem;

                    while (node = node[dir]) {
                      if (ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) {
                        return false;
                      }
                    } // Reverse direction for :only-* (if we haven't yet done so)


                    start = dir = type === "only" && !start && "nextSibling";
                  }

                  return true;
                }

                start = [forward ? parent.firstChild : parent.lastChild]; // non-xml :nth-child(...) stores cache data on `parent`

                if (forward && useCache) {
                  // Seek `elem` from a previously-cached index
                  // ...in a gzip-friendly way
                  node = parent;
                  outerCache = node[expando] || (node[expando] = {}); // Support: IE <9 only
                  // Defend against cloned attroperties (jQuery gh-1709)

                  uniqueCache = outerCache[node.uniqueID] || (outerCache[node.uniqueID] = {});
                  cache = uniqueCache[type] || [];
                  nodeIndex = cache[0] === dirruns && cache[1];
                  diff = nodeIndex && cache[2];
                  node = nodeIndex && parent.childNodes[nodeIndex];

                  while (node = ++nodeIndex && node && node[dir] || ( // Fallback to seeking `elem` from the start
                  diff = nodeIndex = 0) || start.pop()) {
                    // When found, cache indexes on `parent` and break
                    if (node.nodeType === 1 && ++diff && node === elem) {
                      uniqueCache[type] = [dirruns, nodeIndex, diff];
                      break;
                    }
                  }
                } else {
                  // Use previously-cached element index if available
                  if (useCache) {
                    // ...in a gzip-friendly way
                    node = elem;
                    outerCache = node[expando] || (node[expando] = {}); // Support: IE <9 only
                    // Defend against cloned attroperties (jQuery gh-1709)

                    uniqueCache = outerCache[node.uniqueID] || (outerCache[node.uniqueID] = {});
                    cache = uniqueCache[type] || [];
                    nodeIndex = cache[0] === dirruns && cache[1];
                    diff = nodeIndex;
                  } // xml :nth-child(...)
                  // or :nth-last-child(...) or :nth(-last)?-of-type(...)


                  if (diff === false) {
                    // Use the same loop as above to seek `elem` from the start
                    while (node = ++nodeIndex && node && node[dir] || (diff = nodeIndex = 0) || start.pop()) {
                      if ((ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) && ++diff) {
                        // Cache the index of each encountered element
                        if (useCache) {
                          outerCache = node[expando] || (node[expando] = {}); // Support: IE <9 only
                          // Defend against cloned attroperties (jQuery gh-1709)

                          uniqueCache = outerCache[node.uniqueID] || (outerCache[node.uniqueID] = {});
                          uniqueCache[type] = [dirruns, diff];
                        }

                        if (node === elem) {
                          break;
                        }
                      }
                    }
                  }
                } // Incorporate the offset, then check against cycle size


                diff -= last;
                return diff === first || diff % first === 0 && diff / first >= 0;
              }
            };
          },
          "PSEUDO": function (pseudo, argument) {
            // pseudo-class names are case-insensitive
            // http://www.w3.org/TR/selectors/#pseudo-classes
            // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
            // Remember that setFilters inherits from pseudos
            var args,
                fn = Expr.pseudos[pseudo] || Expr.setFilters[pseudo.toLowerCase()] || Sizzle.error("unsupported pseudo: " + pseudo); // The user may use createPseudo to indicate that
            // arguments are needed to create the filter function
            // just as Sizzle does

            if (fn[expando]) {
              return fn(argument);
            } // But maintain support for old signatures


            if (fn.length > 1) {
              args = [pseudo, pseudo, "", argument];
              return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase()) ? markFunction(function (seed, matches) {
                var idx,
                    matched = fn(seed, argument),
                    i = matched.length;

                while (i--) {
                  idx = indexOf(seed, matched[i]);
                  seed[idx] = !(matches[idx] = matched[i]);
                }
              }) : function (elem) {
                return fn(elem, 0, args);
              };
            }

            return fn;
          }
        },
        pseudos: {
          // Potentially complex pseudos
          "not": markFunction(function (selector) {
            // Trim the selector passed to compile
            // to avoid treating leading and trailing
            // spaces as combinators
            var input = [],
                results = [],
                matcher = compile(selector.replace(rtrim, "$1"));
            return matcher[expando] ? markFunction(function (seed, matches, context, xml) {
              var elem,
                  unmatched = matcher(seed, null, xml, []),
                  i = seed.length; // Match elements unmatched by `matcher`

              while (i--) {
                if (elem = unmatched[i]) {
                  seed[i] = !(matches[i] = elem);
                }
              }
            }) : function (elem, context, xml) {
              input[0] = elem;
              matcher(input, null, xml, results); // Don't keep the element (issue #299)

              input[0] = null;
              return !results.pop();
            };
          }),
          "has": markFunction(function (selector) {
            return function (elem) {
              return Sizzle(selector, elem).length > 0;
            };
          }),
          "contains": markFunction(function (text) {
            text = text.replace(runescape, funescape);
            return function (elem) {
              return (elem.textContent || elem.innerText || getText(elem)).indexOf(text) > -1;
            };
          }),
          // "Whether an element is represented by a :lang() selector
          // is based solely on the element's language value
          // being equal to the identifier C,
          // or beginning with the identifier C immediately followed by "-".
          // The matching of C against the element's language value is performed case-insensitively.
          // The identifier C does not have to be a valid language name."
          // http://www.w3.org/TR/selectors/#lang-pseudo
          "lang": markFunction(function (lang) {
            // lang value must be a valid identifier
            if (!ridentifier.test(lang || "")) {
              Sizzle.error("unsupported lang: " + lang);
            }

            lang = lang.replace(runescape, funescape).toLowerCase();
            return function (elem) {
              var elemLang;

              do {
                if (elemLang = documentIsHTML ? elem.lang : elem.getAttribute("xml:lang") || elem.getAttribute("lang")) {
                  elemLang = elemLang.toLowerCase();
                  return elemLang === lang || elemLang.indexOf(lang + "-") === 0;
                }
              } while ((elem = elem.parentNode) && elem.nodeType === 1);

              return false;
            };
          }),
          // Miscellaneous
          "target": function (elem) {
            var hash = window.location && window.location.hash;
            return hash && hash.slice(1) === elem.id;
          },
          "root": function (elem) {
            return elem === docElem;
          },
          "focus": function (elem) {
            return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
          },
          // Boolean properties
          "enabled": createDisabledPseudo(false),
          "disabled": createDisabledPseudo(true),
          "checked": function (elem) {
            // In CSS3, :checked should return both checked and selected elements
            // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
            var nodeName = elem.nodeName.toLowerCase();
            return nodeName === "input" && !!elem.checked || nodeName === "option" && !!elem.selected;
          },
          "selected": function (elem) {
            // Accessing this property makes selected-by-default
            // options in Safari work properly
            if (elem.parentNode) {
              elem.parentNode.selectedIndex;
            }

            return elem.selected === true;
          },
          // Contents
          "empty": function (elem) {
            // http://www.w3.org/TR/selectors/#empty-pseudo
            // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
            //   but not by others (comment: 8; processing instruction: 7; etc.)
            // nodeType < 6 works because attributes (2) do not appear as children
            for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
              if (elem.nodeType < 6) {
                return false;
              }
            }

            return true;
          },
          "parent": function (elem) {
            return !Expr.pseudos["empty"](elem);
          },
          // Element/input types
          "header": function (elem) {
            return rheader.test(elem.nodeName);
          },
          "input": function (elem) {
            return rinputs.test(elem.nodeName);
          },
          "button": function (elem) {
            var name = elem.nodeName.toLowerCase();
            return name === "input" && elem.type === "button" || name === "button";
          },
          "text": function (elem) {
            var attr;
            return elem.nodeName.toLowerCase() === "input" && elem.type === "text" && ( // Support: IE<8
            // New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
            (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text");
          },
          // Position-in-collection
          "first": createPositionalPseudo(function () {
            return [0];
          }),
          "last": createPositionalPseudo(function (matchIndexes, length) {
            return [length - 1];
          }),
          "eq": createPositionalPseudo(function (matchIndexes, length, argument) {
            return [argument < 0 ? argument + length : argument];
          }),
          "even": createPositionalPseudo(function (matchIndexes, length) {
            var i = 0;

            for (; i < length; i += 2) {
              matchIndexes.push(i);
            }

            return matchIndexes;
          }),
          "odd": createPositionalPseudo(function (matchIndexes, length) {
            var i = 1;

            for (; i < length; i += 2) {
              matchIndexes.push(i);
            }

            return matchIndexes;
          }),
          "lt": createPositionalPseudo(function (matchIndexes, length, argument) {
            var i = argument < 0 ? argument + length : argument;

            for (; --i >= 0;) {
              matchIndexes.push(i);
            }

            return matchIndexes;
          }),
          "gt": createPositionalPseudo(function (matchIndexes, length, argument) {
            var i = argument < 0 ? argument + length : argument;

            for (; ++i < length;) {
              matchIndexes.push(i);
            }

            return matchIndexes;
          })
        }
      };
      Expr.pseudos["nth"] = Expr.pseudos["eq"]; // Add button/input type pseudos

      for (i in {
        radio: true,
        checkbox: true,
        file: true,
        password: true,
        image: true
      }) {
        Expr.pseudos[i] = createInputPseudo(i);
      }

      for (i in {
        submit: true,
        reset: true
      }) {
        Expr.pseudos[i] = createButtonPseudo(i);
      } // Easy API for creating new setFilters


      function setFilters() {}

      setFilters.prototype = Expr.filters = Expr.pseudos;
      Expr.setFilters = new setFilters();

      tokenize = Sizzle.tokenize = function (selector, parseOnly) {
        var matched,
            match,
            tokens,
            type,
            soFar,
            groups,
            preFilters,
            cached = tokenCache[selector + " "];

        if (cached) {
          return parseOnly ? 0 : cached.slice(0);
        }

        soFar = selector;
        groups = [];
        preFilters = Expr.preFilter;

        while (soFar) {
          // Comma and first run
          if (!matched || (match = rcomma.exec(soFar))) {
            if (match) {
              // Don't consume trailing commas as valid
              soFar = soFar.slice(match[0].length) || soFar;
            }

            groups.push(tokens = []);
          }

          matched = false; // Combinators

          if (match = rcombinators.exec(soFar)) {
            matched = match.shift();
            tokens.push({
              value: matched,
              // Cast descendant combinators to space
              type: match[0].replace(rtrim, " ")
            });
            soFar = soFar.slice(matched.length);
          } // Filters


          for (type in Expr.filter) {
            if ((match = matchExpr[type].exec(soFar)) && (!preFilters[type] || (match = preFilters[type](match)))) {
              matched = match.shift();
              tokens.push({
                value: matched,
                type: type,
                matches: match
              });
              soFar = soFar.slice(matched.length);
            }
          }

          if (!matched) {
            break;
          }
        } // Return the length of the invalid excess
        // if we're just parsing
        // Otherwise, throw an error or return tokens


        return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) : // Cache the tokens
        tokenCache(selector, groups).slice(0);
      };

      function toSelector(tokens) {
        var i = 0,
            len = tokens.length,
            selector = "";

        for (; i < len; i++) {
          selector += tokens[i].value;
        }

        return selector;
      }

      function addCombinator(matcher, combinator, base) {
        var dir = combinator.dir,
            skip = combinator.next,
            key = skip || dir,
            checkNonElements = base && key === "parentNode",
            doneName = done++;
        return combinator.first ? // Check against closest ancestor/preceding element
        function (elem, context, xml) {
          while (elem = elem[dir]) {
            if (elem.nodeType === 1 || checkNonElements) {
              return matcher(elem, context, xml);
            }
          }

          return false;
        } : // Check against all ancestor/preceding elements
        function (elem, context, xml) {
          var oldCache,
              uniqueCache,
              outerCache,
              newCache = [dirruns, doneName]; // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching

          if (xml) {
            while (elem = elem[dir]) {
              if (elem.nodeType === 1 || checkNonElements) {
                if (matcher(elem, context, xml)) {
                  return true;
                }
              }
            }
          } else {
            while (elem = elem[dir]) {
              if (elem.nodeType === 1 || checkNonElements) {
                outerCache = elem[expando] || (elem[expando] = {}); // Support: IE <9 only
                // Defend against cloned attroperties (jQuery gh-1709)

                uniqueCache = outerCache[elem.uniqueID] || (outerCache[elem.uniqueID] = {});

                if (skip && skip === elem.nodeName.toLowerCase()) {
                  elem = elem[dir] || elem;
                } else if ((oldCache = uniqueCache[key]) && oldCache[0] === dirruns && oldCache[1] === doneName) {
                  // Assign to newCache so results back-propagate to previous elements
                  return newCache[2] = oldCache[2];
                } else {
                  // Reuse newcache so results back-propagate to previous elements
                  uniqueCache[key] = newCache; // A match means we're done; a fail means we have to keep checking

                  if (newCache[2] = matcher(elem, context, xml)) {
                    return true;
                  }
                }
              }
            }
          }

          return false;
        };
      }

      function elementMatcher(matchers) {
        return matchers.length > 1 ? function (elem, context, xml) {
          var i = matchers.length;

          while (i--) {
            if (!matchers[i](elem, context, xml)) {
              return false;
            }
          }

          return true;
        } : matchers[0];
      }

      function multipleContexts(selector, contexts, results) {
        var i = 0,
            len = contexts.length;

        for (; i < len; i++) {
          Sizzle(selector, contexts[i], results);
        }

        return results;
      }

      function condense(unmatched, map, filter, context, xml) {
        var elem,
            newUnmatched = [],
            i = 0,
            len = unmatched.length,
            mapped = map != null;

        for (; i < len; i++) {
          if (elem = unmatched[i]) {
            if (!filter || filter(elem, context, xml)) {
              newUnmatched.push(elem);

              if (mapped) {
                map.push(i);
              }
            }
          }
        }

        return newUnmatched;
      }

      function setMatcher(preFilter, selector, matcher, postFilter, postFinder, postSelector) {
        if (postFilter && !postFilter[expando]) {
          postFilter = setMatcher(postFilter);
        }

        if (postFinder && !postFinder[expando]) {
          postFinder = setMatcher(postFinder, postSelector);
        }

        return markFunction(function (seed, results, context, xml) {
          var temp,
              i,
              elem,
              preMap = [],
              postMap = [],
              preexisting = results.length,
              // Get initial elements from seed or context
          elems = seed || multipleContexts(selector || "*", context.nodeType ? [context] : context, []),
              // Prefilter to get matcher input, preserving a map for seed-results synchronization
          matcherIn = preFilter && (seed || !selector) ? condense(elems, preMap, preFilter, context, xml) : elems,
              matcherOut = matcher ? // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
          postFinder || (seed ? preFilter : preexisting || postFilter) ? // ...intermediate processing is necessary
          [] : // ...otherwise use results directly
          results : matcherIn; // Find primary matches

          if (matcher) {
            matcher(matcherIn, matcherOut, context, xml);
          } // Apply postFilter


          if (postFilter) {
            temp = condense(matcherOut, postMap);
            postFilter(temp, [], context, xml); // Un-match failing elements by moving them back to matcherIn

            i = temp.length;

            while (i--) {
              if (elem = temp[i]) {
                matcherOut[postMap[i]] = !(matcherIn[postMap[i]] = elem);
              }
            }
          }

          if (seed) {
            if (postFinder || preFilter) {
              if (postFinder) {
                // Get the final matcherOut by condensing this intermediate into postFinder contexts
                temp = [];
                i = matcherOut.length;

                while (i--) {
                  if (elem = matcherOut[i]) {
                    // Restore matcherIn since elem is not yet a final match
                    temp.push(matcherIn[i] = elem);
                  }
                }

                postFinder(null, matcherOut = [], temp, xml);
              } // Move matched elements from seed to results to keep them synchronized


              i = matcherOut.length;

              while (i--) {
                if ((elem = matcherOut[i]) && (temp = postFinder ? indexOf(seed, elem) : preMap[i]) > -1) {
                  seed[temp] = !(results[temp] = elem);
                }
              }
            } // Add elements to results, through postFinder if defined

          } else {
            matcherOut = condense(matcherOut === results ? matcherOut.splice(preexisting, matcherOut.length) : matcherOut);

            if (postFinder) {
              postFinder(null, results, matcherOut, xml);
            } else {
              push.apply(results, matcherOut);
            }
          }
        });
      }

      function matcherFromTokens(tokens) {
        var checkContext,
            matcher,
            j,
            len = tokens.length,
            leadingRelative = Expr.relative[tokens[0].type],
            implicitRelative = leadingRelative || Expr.relative[" "],
            i = leadingRelative ? 1 : 0,
            // The foundational matcher ensures that elements are reachable from top-level context(s)
        matchContext = addCombinator(function (elem) {
          return elem === checkContext;
        }, implicitRelative, true),
            matchAnyContext = addCombinator(function (elem) {
          return indexOf(checkContext, elem) > -1;
        }, implicitRelative, true),
            matchers = [function (elem, context, xml) {
          var ret = !leadingRelative && (xml || context !== outermostContext) || ((checkContext = context).nodeType ? matchContext(elem, context, xml) : matchAnyContext(elem, context, xml)); // Avoid hanging onto element (issue #299)

          checkContext = null;
          return ret;
        }];

        for (; i < len; i++) {
          if (matcher = Expr.relative[tokens[i].type]) {
            matchers = [addCombinator(elementMatcher(matchers), matcher)];
          } else {
            matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches); // Return special upon seeing a positional matcher

            if (matcher[expando]) {
              // Find the next relative operator (if any) for proper handling
              j = ++i;

              for (; j < len; j++) {
                if (Expr.relative[tokens[j].type]) {
                  break;
                }
              }

              return setMatcher(i > 1 && elementMatcher(matchers), i > 1 && toSelector( // If the preceding token was a descendant combinator, insert an implicit any-element `*`
              tokens.slice(0, i - 1).concat({
                value: tokens[i - 2].type === " " ? "*" : ""
              })).replace(rtrim, "$1"), matcher, i < j && matcherFromTokens(tokens.slice(i, j)), j < len && matcherFromTokens(tokens = tokens.slice(j)), j < len && toSelector(tokens));
            }

            matchers.push(matcher);
          }
        }

        return elementMatcher(matchers);
      }

      function matcherFromGroupMatchers(elementMatchers, setMatchers) {
        var bySet = setMatchers.length > 0,
            byElement = elementMatchers.length > 0,
            superMatcher = function (seed, context, xml, results, outermost) {
          var elem,
              j,
              matcher,
              matchedCount = 0,
              i = "0",
              unmatched = seed && [],
              setMatched = [],
              contextBackup = outermostContext,
              // We must always have either seed elements or outermost context
          elems = seed || byElement && Expr.find["TAG"]("*", outermost),
              // Use integer dirruns iff this is the outermost matcher
          dirrunsUnique = dirruns += contextBackup == null ? 1 : Math.random() || 0.1,
              len = elems.length;

          if (outermost) {
            outermostContext = context === document || context || outermost;
          } // Add elements passing elementMatchers directly to results
          // Support: IE<9, Safari
          // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id


          for (; i !== len && (elem = elems[i]) != null; i++) {
            if (byElement && elem) {
              j = 0;

              if (!context && elem.ownerDocument !== document) {
                setDocument(elem);
                xml = !documentIsHTML;
              }

              while (matcher = elementMatchers[j++]) {
                if (matcher(elem, context || document, xml)) {
                  results.push(elem);
                  break;
                }
              }

              if (outermost) {
                dirruns = dirrunsUnique;
              }
            } // Track unmatched elements for set filters


            if (bySet) {
              // They will have gone through all possible matchers
              if (elem = !matcher && elem) {
                matchedCount--;
              } // Lengthen the array for every element, matched or not


              if (seed) {
                unmatched.push(elem);
              }
            }
          } // `i` is now the count of elements visited above, and adding it to `matchedCount`
          // makes the latter nonnegative.


          matchedCount += i; // Apply set filters to unmatched elements
          // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
          // equals `i`), unless we didn't visit _any_ elements in the above loop because we have
          // no element matchers and no seed.
          // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
          // case, which will result in a "00" `matchedCount` that differs from `i` but is also
          // numerically zero.

          if (bySet && i !== matchedCount) {
            j = 0;

            while (matcher = setMatchers[j++]) {
              matcher(unmatched, setMatched, context, xml);
            }

            if (seed) {
              // Reintegrate element matches to eliminate the need for sorting
              if (matchedCount > 0) {
                while (i--) {
                  if (!(unmatched[i] || setMatched[i])) {
                    setMatched[i] = pop.call(results);
                  }
                }
              } // Discard index placeholder values to get only actual matches


              setMatched = condense(setMatched);
            } // Add matches to results


            push.apply(results, setMatched); // Seedless set matches succeeding multiple successful matchers stipulate sorting

            if (outermost && !seed && setMatched.length > 0 && matchedCount + setMatchers.length > 1) {
              Sizzle.uniqueSort(results);
            }
          } // Override manipulation of globals by nested matchers


          if (outermost) {
            dirruns = dirrunsUnique;
            outermostContext = contextBackup;
          }

          return unmatched;
        };

        return bySet ? markFunction(superMatcher) : superMatcher;
      }

      compile = Sizzle.compile = function (selector, match
      /* Internal Use Only */
      ) {
        var i,
            setMatchers = [],
            elementMatchers = [],
            cached = compilerCache[selector + " "];

        if (!cached) {
          // Generate a function of recursive functions that can be used to check each element
          if (!match) {
            match = tokenize(selector);
          }

          i = match.length;

          while (i--) {
            cached = matcherFromTokens(match[i]);

            if (cached[expando]) {
              setMatchers.push(cached);
            } else {
              elementMatchers.push(cached);
            }
          } // Cache the compiled function


          cached = compilerCache(selector, matcherFromGroupMatchers(elementMatchers, setMatchers)); // Save selector and tokenization

          cached.selector = selector;
        }

        return cached;
      };
      /**
       * A low-level selection function that works with Sizzle's compiled
       *  selector functions
       * @param {String|Function} selector A selector or a pre-compiled
       *  selector function built with Sizzle.compile
       * @param {Element} context
       * @param {Array} [results]
       * @param {Array} [seed] A set of elements to match against
       */


      select = Sizzle.select = function (selector, context, results, seed) {
        var i,
            tokens,
            token,
            type,
            find,
            compiled = typeof selector === "function" && selector,
            match = !seed && tokenize(selector = compiled.selector || selector);
        results = results || []; // Try to minimize operations if there is only one selector in the list and no seed
        // (the latter of which guarantees us context)

        if (match.length === 1) {
          // Reduce context if the leading compound selector is an ID
          tokens = match[0] = match[0].slice(0);

          if (tokens.length > 2 && (token = tokens[0]).type === "ID" && context.nodeType === 9 && documentIsHTML && Expr.relative[tokens[1].type]) {
            context = (Expr.find["ID"](token.matches[0].replace(runescape, funescape), context) || [])[0];

            if (!context) {
              return results; // Precompiled matchers will still verify ancestry, so step up a level
            } else if (compiled) {
              context = context.parentNode;
            }

            selector = selector.slice(tokens.shift().value.length);
          } // Fetch a seed set for right-to-left matching


          i = matchExpr["needsContext"].test(selector) ? 0 : tokens.length;

          while (i--) {
            token = tokens[i]; // Abort if we hit a combinator

            if (Expr.relative[type = token.type]) {
              break;
            }

            if (find = Expr.find[type]) {
              // Search, expanding context for leading sibling combinators
              if (seed = find(token.matches[0].replace(runescape, funescape), rsibling.test(tokens[0].type) && testContext(context.parentNode) || context)) {
                // If seed is empty or no tokens remain, we can return early
                tokens.splice(i, 1);
                selector = seed.length && toSelector(tokens);

                if (!selector) {
                  push.apply(results, seed);
                  return results;
                }

                break;
              }
            }
          }
        } // Compile and execute a filtering function if one is not provided
        // Provide `match` to avoid retokenization if we modified the selector above


        (compiled || compile(selector, match))(seed, context, !documentIsHTML, results, !context || rsibling.test(selector) && testContext(context.parentNode) || context);
        return results;
      }; // One-time assignments
      // Sort stability


      support.sortStable = expando.split("").sort(sortOrder).join("") === expando; // Support: Chrome 14-35+
      // Always assume duplicates if they aren't passed to the comparison function

      support.detectDuplicates = !!hasDuplicate; // Initialize against the default document

      setDocument();
      return Sizzle;
    }(window);

    jQuery.find = Sizzle;
    jQuery.expr = Sizzle.selectors; // Deprecated

    jQuery.expr[":"] = jQuery.expr.pseudos;
    jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
    jQuery.text = Sizzle.getText;
    jQuery.isXMLDoc = Sizzle.isXML;
    jQuery.contains = Sizzle.contains;
    jQuery.escapeSelector = Sizzle.escape;

    var dir = function (elem, dir, until) {
      var matched = [],
          truncate = until !== undefined;

      while ((elem = elem[dir]) && elem.nodeType !== 9) {
        if (elem.nodeType === 1) {
          if (truncate && jQuery(elem).is(until)) {
            break;
          }

          matched.push(elem);
        }
      }

      return matched;
    };

    var siblings = function (n, elem) {
      var matched = [];

      for (; n; n = n.nextSibling) {
        if (n.nodeType === 1 && n !== elem) {
          matched.push(n);
        }
      }

      return matched;
    };

    var rneedsContext = jQuery.expr.match.needsContext;

    function nodeName(elem, name) {
      return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
    }

    var rsingleTag = /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i; // Implement the identical functionality for filter and not

    function winnow(elements, qualifier, not) {
      if (isFunction(qualifier)) {
        return jQuery.grep(elements, function (elem, i) {
          return !!qualifier.call(elem, i, elem) !== not;
        });
      } // Single element


      if (qualifier.nodeType) {
        return jQuery.grep(elements, function (elem) {
          return elem === qualifier !== not;
        });
      } // Arraylike of elements (jQuery, arguments, Array)


      if (typeof qualifier !== "string") {
        return jQuery.grep(elements, function (elem) {
          return indexOf$1.call(qualifier, elem) > -1 !== not;
        });
      } // Filtered directly for both simple and complex selectors


      return jQuery.filter(qualifier, elements, not);
    }

    jQuery.filter = function (expr, elems, not) {
      var elem = elems[0];

      if (not) {
        expr = ":not(" + expr + ")";
      }

      if (elems.length === 1 && elem.nodeType === 1) {
        return jQuery.find.matchesSelector(elem, expr) ? [elem] : [];
      }

      return jQuery.find.matches(expr, jQuery.grep(elems, function (elem) {
        return elem.nodeType === 1;
      }));
    };

    jQuery.fn.extend({
      find: function (selector) {
        var i,
            ret,
            len = this.length,
            self = this;

        if (typeof selector !== "string") {
          return this.pushStack(jQuery(selector).filter(function () {
            for (i = 0; i < len; i++) {
              if (jQuery.contains(self[i], this)) {
                return true;
              }
            }
          }));
        }

        ret = this.pushStack([]);

        for (i = 0; i < len; i++) {
          jQuery.find(selector, self[i], ret);
        }

        return len > 1 ? jQuery.uniqueSort(ret) : ret;
      },
      filter: function (selector) {
        return this.pushStack(winnow(this, selector || [], false));
      },
      not: function (selector) {
        return this.pushStack(winnow(this, selector || [], true));
      },
      is: function (selector) {
        return !!winnow(this, // If this is a positional/relative selector, check membership in the returned set
        // so $("p:first").is("p:last") won't return true for a doc with two "p".
        typeof selector === "string" && rneedsContext.test(selector) ? jQuery(selector) : selector || [], false).length;
      }
    }); // Initialize a jQuery object
    // A central reference to the root jQuery(document)

    var rootjQuery,
        // A simple way to check for HTML strings
    // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
    // Strict HTML recognition (#11290: must start with <)
    // Shortcut simple #id case for speed
    rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
        init$2 = jQuery.fn.init = function (selector, context, root) {
      var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false)

      if (!selector) {
        return this;
      } // Method init() accepts an alternate rootjQuery
      // so migrate can support jQuery.sub (gh-2101)


      root = root || rootjQuery; // Handle HTML strings

      if (typeof selector === "string") {
        if (selector[0] === "<" && selector[selector.length - 1] === ">" && selector.length >= 3) {
          // Assume that strings that start and end with <> are HTML and skip the regex check
          match = [null, selector, null];
        } else {
          match = rquickExpr.exec(selector);
        } // Match html or make sure no context is specified for #id


        if (match && (match[1] || !context)) {
          // HANDLE: $(html) -> $(array)
          if (match[1]) {
            context = context instanceof jQuery ? context[0] : context; // Option to run scripts is true for back-compat
            // Intentionally let the error be thrown if parseHTML is not present

            jQuery.merge(this, jQuery.parseHTML(match[1], context && context.nodeType ? context.ownerDocument || context : document$2, true)); // HANDLE: $(html, props)

            if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) {
              for (match in context) {
                // Properties of context are called as methods if possible
                if (isFunction(this[match])) {
                  this[match](context[match]); // ...and otherwise set as attributes
                } else {
                  this.attr(match, context[match]);
                }
              }
            }

            return this; // HANDLE: $(#id)
          } else {
            elem = document$2.getElementById(match[2]);

            if (elem) {
              // Inject the element directly into the jQuery object
              this[0] = elem;
              this.length = 1;
            }

            return this;
          } // HANDLE: $(expr, $(...))

        } else if (!context || context.jquery) {
          return (context || root).find(selector); // HANDLE: $(expr, context)
          // (which is just equivalent to: $(context).find(expr)
        } else {
          return this.constructor(context).find(selector);
        } // HANDLE: $(DOMElement)

      } else if (selector.nodeType) {
        this[0] = selector;
        this.length = 1;
        return this; // HANDLE: $(function)
        // Shortcut for document ready
      } else if (isFunction(selector)) {
        return root.ready !== undefined ? root.ready(selector) : // Execute immediately if ready is not present
        selector(jQuery);
      }

      return jQuery.makeArray(selector, this);
    }; // Give the init function the jQuery prototype for later instantiation


    init$2.prototype = jQuery.fn; // Initialize central reference

    rootjQuery = jQuery(document$2);
    var rparentsprev = /^(?:parents|prev(?:Until|All))/,
        // Methods guaranteed to produce a unique set when starting from a unique set
    guaranteedUnique = {
      children: true,
      contents: true,
      next: true,
      prev: true
    };
    jQuery.fn.extend({
      has: function (target) {
        var targets = jQuery(target, this),
            l = targets.length;
        return this.filter(function () {
          var i = 0;

          for (; i < l; i++) {
            if (jQuery.contains(this, targets[i])) {
              return true;
            }
          }
        });
      },
      closest: function (selectors, context) {
        var cur,
            i = 0,
            l = this.length,
            matched = [],
            targets = typeof selectors !== "string" && jQuery(selectors); // Positional selectors never match, since there's no _selection_ context

        if (!rneedsContext.test(selectors)) {
          for (; i < l; i++) {
            for (cur = this[i]; cur && cur !== context; cur = cur.parentNode) {
              // Always skip document fragments
              if (cur.nodeType < 11 && (targets ? targets.index(cur) > -1 : // Don't pass non-elements to Sizzle
              cur.nodeType === 1 && jQuery.find.matchesSelector(cur, selectors))) {
                matched.push(cur);
                break;
              }
            }
          }
        }

        return this.pushStack(matched.length > 1 ? jQuery.uniqueSort(matched) : matched);
      },
      // Determine the position of an element within the set
      index: function (elem) {
        // No argument, return index in parent
        if (!elem) {
          return this[0] && this[0].parentNode ? this.first().prevAll().length : -1;
        } // Index in selector


        if (typeof elem === "string") {
          return indexOf$1.call(jQuery(elem), this[0]);
        } // Locate the position of the desired element


        return indexOf$1.call(this, // If it receives a jQuery object, the first element is used
        elem.jquery ? elem[0] : elem);
      },
      add: function (selector, context) {
        return this.pushStack(jQuery.uniqueSort(jQuery.merge(this.get(), jQuery(selector, context))));
      },
      addBack: function (selector) {
        return this.add(selector == null ? this.prevObject : this.prevObject.filter(selector));
      }
    });

    function sibling(cur, dir) {
      while ((cur = cur[dir]) && cur.nodeType !== 1) {}

      return cur;
    }

    jQuery.each({
      parent: function (elem) {
        var parent = elem.parentNode;
        return parent && parent.nodeType !== 11 ? parent : null;
      },
      parents: function (elem) {
        return dir(elem, "parentNode");
      },
      parentsUntil: function (elem, i, until) {
        return dir(elem, "parentNode", until);
      },
      next: function (elem) {
        return sibling(elem, "nextSibling");
      },
      prev: function (elem) {
        return sibling(elem, "previousSibling");
      },
      nextAll: function (elem) {
        return dir(elem, "nextSibling");
      },
      prevAll: function (elem) {
        return dir(elem, "previousSibling");
      },
      nextUntil: function (elem, i, until) {
        return dir(elem, "nextSibling", until);
      },
      prevUntil: function (elem, i, until) {
        return dir(elem, "previousSibling", until);
      },
      siblings: function (elem) {
        return siblings((elem.parentNode || {}).firstChild, elem);
      },
      children: function (elem) {
        return siblings(elem.firstChild);
      },
      contents: function (elem) {
        if (nodeName(elem, "iframe")) {
          return elem.contentDocument;
        } // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only
        // Treat the template element as a regular one in browsers that
        // don't support it.


        if (nodeName(elem, "template")) {
          elem = elem.content || elem;
        }

        return jQuery.merge([], elem.childNodes);
      }
    }, function (name, fn) {
      jQuery.fn[name] = function (until, selector) {
        var matched = jQuery.map(this, fn, until);

        if (name.slice(-5) !== "Until") {
          selector = until;
        }

        if (selector && typeof selector === "string") {
          matched = jQuery.filter(selector, matched);
        }

        if (this.length > 1) {
          // Remove duplicates
          if (!guaranteedUnique[name]) {
            jQuery.uniqueSort(matched);
          } // Reverse order for parents* and prev-derivatives


          if (rparentsprev.test(name)) {
            matched.reverse();
          }
        }

        return this.pushStack(matched);
      };
    });
    var rnothtmlwhite = /[^\x20\t\r\n\f]+/g; // Convert String-formatted options into Object-formatted ones

    function createOptions(options) {
      var object = {};
      jQuery.each(options.match(rnothtmlwhite) || [], function (_, flag) {
        object[flag] = true;
      });
      return object;
    }
    /*
     * Create a callback list using the following parameters:
     *
     *	options: an optional list of space-separated options that will change how
     *			the callback list behaves or a more traditional option object
     *
     * By default a callback list will act like an event callback list and can be
     * "fired" multiple times.
     *
     * Possible options:
     *
     *	once:			will ensure the callback list can only be fired once (like a Deferred)
     *
     *	memory:			will keep track of previous values and will call any callback added
     *					after the list has been fired right away with the latest "memorized"
     *					values (like a Deferred)
     *
     *	unique:			will ensure a callback can only be added once (no duplicate in the list)
     *
     *	stopOnFalse:	interrupt callings when a callback returns false
     *
     */


    jQuery.Callbacks = function (options) {
      // Convert options from String-formatted to Object-formatted if needed
      // (we check in cache first)
      options = typeof options === "string" ? createOptions(options) : jQuery.extend({}, options);

      var // Flag to know if list is currently firing
      firing,
          // Last fire value for non-forgettable lists
      memory,
          // Flag to know if list was already fired
      fired,
          // Flag to prevent firing
      locked,
          // Actual callback list
      list = [],
          // Queue of execution data for repeatable lists
      queue = [],
          // Index of currently firing callback (modified by add/remove as needed)
      firingIndex = -1,
          // Fire callbacks
      fire = function () {
        // Enforce single-firing
        locked = locked || options.once; // Execute callbacks for all pending executions,
        // respecting firingIndex overrides and runtime changes

        fired = firing = true;

        for (; queue.length; firingIndex = -1) {
          memory = queue.shift();

          while (++firingIndex < list.length) {
            // Run callback and check for early termination
            if (list[firingIndex].apply(memory[0], memory[1]) === false && options.stopOnFalse) {
              // Jump to end and forget the data so .add doesn't re-fire
              firingIndex = list.length;
              memory = false;
            }
          }
        } // Forget the data if we're done with it


        if (!options.memory) {
          memory = false;
        }

        firing = false; // Clean up if we're done firing for good

        if (locked) {
          // Keep an empty list if we have data for future add calls
          if (memory) {
            list = []; // Otherwise, this object is spent
          } else {
            list = "";
          }
        }
      },
          // Actual Callbacks object
      self = {
        // Add a callback or a collection of callbacks to the list
        add: function () {
          if (list) {
            // If we have memory from a past run, we should fire after adding
            if (memory && !firing) {
              firingIndex = list.length - 1;
              queue.push(memory);
            }

            (function add(args) {
              jQuery.each(args, function (_, arg) {
                if (isFunction(arg)) {
                  if (!options.unique || !self.has(arg)) {
                    list.push(arg);
                  }
                } else if (arg && arg.length && toType(arg) !== "string") {
                  // Inspect recursively
                  add(arg);
                }
              });
            })(arguments);

            if (memory && !firing) {
              fire();
            }
          }

          return this;
        },
        // Remove a callback from the list
        remove: function () {
          jQuery.each(arguments, function (_, arg) {
            var index;

            while ((index = jQuery.inArray(arg, list, index)) > -1) {
              list.splice(index, 1); // Handle firing indexes

              if (index <= firingIndex) {
                firingIndex--;
              }
            }
          });
          return this;
        },
        // Check if a given callback is in the list.
        // If no argument is given, return whether or not list has callbacks attached.
        has: function (fn) {
          return fn ? jQuery.inArray(fn, list) > -1 : list.length > 0;
        },
        // Remove all callbacks from the list
        empty: function () {
          if (list) {
            list = [];
          }

          return this;
        },
        // Disable .fire and .add
        // Abort any current/pending executions
        // Clear all callbacks and values
        disable: function () {
          locked = queue = [];
          list = memory = "";
          return this;
        },
        disabled: function () {
          return !list;
        },
        // Disable .fire
        // Also disable .add unless we have memory (since it would have no effect)
        // Abort any pending executions
        lock: function () {
          locked = queue = [];

          if (!memory && !firing) {
            list = memory = "";
          }

          return this;
        },
        locked: function () {
          return !!locked;
        },
        // Call all callbacks with the given context and arguments
        fireWith: function (context, args) {
          if (!locked) {
            args = args || [];
            args = [context, args.slice ? args.slice() : args];
            queue.push(args);

            if (!firing) {
              fire();
            }
          }

          return this;
        },
        // Call all the callbacks with the given arguments
        fire: function () {
          self.fireWith(this, arguments);
          return this;
        },
        // To know if the callbacks have already been called at least once
        fired: function () {
          return !!fired;
        }
      };

      return self;
    };

    function Identity(v) {
      return v;
    }

    function Thrower(ex) {
      throw ex;
    }

    function adoptValue(value, resolve, reject, noValue) {
      var method;

      try {
        // Check for promise aspect first to privilege synchronous behavior
        if (value && isFunction(method = value.promise)) {
          method.call(value).done(resolve).fail(reject); // Other thenables
        } else if (value && isFunction(method = value.then)) {
          method.call(value, resolve, reject); // Other non-thenables
        } else {
          // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
          // * false: [ value ].slice( 0 ) => resolve( value )
          // * true: [ value ].slice( 1 ) => resolve()
          resolve.apply(undefined, [value].slice(noValue));
        } // For Promises/A+, convert exceptions into rejections
        // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
        // Deferred#then to conditionally suppress rejection.

      } catch (value) {
        // Support: Android 4.0 only
        // Strict mode functions invoked without .call/.apply get global-object context
        reject.apply(undefined, [value]);
      }
    }

    jQuery.extend({
      Deferred: function (func) {
        var tuples = [// action, add listener, callbacks,
        // ... .then handlers, argument index, [final state]
        ["notify", "progress", jQuery.Callbacks("memory"), jQuery.Callbacks("memory"), 2], ["resolve", "done", jQuery.Callbacks("once memory"), jQuery.Callbacks("once memory"), 0, "resolved"], ["reject", "fail", jQuery.Callbacks("once memory"), jQuery.Callbacks("once memory"), 1, "rejected"]],
            state = "pending",
            promise = {
          state: function () {
            return state;
          },
          always: function () {
            deferred.done(arguments).fail(arguments);
            return this;
          },
          "catch": function (fn) {
            return promise.then(null, fn);
          },
          // Keep pipe for back-compat
          pipe: function
            /* fnDone, fnFail, fnProgress */
          () {
            var fns = arguments;
            return jQuery.Deferred(function (newDefer) {
              jQuery.each(tuples, function (i, tuple) {
                // Map tuples (progress, done, fail) to arguments (done, fail, progress)
                var fn = isFunction(fns[tuple[4]]) && fns[tuple[4]]; // deferred.progress(function() { bind to newDefer or newDefer.notify })
                // deferred.done(function() { bind to newDefer or newDefer.resolve })
                // deferred.fail(function() { bind to newDefer or newDefer.reject })

                deferred[tuple[1]](function () {
                  var returned = fn && fn.apply(this, arguments);

                  if (returned && isFunction(returned.promise)) {
                    returned.promise().progress(newDefer.notify).done(newDefer.resolve).fail(newDefer.reject);
                  } else {
                    newDefer[tuple[0] + "With"](this, fn ? [returned] : arguments);
                  }
                });
              });
              fns = null;
            }).promise();
          },
          then: function (onFulfilled, onRejected, onProgress) {
            var maxDepth = 0;

            function resolve(depth, deferred, handler, special) {
              return function () {
                var that = this,
                    args = arguments,
                    mightThrow = function () {
                  var returned, then; // Support: Promises/A+ section 2.3.3.3.3
                  // https://promisesaplus.com/#point-59
                  // Ignore double-resolution attempts

                  if (depth < maxDepth) {
                    return;
                  }

                  returned = handler.apply(that, args); // Support: Promises/A+ section 2.3.1
                  // https://promisesaplus.com/#point-48

                  if (returned === deferred.promise()) {
                    throw new TypeError("Thenable self-resolution");
                  } // Support: Promises/A+ sections 2.3.3.1, 3.5
                  // https://promisesaplus.com/#point-54
                  // https://promisesaplus.com/#point-75
                  // Retrieve `then` only once


                  then = returned && ( // Support: Promises/A+ section 2.3.4
                  // https://promisesaplus.com/#point-64
                  // Only check objects and functions for thenability
                  typeof returned === "object" || typeof returned === "function") && returned.then; // Handle a returned thenable

                  if (isFunction(then)) {
                    // Special processors (notify) just wait for resolution
                    if (special) {
                      then.call(returned, resolve(maxDepth, deferred, Identity, special), resolve(maxDepth, deferred, Thrower, special)); // Normal processors (resolve) also hook into progress
                    } else {
                      // ...and disregard older resolution values
                      maxDepth++;
                      then.call(returned, resolve(maxDepth, deferred, Identity, special), resolve(maxDepth, deferred, Thrower, special), resolve(maxDepth, deferred, Identity, deferred.notifyWith));
                    } // Handle all other returned values

                  } else {
                    // Only substitute handlers pass on context
                    // and multiple values (non-spec behavior)
                    if (handler !== Identity) {
                      that = undefined;
                      args = [returned];
                    } // Process the value(s)
                    // Default process is resolve


                    (special || deferred.resolveWith)(that, args);
                  }
                },
                    // Only normal processors (resolve) catch and reject exceptions
                process = special ? mightThrow : function () {
                  try {
                    mightThrow();
                  } catch (e) {
                    if (jQuery.Deferred.exceptionHook) {
                      jQuery.Deferred.exceptionHook(e, process.stackTrace);
                    } // Support: Promises/A+ section 2.3.3.3.4.1
                    // https://promisesaplus.com/#point-61
                    // Ignore post-resolution exceptions


                    if (depth + 1 >= maxDepth) {
                      // Only substitute handlers pass on context
                      // and multiple values (non-spec behavior)
                      if (handler !== Thrower) {
                        that = undefined;
                        args = [e];
                      }

                      deferred.rejectWith(that, args);
                    }
                  }
                }; // Support: Promises/A+ section 2.3.3.3.1
                // https://promisesaplus.com/#point-57
                // Re-resolve promises immediately to dodge false rejection from
                // subsequent errors


                if (depth) {
                  process();
                } else {
                  // Call an optional hook to record the stack, in case of exception
                  // since it's otherwise lost when execution goes async
                  if (jQuery.Deferred.getStackHook) {
                    process.stackTrace = jQuery.Deferred.getStackHook();
                  }

                  window.setTimeout(process);
                }
              };
            }

            return jQuery.Deferred(function (newDefer) {
              // progress_handlers.add( ... )
              tuples[0][3].add(resolve(0, newDefer, isFunction(onProgress) ? onProgress : Identity, newDefer.notifyWith)); // fulfilled_handlers.add( ... )

              tuples[1][3].add(resolve(0, newDefer, isFunction(onFulfilled) ? onFulfilled : Identity)); // rejected_handlers.add( ... )

              tuples[2][3].add(resolve(0, newDefer, isFunction(onRejected) ? onRejected : Thrower));
            }).promise();
          },
          // Get a promise for this deferred
          // If obj is provided, the promise aspect is added to the object
          promise: function (obj) {
            return obj != null ? jQuery.extend(obj, promise) : promise;
          }
        },
            deferred = {}; // Add list-specific methods

        jQuery.each(tuples, function (i, tuple) {
          var list = tuple[2],
              stateString = tuple[5]; // promise.progress = list.add
          // promise.done = list.add
          // promise.fail = list.add

          promise[tuple[1]] = list.add; // Handle state

          if (stateString) {
            list.add(function () {
              // state = "resolved" (i.e., fulfilled)
              // state = "rejected"
              state = stateString;
            }, // rejected_callbacks.disable
            // fulfilled_callbacks.disable
            tuples[3 - i][2].disable, // rejected_handlers.disable
            // fulfilled_handlers.disable
            tuples[3 - i][3].disable, // progress_callbacks.lock
            tuples[0][2].lock, // progress_handlers.lock
            tuples[0][3].lock);
          } // progress_handlers.fire
          // fulfilled_handlers.fire
          // rejected_handlers.fire


          list.add(tuple[3].fire); // deferred.notify = function() { deferred.notifyWith(...) }
          // deferred.resolve = function() { deferred.resolveWith(...) }
          // deferred.reject = function() { deferred.rejectWith(...) }

          deferred[tuple[0]] = function () {
            deferred[tuple[0] + "With"](this === deferred ? undefined : this, arguments);
            return this;
          }; // deferred.notifyWith = list.fireWith
          // deferred.resolveWith = list.fireWith
          // deferred.rejectWith = list.fireWith


          deferred[tuple[0] + "With"] = list.fireWith;
        }); // Make the deferred a promise

        promise.promise(deferred); // Call given func if any

        if (func) {
          func.call(deferred, deferred);
        } // All done!


        return deferred;
      },
      // Deferred helper
      when: function (singleValue) {
        var // count of uncompleted subordinates
        remaining = arguments.length,
            // count of unprocessed arguments
        i = remaining,
            // subordinate fulfillment data
        resolveContexts = Array(i),
            resolveValues = slice.call(arguments),
            // the master Deferred
        master = jQuery.Deferred(),
            // subordinate callback factory
        updateFunc = function (i) {
          return function (value) {
            resolveContexts[i] = this;
            resolveValues[i] = arguments.length > 1 ? slice.call(arguments) : value;

            if (! --remaining) {
              master.resolveWith(resolveContexts, resolveValues);
            }
          };
        }; // Single- and empty arguments are adopted like Promise.resolve


        if (remaining <= 1) {
          adoptValue(singleValue, master.done(updateFunc(i)).resolve, master.reject, !remaining); // Use .then() to unwrap secondary thenables (cf. gh-3000)

          if (master.state() === "pending" || isFunction(resolveValues[i] && resolveValues[i].then)) {
            return master.then();
          }
        } // Multiple arguments are aggregated like Promise.all array elements


        while (i--) {
          adoptValue(resolveValues[i], updateFunc(i), master.reject);
        }

        return master.promise();
      }
    }); // These usually indicate a programmer mistake during development,
    // warn about them ASAP rather than swallowing them by default.

    var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;

    jQuery.Deferred.exceptionHook = function (error, stack) {
      // Support: IE 8 - 9 only
      // Console exists when dev tools are open, which can happen at any time
      if (window.console && window.console.warn && error && rerrorNames.test(error.name)) {
        window.console.warn("jQuery.Deferred exception: " + error.message, error.stack, stack);
      }
    };

    jQuery.readyException = function (error) {
      window.setTimeout(function () {
        throw error;
      });
    }; // The deferred used on DOM ready


    var readyList = jQuery.Deferred();

    jQuery.fn.ready = function (fn) {
      readyList.then(fn) // Wrap jQuery.readyException in a function so that the lookup
      // happens at the time of error handling instead of callback
      // registration.
      .catch(function (error) {
        jQuery.readyException(error);
      });
      return this;
    };

    jQuery.extend({
      // Is the DOM ready to be used? Set to true once it occurs.
      isReady: false,
      // A counter to track how many items to wait for before
      // the ready event fires. See #6781
      readyWait: 1,
      // Handle when the DOM is ready
      ready: function (wait) {
        // Abort if there are pending holds or we're already ready
        if (wait === true ? --jQuery.readyWait : jQuery.isReady) {
          return;
        } // Remember that the DOM is ready


        jQuery.isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be

        if (wait !== true && --jQuery.readyWait > 0) {
          return;
        } // If there are functions bound, to execute


        readyList.resolveWith(document$2, [jQuery]);
      }
    });
    jQuery.ready.then = readyList.then; // The ready event handler and self cleanup method

    function completed() {
      document$2.removeEventListener("DOMContentLoaded", completed);
      window.removeEventListener("load", completed);
      jQuery.ready();
    } // Catch cases where $(document).ready() is called
    // after the browser event has already occurred.
    // Support: IE <=9 - 10 only
    // Older IE sometimes signals "interactive" too soon


    if (document$2.readyState === "complete" || document$2.readyState !== "loading" && !document$2.documentElement.doScroll) {
      // Handle it asynchronously to allow scripts the opportunity to delay ready
      window.setTimeout(jQuery.ready);
    } else {
      // Use the handy event callback
      document$2.addEventListener("DOMContentLoaded", completed); // A fallback to window.onload, that will always work

      window.addEventListener("load", completed);
    } // Multifunctional method to get and set values of a collection
    // The value/s can optionally be executed if it's a function


    var access = function (elems, fn, key, value, chainable, emptyGet, raw) {
      var i = 0,
          len = elems.length,
          bulk = key == null; // Sets many values

      if (toType(key) === "object") {
        chainable = true;

        for (i in key) {
          access(elems, fn, i, key[i], true, emptyGet, raw);
        } // Sets one value

      } else if (value !== undefined) {
        chainable = true;

        if (!isFunction(value)) {
          raw = true;
        }

        if (bulk) {
          // Bulk operations run against the entire set
          if (raw) {
            fn.call(elems, value);
            fn = null; // ...except when executing function values
          } else {
            bulk = fn;

            fn = function (elem, key, value) {
              return bulk.call(jQuery(elem), value);
            };
          }
        }

        if (fn) {
          for (; i < len; i++) {
            fn(elems[i], key, raw ? value : value.call(elems[i], i, fn(elems[i], key)));
          }
        }
      }

      if (chainable) {
        return elems;
      } // Gets


      if (bulk) {
        return fn.call(elems);
      }

      return len ? fn(elems[0], key) : emptyGet;
    }; // Matches dashed string for camelizing


    var rmsPrefix = /^-ms-/,
        rdashAlpha = /-([a-z])/g; // Used by camelCase as callback to replace()

    function fcamelCase(all, letter) {
      return letter.toUpperCase();
    } // Convert dashed to camelCase; used by the css and data modules
    // Support: IE <=9 - 11, Edge 12 - 15
    // Microsoft forgot to hump their vendor prefix (#9572)


    function camelCase(string) {
      return string.replace(rmsPrefix, "ms-").replace(rdashAlpha, fcamelCase);
    }

    var acceptData = function (owner) {
      // Accepts only:
      //  - Node
      //    - Node.ELEMENT_NODE
      //    - Node.DOCUMENT_NODE
      //  - Object
      //    - Any
      return owner.nodeType === 1 || owner.nodeType === 9 || !+owner.nodeType;
    };

    function Data() {
      this.expando = jQuery.expando + Data.uid++;
    }

    Data.uid = 1;
    Data.prototype = {
      cache: function (owner) {
        // Check if the owner object already has a cache
        var value = owner[this.expando]; // If not, create one

        if (!value) {
          value = {}; // We can accept data for non-element nodes in modern browsers,
          // but we should not, see #8335.
          // Always return an empty object.

          if (acceptData(owner)) {
            // If it is a node unlikely to be stringify-ed or looped over
            // use plain assignment
            if (owner.nodeType) {
              owner[this.expando] = value; // Otherwise secure it in a non-enumerable property
              // configurable must be true to allow the property to be
              // deleted when data is removed
            } else {
              Object.defineProperty(owner, this.expando, {
                value: value,
                configurable: true
              });
            }
          }
        }

        return value;
      },
      set: function (owner, data, value) {
        var prop,
            cache = this.cache(owner); // Handle: [ owner, key, value ] args
        // Always use camelCase key (gh-2257)

        if (typeof data === "string") {
          cache[camelCase(data)] = value; // Handle: [ owner, { properties } ] args
        } else {
          // Copy the properties one-by-one to the cache object
          for (prop in data) {
            cache[camelCase(prop)] = data[prop];
          }
        }

        return cache;
      },
      get: function (owner, key) {
        return key === undefined ? this.cache(owner) : // Always use camelCase key (gh-2257)
        owner[this.expando] && owner[this.expando][camelCase(key)];
      },
      access: function (owner, key, value) {
        // In cases where either:
        //
        //   1. No key was specified
        //   2. A string key was specified, but no value provided
        //
        // Take the "read" path and allow the get method to determine
        // which value to return, respectively either:
        //
        //   1. The entire cache object
        //   2. The data stored at the key
        //
        if (key === undefined || key && typeof key === "string" && value === undefined) {
          return this.get(owner, key);
        } // When the key is not a string, or both a key and value
        // are specified, set or extend (existing objects) with either:
        //
        //   1. An object of properties
        //   2. A key and value
        //


        this.set(owner, key, value); // Since the "set" path can have two possible entry points
        // return the expected data based on which path was taken[*]

        return value !== undefined ? value : key;
      },
      remove: function (owner, key) {
        var i,
            cache = owner[this.expando];

        if (cache === undefined) {
          return;
        }

        if (key !== undefined) {
          // Support array or space separated string of keys
          if (Array.isArray(key)) {
            // If key is an array of keys...
            // We always set camelCase keys, so remove that.
            key = key.map(camelCase);
          } else {
            key = camelCase(key); // If a key with the spaces exists, use it.
            // Otherwise, create an array by matching non-whitespace

            key = key in cache ? [key] : key.match(rnothtmlwhite) || [];
          }

          i = key.length;

          while (i--) {
            delete cache[key[i]];
          }
        } // Remove the expando if there's no more data


        if (key === undefined || jQuery.isEmptyObject(cache)) {
          // Support: Chrome <=35 - 45
          // Webkit & Blink performance suffers when deleting properties
          // from DOM nodes, so set to undefined instead
          // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
          if (owner.nodeType) {
            owner[this.expando] = undefined;
          } else {
            delete owner[this.expando];
          }
        }
      },
      hasData: function (owner) {
        var cache = owner[this.expando];
        return cache !== undefined && !jQuery.isEmptyObject(cache);
      }
    };
    var dataPriv = new Data();
    var dataUser = new Data(); //	Implementation Summary
    //
    //	1. Enforce API surface and semantic compatibility with 1.9.x branch
    //	2. Improve the module's maintainability by reducing the storage
    //		paths to a single mechanism.
    //	3. Use the same single mechanism to support "private" and "user" data.
    //	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
    //	5. Avoid exposing implementation details on user objects (eg. expando properties)
    //	6. Provide a clear path for implementation upgrade to WeakMap in 2014

    var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
        rmultiDash = /[A-Z]/g;

    function getData(data) {
      if (data === "true") {
        return true;
      }

      if (data === "false") {
        return false;
      }

      if (data === "null") {
        return null;
      } // Only convert to a number if it doesn't change the string


      if (data === +data + "") {
        return +data;
      }

      if (rbrace.test(data)) {
        return JSON.parse(data);
      }

      return data;
    }

    function dataAttr(elem, key, data) {
      var name; // If nothing was found internally, try to fetch any
      // data from the HTML5 data-* attribute

      if (data === undefined && elem.nodeType === 1) {
        name = "data-" + key.replace(rmultiDash, "-$&").toLowerCase();
        data = elem.getAttribute(name);

        if (typeof data === "string") {
          try {
            data = getData(data);
          } catch (e) {} // Make sure we set the data so it isn't changed later


          dataUser.set(elem, key, data);
        } else {
          data = undefined;
        }
      }

      return data;
    }

    jQuery.extend({
      hasData: function (elem) {
        return dataUser.hasData(elem) || dataPriv.hasData(elem);
      },
      data: function (elem, name, data) {
        return dataUser.access(elem, name, data);
      },
      removeData: function (elem, name) {
        dataUser.remove(elem, name);
      },
      // TODO: Now that all calls to _data and _removeData have been replaced
      // with direct calls to dataPriv methods, these can be deprecated.
      _data: function (elem, name, data) {
        return dataPriv.access(elem, name, data);
      },
      _removeData: function (elem, name) {
        dataPriv.remove(elem, name);
      }
    });
    jQuery.fn.extend({
      data: function (key, value) {
        var i,
            name,
            data,
            elem = this[0],
            attrs = elem && elem.attributes; // Gets all values

        if (key === undefined) {
          if (this.length) {
            data = dataUser.get(elem);

            if (elem.nodeType === 1 && !dataPriv.get(elem, "hasDataAttrs")) {
              i = attrs.length;

              while (i--) {
                // Support: IE 11 only
                // The attrs elements can be null (#14894)
                if (attrs[i]) {
                  name = attrs[i].name;

                  if (name.indexOf("data-") === 0) {
                    name = camelCase(name.slice(5));
                    dataAttr(elem, name, data[name]);
                  }
                }
              }

              dataPriv.set(elem, "hasDataAttrs", true);
            }
          }

          return data;
        } // Sets multiple values


        if (typeof key === "object") {
          return this.each(function () {
            dataUser.set(this, key);
          });
        }

        return access(this, function (value) {
          var data; // The calling jQuery object (element matches) is not empty
          // (and therefore has an element appears at this[ 0 ]) and the
          // `value` parameter was not undefined. An empty jQuery object
          // will result in `undefined` for elem = this[ 0 ] which will
          // throw an exception if an attempt to read a data cache is made.

          if (elem && value === undefined) {
            // Attempt to get data from the cache
            // The key will always be camelCased in Data
            data = dataUser.get(elem, key);

            if (data !== undefined) {
              return data;
            } // Attempt to "discover" the data in
            // HTML5 custom data-* attrs


            data = dataAttr(elem, key);

            if (data !== undefined) {
              return data;
            } // We tried really hard, but the data doesn't exist.


            return;
          } // Set the data...


          this.each(function () {
            // We always store the camelCased key
            dataUser.set(this, key, value);
          });
        }, null, value, arguments.length > 1, null, true);
      },
      removeData: function (key) {
        return this.each(function () {
          dataUser.remove(this, key);
        });
      }
    });
    jQuery.extend({
      queue: function (elem, type, data) {
        var queue;

        if (elem) {
          type = (type || "fx") + "queue";
          queue = dataPriv.get(elem, type); // Speed up dequeue by getting out quickly if this is just a lookup

          if (data) {
            if (!queue || Array.isArray(data)) {
              queue = dataPriv.access(elem, type, jQuery.makeArray(data));
            } else {
              queue.push(data);
            }
          }

          return queue || [];
        }
      },
      dequeue: function (elem, type) {
        type = type || "fx";

        var queue = jQuery.queue(elem, type),
            startLength = queue.length,
            fn = queue.shift(),
            hooks = jQuery._queueHooks(elem, type),
            next = function () {
          jQuery.dequeue(elem, type);
        }; // If the fx queue is dequeued, always remove the progress sentinel


        if (fn === "inprogress") {
          fn = queue.shift();
          startLength--;
        }

        if (fn) {
          // Add a progress sentinel to prevent the fx queue from being
          // automatically dequeued
          if (type === "fx") {
            queue.unshift("inprogress");
          } // Clear up the last queue stop function


          delete hooks.stop;
          fn.call(elem, next, hooks);
        }

        if (!startLength && hooks) {
          hooks.empty.fire();
        }
      },
      // Not public - generate a queueHooks object, or return the current one
      _queueHooks: function (elem, type) {
        var key = type + "queueHooks";
        return dataPriv.get(elem, key) || dataPriv.access(elem, key, {
          empty: jQuery.Callbacks("once memory").add(function () {
            dataPriv.remove(elem, [type + "queue", key]);
          })
        });
      }
    });
    jQuery.fn.extend({
      queue: function (type, data) {
        var setter = 2;

        if (typeof type !== "string") {
          data = type;
          type = "fx";
          setter--;
        }

        if (arguments.length < setter) {
          return jQuery.queue(this[0], type);
        }

        return data === undefined ? this : this.each(function () {
          var queue = jQuery.queue(this, type, data); // Ensure a hooks for this queue

          jQuery._queueHooks(this, type);

          if (type === "fx" && queue[0] !== "inprogress") {
            jQuery.dequeue(this, type);
          }
        });
      },
      dequeue: function (type) {
        return this.each(function () {
          jQuery.dequeue(this, type);
        });
      },
      clearQueue: function (type) {
        return this.queue(type || "fx", []);
      },
      // Get a promise resolved when queues of a certain type
      // are emptied (fx is the type by default)
      promise: function (type, obj) {
        var tmp,
            count = 1,
            defer = jQuery.Deferred(),
            elements = this,
            i = this.length,
            resolve = function () {
          if (! --count) {
            defer.resolveWith(elements, [elements]);
          }
        };

        if (typeof type !== "string") {
          obj = type;
          type = undefined;
        }

        type = type || "fx";

        while (i--) {
          tmp = dataPriv.get(elements[i], type + "queueHooks");

          if (tmp && tmp.empty) {
            count++;
            tmp.empty.add(resolve);
          }
        }

        resolve();
        return defer.promise(obj);
      }
    });
    var pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source;
    var rcssNum = new RegExp("^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i");
    var cssExpand = ["Top", "Right", "Bottom", "Left"];

    var isHiddenWithinTree = function (elem, el) {
      // isHiddenWithinTree might be called from jQuery#filter function;
      // in that case, element will be second argument
      elem = el || elem; // Inline style trumps all

      return elem.style.display === "none" || elem.style.display === "" && // Otherwise, check computed style
      // Support: Firefox <=43 - 45
      // Disconnected elements can have computed display: none, so first confirm that elem is
      // in the document.
      jQuery.contains(elem.ownerDocument, elem) && jQuery.css(elem, "display") === "none";
    };

    var swap = function (elem, options, callback, args) {
      var ret,
          name,
          old = {}; // Remember the old values, and insert the new ones

      for (name in options) {
        old[name] = elem.style[name];
        elem.style[name] = options[name];
      }

      ret = callback.apply(elem, args || []); // Revert the old values

      for (name in options) {
        elem.style[name] = old[name];
      }

      return ret;
    };

    function adjustCSS(elem, prop, valueParts, tween) {
      var adjusted,
          scale,
          maxIterations = 20,
          currentValue = tween ? function () {
        return tween.cur();
      } : function () {
        return jQuery.css(elem, prop, "");
      },
          initial = currentValue(),
          unit = valueParts && valueParts[3] || (jQuery.cssNumber[prop] ? "" : "px"),
          // Starting value computation is required for potential unit mismatches
      initialInUnit = (jQuery.cssNumber[prop] || unit !== "px" && +initial) && rcssNum.exec(jQuery.css(elem, prop));

      if (initialInUnit && initialInUnit[3] !== unit) {
        // Support: Firefox <=54
        // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)
        initial = initial / 2; // Trust units reported by jQuery.css

        unit = unit || initialInUnit[3]; // Iteratively approximate from a nonzero starting point

        initialInUnit = +initial || 1;

        while (maxIterations--) {
          // Evaluate and update our best guess (doubling guesses that zero out).
          // Finish if the scale equals or crosses 1 (making the old*new product non-positive).
          jQuery.style(elem, prop, initialInUnit + unit);

          if ((1 - scale) * (1 - (scale = currentValue() / initial || 0.5)) <= 0) {
            maxIterations = 0;
          }

          initialInUnit = initialInUnit / scale;
        }

        initialInUnit = initialInUnit * 2;
        jQuery.style(elem, prop, initialInUnit + unit); // Make sure we update the tween properties later on

        valueParts = valueParts || [];
      }

      if (valueParts) {
        initialInUnit = +initialInUnit || +initial || 0; // Apply relative offset (+=/-=) if specified

        adjusted = valueParts[1] ? initialInUnit + (valueParts[1] + 1) * valueParts[2] : +valueParts[2];

        if (tween) {
          tween.unit = unit;
          tween.start = initialInUnit;
          tween.end = adjusted;
        }
      }

      return adjusted;
    }

    var defaultDisplayMap = {};

    function getDefaultDisplay(elem) {
      var temp,
          doc = elem.ownerDocument,
          nodeName = elem.nodeName,
          display = defaultDisplayMap[nodeName];

      if (display) {
        return display;
      }

      temp = doc.body.appendChild(doc.createElement(nodeName));
      display = jQuery.css(temp, "display");
      temp.parentNode.removeChild(temp);

      if (display === "none") {
        display = "block";
      }

      defaultDisplayMap[nodeName] = display;
      return display;
    }

    function showHide(elements, show) {
      var display,
          elem,
          values = [],
          index = 0,
          length = elements.length; // Determine new display value for elements that need to change

      for (; index < length; index++) {
        elem = elements[index];

        if (!elem.style) {
          continue;
        }

        display = elem.style.display;

        if (show) {
          // Since we force visibility upon cascade-hidden elements, an immediate (and slow)
          // check is required in this first loop unless we have a nonempty display value (either
          // inline or about-to-be-restored)
          if (display === "none") {
            values[index] = dataPriv.get(elem, "display") || null;

            if (!values[index]) {
              elem.style.display = "";
            }
          }

          if (elem.style.display === "" && isHiddenWithinTree(elem)) {
            values[index] = getDefaultDisplay(elem);
          }
        } else {
          if (display !== "none") {
            values[index] = "none"; // Remember what we're overwriting

            dataPriv.set(elem, "display", display);
          }
        }
      } // Set the display of the elements in a second loop to avoid constant reflow


      for (index = 0; index < length; index++) {
        if (values[index] != null) {
          elements[index].style.display = values[index];
        }
      }

      return elements;
    }

    jQuery.fn.extend({
      show: function () {
        return showHide(this, true);
      },
      hide: function () {
        return showHide(this);
      },
      toggle: function (state) {
        if (typeof state === "boolean") {
          return state ? this.show() : this.hide();
        }

        return this.each(function () {
          if (isHiddenWithinTree(this)) {
            jQuery(this).show();
          } else {
            jQuery(this).hide();
          }
        });
      }
    });
    var rcheckableType = /^(?:checkbox|radio)$/i;
    var rtagName = /<([a-z][^\/\0>\x20\t\r\n\f]+)/i;
    var rscriptType = /^$|^module$|\/(?:java|ecma)script/i; // We have to close these tags to support XHTML (#13200)

    var wrapMap = {
      // Support: IE <=9 only
      option: [1, "<select multiple='multiple'>", "</select>"],
      // XHTML parsers do not magically insert elements in the
      // same way that tag soup parsers do. So we cannot shorten
      // this by omitting <tbody> or other required elements.
      thead: [1, "<table>", "</table>"],
      col: [2, "<table><colgroup>", "</colgroup></table>"],
      tr: [2, "<table><tbody>", "</tbody></table>"],
      td: [3, "<table><tbody><tr>", "</tr></tbody></table>"],
      _default: [0, "", ""]
    }; // Support: IE <=9 only

    wrapMap.optgroup = wrapMap.option;
    wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
    wrapMap.th = wrapMap.td;

    function getAll(context, tag) {
      // Support: IE <=9 - 11 only
      // Use typeof to avoid zero-argument method invocation on host objects (#15151)
      var ret;

      if (typeof context.getElementsByTagName !== "undefined") {
        ret = context.getElementsByTagName(tag || "*");
      } else if (typeof context.querySelectorAll !== "undefined") {
        ret = context.querySelectorAll(tag || "*");
      } else {
        ret = [];
      }

      if (tag === undefined || tag && nodeName(context, tag)) {
        return jQuery.merge([context], ret);
      }

      return ret;
    } // Mark scripts as having already been evaluated


    function setGlobalEval(elems, refElements) {
      var i = 0,
          l = elems.length;

      for (; i < l; i++) {
        dataPriv.set(elems[i], "globalEval", !refElements || dataPriv.get(refElements[i], "globalEval"));
      }
    }

    var rhtml = /<|&#?\w+;/;

    function buildFragment(elems, context, scripts, selection, ignored) {
      var elem,
          tmp,
          tag,
          wrap,
          contains,
          j,
          fragment = context.createDocumentFragment(),
          nodes = [],
          i = 0,
          l = elems.length;

      for (; i < l; i++) {
        elem = elems[i];

        if (elem || elem === 0) {
          // Add nodes directly
          if (toType(elem) === "object") {
            // Support: Android <=4.0 only, PhantomJS 1 only
            // push.apply(_, arraylike) throws on ancient WebKit
            jQuery.merge(nodes, elem.nodeType ? [elem] : elem); // Convert non-html into a text node
          } else if (!rhtml.test(elem)) {
            nodes.push(context.createTextNode(elem)); // Convert html into DOM nodes
          } else {
            tmp = tmp || fragment.appendChild(context.createElement("div")); // Deserialize a standard representation

            tag = (rtagName.exec(elem) || ["", ""])[1].toLowerCase();
            wrap = wrapMap[tag] || wrapMap._default;
            tmp.innerHTML = wrap[1] + jQuery.htmlPrefilter(elem) + wrap[2]; // Descend through wrappers to the right content

            j = wrap[0];

            while (j--) {
              tmp = tmp.lastChild;
            } // Support: Android <=4.0 only, PhantomJS 1 only
            // push.apply(_, arraylike) throws on ancient WebKit


            jQuery.merge(nodes, tmp.childNodes); // Remember the top-level container

            tmp = fragment.firstChild; // Ensure the created nodes are orphaned (#12392)

            tmp.textContent = "";
          }
        }
      } // Remove wrapper from fragment


      fragment.textContent = "";
      i = 0;

      while (elem = nodes[i++]) {
        // Skip elements already in the context collection (trac-4087)
        if (selection && jQuery.inArray(elem, selection) > -1) {
          if (ignored) {
            ignored.push(elem);
          }

          continue;
        }

        contains = jQuery.contains(elem.ownerDocument, elem); // Append to fragment

        tmp = getAll(fragment.appendChild(elem), "script"); // Preserve script evaluation history

        if (contains) {
          setGlobalEval(tmp);
        } // Capture executables


        if (scripts) {
          j = 0;

          while (elem = tmp[j++]) {
            if (rscriptType.test(elem.type || "")) {
              scripts.push(elem);
            }
          }
        }
      }

      return fragment;
    }

    (function () {
      var fragment = document$2.createDocumentFragment(),
          div = fragment.appendChild(document$2.createElement("div")),
          input = document$2.createElement("input"); // Support: Android 4.0 - 4.3 only
      // Check state lost if the name is set (#11217)
      // Support: Windows Web Apps (WWA)
      // `name` and `type` must use .setAttribute for WWA (#14901)

      input.setAttribute("type", "radio");
      input.setAttribute("checked", "checked");
      input.setAttribute("name", "t");
      div.appendChild(input); // Support: Android <=4.1 only
      // Older WebKit doesn't clone checked state correctly in fragments

      support.checkClone = div.cloneNode(true).cloneNode(true).lastChild.checked; // Support: IE <=11 only
      // Make sure textarea (and checkbox) defaultValue is properly cloned

      div.innerHTML = "<textarea>x</textarea>";
      support.noCloneChecked = !!div.cloneNode(true).lastChild.defaultValue;
    })();

    var documentElement = document$2.documentElement;
    var rkeyEvent = /^key/,
        rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
        rtypenamespace = /^([^.]*)(?:\.(.+)|)/;

    function returnTrue() {
      return true;
    }

    function returnFalse() {
      return false;
    } // Support: IE <=9 only
    // See #13393 for more info


    function safeActiveElement() {
      try {
        return document$2.activeElement;
      } catch (err) {}
    }

    function on(elem, types, selector, data, fn, one) {
      var origFn, type; // Types can be a map of types/handlers

      if (typeof types === "object") {
        // ( types-Object, selector, data )
        if (typeof selector !== "string") {
          // ( types-Object, data )
          data = data || selector;
          selector = undefined;
        }

        for (type in types) {
          on(elem, type, selector, data, types[type], one);
        }

        return elem;
      }

      if (data == null && fn == null) {
        // ( types, fn )
        fn = selector;
        data = selector = undefined;
      } else if (fn == null) {
        if (typeof selector === "string") {
          // ( types, selector, fn )
          fn = data;
          data = undefined;
        } else {
          // ( types, data, fn )
          fn = data;
          data = selector;
          selector = undefined;
        }
      }

      if (fn === false) {
        fn = returnFalse;
      } else if (!fn) {
        return elem;
      }

      if (one === 1) {
        origFn = fn;

        fn = function (event) {
          // Can use an empty set, since event contains the info
          jQuery().off(event);
          return origFn.apply(this, arguments);
        }; // Use same guid so caller can remove using origFn


        fn.guid = origFn.guid || (origFn.guid = jQuery.guid++);
      }

      return elem.each(function () {
        jQuery.event.add(this, types, fn, data, selector);
      });
    }
    /*
     * Helper functions for managing events -- not part of the public interface.
     * Props to Dean Edwards' addEvent library for many of the ideas.
     */


    jQuery.event = {
      global: {},
      add: function (elem, types, handler, data, selector) {
        var handleObjIn,
            eventHandle,
            tmp,
            events,
            t,
            handleObj,
            special,
            handlers,
            type,
            namespaces,
            origType,
            elemData = dataPriv.get(elem); // Don't attach events to noData or text/comment nodes (but allow plain objects)

        if (!elemData) {
          return;
        } // Caller can pass in an object of custom data in lieu of the handler


        if (handler.handler) {
          handleObjIn = handler;
          handler = handleObjIn.handler;
          selector = handleObjIn.selector;
        } // Ensure that invalid selectors throw exceptions at attach time
        // Evaluate against documentElement in case elem is a non-element node (e.g., document)


        if (selector) {
          jQuery.find.matchesSelector(documentElement, selector);
        } // Make sure that the handler has a unique ID, used to find/remove it later


        if (!handler.guid) {
          handler.guid = jQuery.guid++;
        } // Init the element's event structure and main handler, if this is the first


        if (!(events = elemData.events)) {
          events = elemData.events = {};
        }

        if (!(eventHandle = elemData.handle)) {
          eventHandle = elemData.handle = function (e) {
            // Discard the second event of a jQuery.event.trigger() and
            // when an event is called after a page has unloaded
            return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? jQuery.event.dispatch.apply(elem, arguments) : undefined;
          };
        } // Handle multiple events separated by a space


        types = (types || "").match(rnothtmlwhite) || [""];
        t = types.length;

        while (t--) {
          tmp = rtypenamespace.exec(types[t]) || [];
          type = origType = tmp[1];
          namespaces = (tmp[2] || "").split(".").sort(); // There *must* be a type, no attaching namespace-only handlers

          if (!type) {
            continue;
          } // If event changes its type, use the special event handlers for the changed type


          special = jQuery.event.special[type] || {}; // If selector defined, determine special event api type, otherwise given type

          type = (selector ? special.delegateType : special.bindType) || type; // Update special based on newly reset type

          special = jQuery.event.special[type] || {}; // handleObj is passed to all event handlers

          handleObj = jQuery.extend({
            type: type,
            origType: origType,
            data: data,
            handler: handler,
            guid: handler.guid,
            selector: selector,
            needsContext: selector && jQuery.expr.match.needsContext.test(selector),
            namespace: namespaces.join(".")
          }, handleObjIn); // Init the event handler queue if we're the first

          if (!(handlers = events[type])) {
            handlers = events[type] = [];
            handlers.delegateCount = 0; // Only use addEventListener if the special events handler returns false

            if (!special.setup || special.setup.call(elem, data, namespaces, eventHandle) === false) {
              if (elem.addEventListener) {
                elem.addEventListener(type, eventHandle);
              }
            }
          }

          if (special.add) {
            special.add.call(elem, handleObj);

            if (!handleObj.handler.guid) {
              handleObj.handler.guid = handler.guid;
            }
          } // Add to the element's handler list, delegates in front


          if (selector) {
            handlers.splice(handlers.delegateCount++, 0, handleObj);
          } else {
            handlers.push(handleObj);
          } // Keep track of which events have ever been used, for event optimization


          jQuery.event.global[type] = true;
        }
      },
      // Detach an event or set of events from an element
      remove: function (elem, types, handler, selector, mappedTypes) {
        var j,
            origCount,
            tmp,
            events,
            t,
            handleObj,
            special,
            handlers,
            type,
            namespaces,
            origType,
            elemData = dataPriv.hasData(elem) && dataPriv.get(elem);

        if (!elemData || !(events = elemData.events)) {
          return;
        } // Once for each type.namespace in types; type may be omitted


        types = (types || "").match(rnothtmlwhite) || [""];
        t = types.length;

        while (t--) {
          tmp = rtypenamespace.exec(types[t]) || [];
          type = origType = tmp[1];
          namespaces = (tmp[2] || "").split(".").sort(); // Unbind all events (on this namespace, if provided) for the element

          if (!type) {
            for (type in events) {
              jQuery.event.remove(elem, type + types[t], handler, selector, true);
            }

            continue;
          }

          special = jQuery.event.special[type] || {};
          type = (selector ? special.delegateType : special.bindType) || type;
          handlers = events[type] || [];
          tmp = tmp[2] && new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)"); // Remove matching events

          origCount = j = handlers.length;

          while (j--) {
            handleObj = handlers[j];

            if ((mappedTypes || origType === handleObj.origType) && (!handler || handler.guid === handleObj.guid) && (!tmp || tmp.test(handleObj.namespace)) && (!selector || selector === handleObj.selector || selector === "**" && handleObj.selector)) {
              handlers.splice(j, 1);

              if (handleObj.selector) {
                handlers.delegateCount--;
              }

              if (special.remove) {
                special.remove.call(elem, handleObj);
              }
            }
          } // Remove generic event handler if we removed something and no more handlers exist
          // (avoids potential for endless recursion during removal of special event handlers)


          if (origCount && !handlers.length) {
            if (!special.teardown || special.teardown.call(elem, namespaces, elemData.handle) === false) {
              jQuery.removeEvent(elem, type, elemData.handle);
            }

            delete events[type];
          }
        } // Remove data and the expando if it's no longer used


        if (jQuery.isEmptyObject(events)) {
          dataPriv.remove(elem, "handle events");
        }
      },
      dispatch: function (nativeEvent) {
        // Make a writable jQuery.Event from the native event object
        var event = jQuery.event.fix(nativeEvent);
        var i,
            j,
            ret,
            matched,
            handleObj,
            handlerQueue,
            args = new Array(arguments.length),
            handlers = (dataPriv.get(this, "events") || {})[event.type] || [],
            special = jQuery.event.special[event.type] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event

        args[0] = event;

        for (i = 1; i < arguments.length; i++) {
          args[i] = arguments[i];
        }

        event.delegateTarget = this; // Call the preDispatch hook for the mapped type, and let it bail if desired

        if (special.preDispatch && special.preDispatch.call(this, event) === false) {
          return;
        } // Determine handlers


        handlerQueue = jQuery.event.handlers.call(this, event, handlers); // Run delegates first; they may want to stop propagation beneath us

        i = 0;

        while ((matched = handlerQueue[i++]) && !event.isPropagationStopped()) {
          event.currentTarget = matched.elem;
          j = 0;

          while ((handleObj = matched.handlers[j++]) && !event.isImmediatePropagationStopped()) {
            // Triggered event must either 1) have no namespace, or 2) have namespace(s)
            // a subset or equal to those in the bound event (both can have no namespace).
            if (!event.rnamespace || event.rnamespace.test(handleObj.namespace)) {
              event.handleObj = handleObj;
              event.data = handleObj.data;
              ret = ((jQuery.event.special[handleObj.origType] || {}).handle || handleObj.handler).apply(matched.elem, args);

              if (ret !== undefined) {
                if ((event.result = ret) === false) {
                  event.preventDefault();
                  event.stopPropagation();
                }
              }
            }
          }
        } // Call the postDispatch hook for the mapped type


        if (special.postDispatch) {
          special.postDispatch.call(this, event);
        }

        return event.result;
      },
      handlers: function (event, handlers) {
        var i,
            handleObj,
            sel,
            matchedHandlers,
            matchedSelectors,
            handlerQueue = [],
            delegateCount = handlers.delegateCount,
            cur = event.target; // Find delegate handlers

        if (delegateCount && // Support: IE <=9
        // Black-hole SVG <use> instance trees (trac-13180)
        cur.nodeType && // Support: Firefox <=42
        // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)
        // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click
        // Support: IE 11 only
        // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343)
        !(event.type === "click" && event.button >= 1)) {
          for (; cur !== this; cur = cur.parentNode || this) {
            // Don't check non-elements (#13208)
            // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
            if (cur.nodeType === 1 && !(event.type === "click" && cur.disabled === true)) {
              matchedHandlers = [];
              matchedSelectors = {};

              for (i = 0; i < delegateCount; i++) {
                handleObj = handlers[i]; // Don't conflict with Object.prototype properties (#13203)

                sel = handleObj.selector + " ";

                if (matchedSelectors[sel] === undefined) {
                  matchedSelectors[sel] = handleObj.needsContext ? jQuery(sel, this).index(cur) > -1 : jQuery.find(sel, this, null, [cur]).length;
                }

                if (matchedSelectors[sel]) {
                  matchedHandlers.push(handleObj);
                }
              }

              if (matchedHandlers.length) {
                handlerQueue.push({
                  elem: cur,
                  handlers: matchedHandlers
                });
              }
            }
          }
        } // Add the remaining (directly-bound) handlers


        cur = this;

        if (delegateCount < handlers.length) {
          handlerQueue.push({
            elem: cur,
            handlers: handlers.slice(delegateCount)
          });
        }

        return handlerQueue;
      },
      addProp: function (name, hook) {
        Object.defineProperty(jQuery.Event.prototype, name, {
          enumerable: true,
          configurable: true,
          get: isFunction(hook) ? function () {
            if (this.originalEvent) {
              return hook(this.originalEvent);
            }
          } : function () {
            if (this.originalEvent) {
              return this.originalEvent[name];
            }
          },
          set: function (value) {
            Object.defineProperty(this, name, {
              enumerable: true,
              configurable: true,
              writable: true,
              value: value
            });
          }
        });
      },
      fix: function (originalEvent) {
        return originalEvent[jQuery.expando] ? originalEvent : new jQuery.Event(originalEvent);
      },
      special: {
        load: {
          // Prevent triggered image.load events from bubbling to window.load
          noBubble: true
        },
        focus: {
          // Fire native event if possible so blur/focus sequence is correct
          trigger: function () {
            if (this !== safeActiveElement() && this.focus) {
              this.focus();
              return false;
            }
          },
          delegateType: "focusin"
        },
        blur: {
          trigger: function () {
            if (this === safeActiveElement() && this.blur) {
              this.blur();
              return false;
            }
          },
          delegateType: "focusout"
        },
        click: {
          // For checkbox, fire native event so checked state will be right
          trigger: function () {
            if (this.type === "checkbox" && this.click && nodeName(this, "input")) {
              this.click();
              return false;
            }
          },
          // For cross-browser consistency, don't fire native .click() on links
          _default: function (event) {
            return nodeName(event.target, "a");
          }
        },
        beforeunload: {
          postDispatch: function (event) {
            // Support: Firefox 20+
            // Firefox doesn't alert if the returnValue field is not set.
            if (event.result !== undefined && event.originalEvent) {
              event.originalEvent.returnValue = event.result;
            }
          }
        }
      }
    };

    jQuery.removeEvent = function (elem, type, handle) {
      // This "if" is needed for plain objects
      if (elem.removeEventListener) {
        elem.removeEventListener(type, handle);
      }
    };

    jQuery.Event = function (src, props) {
      // Allow instantiation without the 'new' keyword
      if (!(this instanceof jQuery.Event)) {
        return new jQuery.Event(src, props);
      } // Event object


      if (src && src.type) {
        this.originalEvent = src;
        this.type = src.type; // Events bubbling up the document may have been marked as prevented
        // by a handler lower down the tree; reflect the correct value.

        this.isDefaultPrevented = src.defaultPrevented || src.defaultPrevented === undefined && // Support: Android <=2.3 only
        src.returnValue === false ? returnTrue : returnFalse; // Create target properties
        // Support: Safari <=6 - 7 only
        // Target should not be a text node (#504, #13143)

        this.target = src.target && src.target.nodeType === 3 ? src.target.parentNode : src.target;
        this.currentTarget = src.currentTarget;
        this.relatedTarget = src.relatedTarget; // Event type
      } else {
        this.type = src;
      } // Put explicitly provided properties onto the event object


      if (props) {
        jQuery.extend(this, props);
      } // Create a timestamp if incoming event doesn't have one


      this.timeStamp = src && src.timeStamp || Date.now(); // Mark it as fixed

      this[jQuery.expando] = true;
    }; // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
    // https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html


    jQuery.Event.prototype = {
      constructor: jQuery.Event,
      isDefaultPrevented: returnFalse,
      isPropagationStopped: returnFalse,
      isImmediatePropagationStopped: returnFalse,
      isSimulated: false,
      preventDefault: function () {
        var e = this.originalEvent;
        this.isDefaultPrevented = returnTrue;

        if (e && !this.isSimulated) {
          e.preventDefault();
        }
      },
      stopPropagation: function () {
        var e = this.originalEvent;
        this.isPropagationStopped = returnTrue;

        if (e && !this.isSimulated) {
          e.stopPropagation();
        }
      },
      stopImmediatePropagation: function () {
        var e = this.originalEvent;
        this.isImmediatePropagationStopped = returnTrue;

        if (e && !this.isSimulated) {
          e.stopImmediatePropagation();
        }

        this.stopPropagation();
      }
    }; // Includes all common event props including KeyEvent and MouseEvent specific props

    jQuery.each({
      altKey: true,
      bubbles: true,
      cancelable: true,
      changedTouches: true,
      ctrlKey: true,
      detail: true,
      eventPhase: true,
      metaKey: true,
      pageX: true,
      pageY: true,
      shiftKey: true,
      view: true,
      "char": true,
      charCode: true,
      key: true,
      keyCode: true,
      button: true,
      buttons: true,
      clientX: true,
      clientY: true,
      offsetX: true,
      offsetY: true,
      pointerId: true,
      pointerType: true,
      screenX: true,
      screenY: true,
      targetTouches: true,
      toElement: true,
      touches: true,
      which: function (event) {
        var button = event.button; // Add which for key events

        if (event.which == null && rkeyEvent.test(event.type)) {
          return event.charCode != null ? event.charCode : event.keyCode;
        } // Add which for click: 1 === left; 2 === middle; 3 === right


        if (!event.which && button !== undefined && rmouseEvent.test(event.type)) {
          if (button & 1) {
            return 1;
          }

          if (button & 2) {
            return 3;
          }

          if (button & 4) {
            return 2;
          }

          return 0;
        }

        return event.which;
      }
    }, jQuery.event.addProp); // Create mouseenter/leave events using mouseover/out and event-time checks
    // so that event delegation works in jQuery.
    // Do the same for pointerenter/pointerleave and pointerover/pointerout
    //
    // Support: Safari 7 only
    // Safari sends mouseenter too often; see:
    // https://bugs.chromium.org/p/chromium/issues/detail?id=470258
    // for the description of the bug (it existed in older Chrome versions as well).

    jQuery.each({
      mouseenter: "mouseover",
      mouseleave: "mouseout",
      pointerenter: "pointerover",
      pointerleave: "pointerout"
    }, function (orig, fix) {
      jQuery.event.special[orig] = {
        delegateType: fix,
        bindType: fix,
        handle: function (event) {
          var ret,
              target = this,
              related = event.relatedTarget,
              handleObj = event.handleObj; // For mouseenter/leave call the handler if related is outside the target.
          // NB: No relatedTarget if the mouse left/entered the browser window

          if (!related || related !== target && !jQuery.contains(target, related)) {
            event.type = handleObj.origType;
            ret = handleObj.handler.apply(this, arguments);
            event.type = fix;
          }

          return ret;
        }
      };
    });
    jQuery.fn.extend({
      on: function (types, selector, data, fn) {
        return on(this, types, selector, data, fn);
      },
      one: function (types, selector, data, fn) {
        return on(this, types, selector, data, fn, 1);
      },
      off: function (types, selector, fn) {
        var handleObj, type;

        if (types && types.preventDefault && types.handleObj) {
          // ( event )  dispatched jQuery.Event
          handleObj = types.handleObj;
          jQuery(types.delegateTarget).off(handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler);
          return this;
        }

        if (typeof types === "object") {
          // ( types-object [, selector] )
          for (type in types) {
            this.off(type, selector, types[type]);
          }

          return this;
        }

        if (selector === false || typeof selector === "function") {
          // ( types [, fn] )
          fn = selector;
          selector = undefined;
        }

        if (fn === false) {
          fn = returnFalse;
        }

        return this.each(function () {
          jQuery.event.remove(this, types, fn, selector);
        });
      }
    });
    var
    /* eslint-disable max-len */
    // See https://github.com/eslint/eslint/issues/3229
    rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,

    /* eslint-enable */
    // Support: IE <=10 - 11, Edge 12 - 13 only
    // In IE/Edge using regex groups here causes severe slowdowns.
    // See https://connect.microsoft.com/IE/feedback/details/1736512/
    rnoInnerhtml = /<script|<style|<link/i,
        // checked="checked" or checked
    rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
        rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g; // Prefer a tbody over its parent table for containing new rows

    function manipulationTarget(elem, content) {
      if (nodeName(elem, "table") && nodeName(content.nodeType !== 11 ? content : content.firstChild, "tr")) {
        return jQuery(elem).children("tbody")[0] || elem;
      }

      return elem;
    } // Replace/restore the type attribute of script elements for safe DOM manipulation


    function disableScript(elem) {
      elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type;
      return elem;
    }

    function restoreScript(elem) {
      if ((elem.type || "").slice(0, 5) === "true/") {
        elem.type = elem.type.slice(5);
      } else {
        elem.removeAttribute("type");
      }

      return elem;
    }

    function cloneCopyEvent(src, dest) {
      var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;

      if (dest.nodeType !== 1) {
        return;
      } // 1. Copy private data: events, handlers, etc.


      if (dataPriv.hasData(src)) {
        pdataOld = dataPriv.access(src);
        pdataCur = dataPriv.set(dest, pdataOld);
        events = pdataOld.events;

        if (events) {
          delete pdataCur.handle;
          pdataCur.events = {};

          for (type in events) {
            for (i = 0, l = events[type].length; i < l; i++) {
              jQuery.event.add(dest, type, events[type][i]);
            }
          }
        }
      } // 2. Copy user data


      if (dataUser.hasData(src)) {
        udataOld = dataUser.access(src);
        udataCur = jQuery.extend({}, udataOld);
        dataUser.set(dest, udataCur);
      }
    } // Fix IE bugs, see support tests


    function fixInput(src, dest) {
      var nodeName = dest.nodeName.toLowerCase(); // Fails to persist the checked state of a cloned checkbox or radio button.

      if (nodeName === "input" && rcheckableType.test(src.type)) {
        dest.checked = src.checked; // Fails to return the selected option to the default selected state when cloning options
      } else if (nodeName === "input" || nodeName === "textarea") {
        dest.defaultValue = src.defaultValue;
      }
    }

    function domManip(collection, args, callback, ignored) {
      // Flatten any nested arrays
      args = concat$1.apply([], args);
      var fragment,
          first,
          scripts,
          hasScripts,
          node,
          doc,
          i = 0,
          l = collection.length,
          iNoClone = l - 1,
          value = args[0],
          valueIsFunction = isFunction(value); // We can't cloneNode fragments that contain checked, in WebKit

      if (valueIsFunction || l > 1 && typeof value === "string" && !support.checkClone && rchecked.test(value)) {
        return collection.each(function (index) {
          var self = collection.eq(index);

          if (valueIsFunction) {
            args[0] = value.call(this, index, self.html());
          }

          domManip(self, args, callback, ignored);
        });
      }

      if (l) {
        fragment = buildFragment(args, collection[0].ownerDocument, false, collection, ignored);
        first = fragment.firstChild;

        if (fragment.childNodes.length === 1) {
          fragment = first;
        } // Require either new content or an interest in ignored elements to invoke the callback


        if (first || ignored) {
          scripts = jQuery.map(getAll(fragment, "script"), disableScript);
          hasScripts = scripts.length; // Use the original fragment for the last item
          // instead of the first because it can end up
          // being emptied incorrectly in certain situations (#8070).

          for (; i < l; i++) {
            node = fragment;

            if (i !== iNoClone) {
              node = jQuery.clone(node, true, true); // Keep references to cloned scripts for later restoration

              if (hasScripts) {
                // Support: Android <=4.0 only, PhantomJS 1 only
                // push.apply(_, arraylike) throws on ancient WebKit
                jQuery.merge(scripts, getAll(node, "script"));
              }
            }

            callback.call(collection[i], node, i);
          }

          if (hasScripts) {
            doc = scripts[scripts.length - 1].ownerDocument; // Reenable scripts

            jQuery.map(scripts, restoreScript); // Evaluate executable scripts on first document insertion

            for (i = 0; i < hasScripts; i++) {
              node = scripts[i];

              if (rscriptType.test(node.type || "") && !dataPriv.access(node, "globalEval") && jQuery.contains(doc, node)) {
                if (node.src && (node.type || "").toLowerCase() !== "module") {
                  // Optional AJAX dependency, but won't run scripts if not present
                  if (jQuery._evalUrl) {
                    jQuery._evalUrl(node.src);
                  }
                } else {
                  DOMEval(node.textContent.replace(rcleanScript, ""), doc, node);
                }
              }
            }
          }
        }
      }

      return collection;
    }

    function remove(elem, selector, keepData) {
      var node,
          nodes = selector ? jQuery.filter(selector, elem) : elem,
          i = 0;

      for (; (node = nodes[i]) != null; i++) {
        if (!keepData && node.nodeType === 1) {
          jQuery.cleanData(getAll(node));
        }

        if (node.parentNode) {
          if (keepData && jQuery.contains(node.ownerDocument, node)) {
            setGlobalEval(getAll(node, "script"));
          }

          node.parentNode.removeChild(node);
        }
      }

      return elem;
    }

    jQuery.extend({
      htmlPrefilter: function (html) {
        return html.replace(rxhtmlTag, "<$1></$2>");
      },
      clone: function (elem, dataAndEvents, deepDataAndEvents) {
        var i,
            l,
            srcElements,
            destElements,
            clone = elem.cloneNode(true),
            inPage = jQuery.contains(elem.ownerDocument, elem); // Fix IE cloning issues

        if (!support.noCloneChecked && (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem)) {
          // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2
          destElements = getAll(clone);
          srcElements = getAll(elem);

          for (i = 0, l = srcElements.length; i < l; i++) {
            fixInput(srcElements[i], destElements[i]);
          }
        } // Copy the events from the original to the clone


        if (dataAndEvents) {
          if (deepDataAndEvents) {
            srcElements = srcElements || getAll(elem);
            destElements = destElements || getAll(clone);

            for (i = 0, l = srcElements.length; i < l; i++) {
              cloneCopyEvent(srcElements[i], destElements[i]);
            }
          } else {
            cloneCopyEvent(elem, clone);
          }
        } // Preserve script evaluation history


        destElements = getAll(clone, "script");

        if (destElements.length > 0) {
          setGlobalEval(destElements, !inPage && getAll(elem, "script"));
        } // Return the cloned set


        return clone;
      },
      cleanData: function (elems) {
        var data,
            elem,
            type,
            special = jQuery.event.special,
            i = 0;

        for (; (elem = elems[i]) !== undefined; i++) {
          if (acceptData(elem)) {
            if (data = elem[dataPriv.expando]) {
              if (data.events) {
                for (type in data.events) {
                  if (special[type]) {
                    jQuery.event.remove(elem, type); // This is a shortcut to avoid jQuery.event.remove's overhead
                  } else {
                    jQuery.removeEvent(elem, type, data.handle);
                  }
                }
              } // Support: Chrome <=35 - 45+
              // Assign undefined instead of using delete, see Data#remove


              elem[dataPriv.expando] = undefined;
            }

            if (elem[dataUser.expando]) {
              // Support: Chrome <=35 - 45+
              // Assign undefined instead of using delete, see Data#remove
              elem[dataUser.expando] = undefined;
            }
          }
        }
      }
    });
    jQuery.fn.extend({
      detach: function (selector) {
        return remove(this, selector, true);
      },
      remove: function (selector) {
        return remove(this, selector);
      },
      text: function (value) {
        return access(this, function (value) {
          return value === undefined ? jQuery.text(this) : this.empty().each(function () {
            if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) {
              this.textContent = value;
            }
          });
        }, null, value, arguments.length);
      },
      append: function () {
        return domManip(this, arguments, function (elem) {
          if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) {
            var target = manipulationTarget(this, elem);
            target.appendChild(elem);
          }
        });
      },
      prepend: function () {
        return domManip(this, arguments, function (elem) {
          if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) {
            var target = manipulationTarget(this, elem);
            target.insertBefore(elem, target.firstChild);
          }
        });
      },
      before: function () {
        return domManip(this, arguments, function (elem) {
          if (this.parentNode) {
            this.parentNode.insertBefore(elem, this);
          }
        });
      },
      after: function () {
        return domManip(this, arguments, function (elem) {
          if (this.parentNode) {
            this.parentNode.insertBefore(elem, this.nextSibling);
          }
        });
      },
      empty: function () {
        var elem,
            i = 0;

        for (; (elem = this[i]) != null; i++) {
          if (elem.nodeType === 1) {
            // Prevent memory leaks
            jQuery.cleanData(getAll(elem, false)); // Remove any remaining nodes

            elem.textContent = "";
          }
        }

        return this;
      },
      clone: function (dataAndEvents, deepDataAndEvents) {
        dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
        deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
        return this.map(function () {
          return jQuery.clone(this, dataAndEvents, deepDataAndEvents);
        });
      },
      html: function (value) {
        return access(this, function (value) {
          var elem = this[0] || {},
              i = 0,
              l = this.length;

          if (value === undefined && elem.nodeType === 1) {
            return elem.innerHTML;
          } // See if we can take a shortcut and just use innerHTML


          if (typeof value === "string" && !rnoInnerhtml.test(value) && !wrapMap[(rtagName.exec(value) || ["", ""])[1].toLowerCase()]) {
            value = jQuery.htmlPrefilter(value);

            try {
              for (; i < l; i++) {
                elem = this[i] || {}; // Remove element nodes and prevent memory leaks

                if (elem.nodeType === 1) {
                  jQuery.cleanData(getAll(elem, false));
                  elem.innerHTML = value;
                }
              }

              elem = 0; // If using innerHTML throws an exception, use the fallback method
            } catch (e) {}
          }

          if (elem) {
            this.empty().append(value);
          }
        }, null, value, arguments.length);
      },
      replaceWith: function () {
        var ignored = []; // Make the changes, replacing each non-ignored context element with the new content

        return domManip(this, arguments, function (elem) {
          var parent = this.parentNode;

          if (jQuery.inArray(this, ignored) < 0) {
            jQuery.cleanData(getAll(this));

            if (parent) {
              parent.replaceChild(elem, this);
            }
          } // Force callback invocation

        }, ignored);
      }
    });
    jQuery.each({
      appendTo: "append",
      prependTo: "prepend",
      insertBefore: "before",
      insertAfter: "after",
      replaceAll: "replaceWith"
    }, function (name, original) {
      jQuery.fn[name] = function (selector) {
        var elems,
            ret = [],
            insert = jQuery(selector),
            last = insert.length - 1,
            i = 0;

        for (; i <= last; i++) {
          elems = i === last ? this : this.clone(true);
          jQuery(insert[i])[original](elems); // Support: Android <=4.0 only, PhantomJS 1 only
          // .get() because push.apply(_, arraylike) throws on ancient WebKit

          push$1.apply(ret, elems.get());
        }

        return this.pushStack(ret);
      };
    });
    var rnumnonpx = new RegExp("^(" + pnum + ")(?!px)[a-z%]+$", "i");

    var getStyles = function (elem) {
      // Support: IE <=11 only, Firefox <=30 (#15098, #14150)
      // IE throws on elements created in popups
      // FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
      var view = elem.ownerDocument.defaultView;

      if (!view || !view.opener) {
        view = window;
      }

      return view.getComputedStyle(elem);
    };

    var rboxStyle = new RegExp(cssExpand.join("|"), "i");

    (function () {
      // Executing both pixelPosition & boxSizingReliable tests require only one layout
      // so they're executed at the same time to save the second computation.
      function computeStyleTests() {
        // This is a singleton, we need to execute it only once
        if (!div) {
          return;
        }

        container.style.cssText = "position:absolute;left:-11111px;width:60px;" + "margin-top:1px;padding:0;border:0";
        div.style.cssText = "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + "margin:auto;border:1px;padding:1px;" + "width:60%;top:1%";
        documentElement.appendChild(container).appendChild(div);
        var divStyle = window.getComputedStyle(div);
        pixelPositionVal = divStyle.top !== "1%"; // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44

        reliableMarginLeftVal = roundPixelMeasures(divStyle.marginLeft) === 12; // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3
        // Some styles come back with percentage values, even though they shouldn't

        div.style.right = "60%";
        pixelBoxStylesVal = roundPixelMeasures(divStyle.right) === 36; // Support: IE 9 - 11 only
        // Detect misreporting of content dimensions for box-sizing:border-box elements

        boxSizingReliableVal = roundPixelMeasures(divStyle.width) === 36; // Support: IE 9 only
        // Detect overflow:scroll screwiness (gh-3699)

        div.style.position = "absolute";
        scrollboxSizeVal = div.offsetWidth === 36 || "absolute";
        documentElement.removeChild(container); // Nullify the div so it wouldn't be stored in the memory and
        // it will also be a sign that checks already performed

        div = null;
      }

      function roundPixelMeasures(measure) {
        return Math.round(parseFloat(measure));
      }

      var pixelPositionVal,
          boxSizingReliableVal,
          scrollboxSizeVal,
          pixelBoxStylesVal,
          reliableMarginLeftVal,
          container = document$2.createElement("div"),
          div = document$2.createElement("div"); // Finish early in limited (non-browser) environments

      if (!div.style) {
        return;
      } // Support: IE <=9 - 11 only
      // Style of cloned element affects source element cloned (#8908)


      div.style.backgroundClip = "content-box";
      div.cloneNode(true).style.backgroundClip = "";
      support.clearCloneStyle = div.style.backgroundClip === "content-box";
      jQuery.extend(support, {
        boxSizingReliable: function () {
          computeStyleTests();
          return boxSizingReliableVal;
        },
        pixelBoxStyles: function () {
          computeStyleTests();
          return pixelBoxStylesVal;
        },
        pixelPosition: function () {
          computeStyleTests();
          return pixelPositionVal;
        },
        reliableMarginLeft: function () {
          computeStyleTests();
          return reliableMarginLeftVal;
        },
        scrollboxSize: function () {
          computeStyleTests();
          return scrollboxSizeVal;
        }
      });
    })();

    function curCSS(elem, name, computed) {
      var width,
          minWidth,
          maxWidth,
          ret,
          // Support: Firefox 51+
      // Retrieving style before computed somehow
      // fixes an issue with getting wrong values
      // on detached elements
      style = elem.style;
      computed = computed || getStyles(elem); // getPropertyValue is needed for:
      //   .css('filter') (IE 9 only, #12537)
      //   .css('--customProperty) (#3144)

      if (computed) {
        ret = computed.getPropertyValue(name) || computed[name];

        if (ret === "" && !jQuery.contains(elem.ownerDocument, elem)) {
          ret = jQuery.style(elem, name);
        } // A tribute to the "awesome hack by Dean Edwards"
        // Android Browser returns percentage for some values,
        // but width seems to be reliably pixels.
        // This is against the CSSOM draft spec:
        // https://drafts.csswg.org/cssom/#resolved-values


        if (!support.pixelBoxStyles() && rnumnonpx.test(ret) && rboxStyle.test(name)) {
          // Remember the original values
          width = style.width;
          minWidth = style.minWidth;
          maxWidth = style.maxWidth; // Put in the new values to get a computed value out

          style.minWidth = style.maxWidth = style.width = ret;
          ret = computed.width; // Revert the changed values

          style.width = width;
          style.minWidth = minWidth;
          style.maxWidth = maxWidth;
        }
      }

      return ret !== undefined ? // Support: IE <=9 - 11 only
      // IE returns zIndex value as an integer.
      ret + "" : ret;
    }

    function addGetHookIf(conditionFn, hookFn) {
      // Define the hook, we'll check on the first run if it's really needed.
      return {
        get: function () {
          if (conditionFn()) {
            // Hook not needed (or it's not possible to use it due
            // to missing dependency), remove it.
            delete this.get;
            return;
          } // Hook needed; redefine it so that the support test is not executed again.


          return (this.get = hookFn).apply(this, arguments);
        }
      };
    }

    var // Swappable if display is none or starts with table
    // except "table", "table-cell", or "table-caption"
    // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
    rdisplayswap = /^(none|table(?!-c[ea]).+)/,
        rcustomProp = /^--/,
        cssShow = {
      position: "absolute",
      visibility: "hidden",
      display: "block"
    },
        cssNormalTransform = {
      letterSpacing: "0",
      fontWeight: "400"
    },
        cssPrefixes = ["Webkit", "Moz", "ms"],
        emptyStyle = document$2.createElement("div").style; // Return a css property mapped to a potentially vendor prefixed property

    function vendorPropName(name) {
      // Shortcut for names that are not vendor prefixed
      if (name in emptyStyle) {
        return name;
      } // Check for vendor prefixed names


      var capName = name[0].toUpperCase() + name.slice(1),
          i = cssPrefixes.length;

      while (i--) {
        name = cssPrefixes[i] + capName;

        if (name in emptyStyle) {
          return name;
        }
      }
    } // Return a property mapped along what jQuery.cssProps suggests or to
    // a vendor prefixed property.


    function finalPropName(name) {
      var ret = jQuery.cssProps[name];

      if (!ret) {
        ret = jQuery.cssProps[name] = vendorPropName(name) || name;
      }

      return ret;
    }

    function setPositiveNumber(elem, value, subtract) {
      // Any relative (+/-) values have already been
      // normalized at this point
      var matches = rcssNum.exec(value);
      return matches ? // Guard against undefined "subtract", e.g., when used as in cssHooks
      Math.max(0, matches[2] - (subtract || 0)) + (matches[3] || "px") : value;
    }

    function boxModelAdjustment(elem, dimension, box, isBorderBox, styles, computedVal) {
      var i = dimension === "width" ? 1 : 0,
          extra = 0,
          delta = 0; // Adjustment may not be necessary

      if (box === (isBorderBox ? "border" : "content")) {
        return 0;
      }

      for (; i < 4; i += 2) {
        // Both box models exclude margin
        if (box === "margin") {
          delta += jQuery.css(elem, box + cssExpand[i], true, styles);
        } // If we get here with a content-box, we're seeking "padding" or "border" or "margin"


        if (!isBorderBox) {
          // Add padding
          delta += jQuery.css(elem, "padding" + cssExpand[i], true, styles); // For "border" or "margin", add border

          if (box !== "padding") {
            delta += jQuery.css(elem, "border" + cssExpand[i] + "Width", true, styles); // But still keep track of it otherwise
          } else {
            extra += jQuery.css(elem, "border" + cssExpand[i] + "Width", true, styles);
          } // If we get here with a border-box (content + padding + border), we're seeking "content" or
          // "padding" or "margin"

        } else {
          // For "content", subtract padding
          if (box === "content") {
            delta -= jQuery.css(elem, "padding" + cssExpand[i], true, styles);
          } // For "content" or "padding", subtract border


          if (box !== "margin") {
            delta -= jQuery.css(elem, "border" + cssExpand[i] + "Width", true, styles);
          }
        }
      } // Account for positive content-box scroll gutter when requested by providing computedVal


      if (!isBorderBox && computedVal >= 0) {
        // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border
        // Assuming integer scroll gutter, subtract the rest and round down
        delta += Math.max(0, Math.ceil(elem["offset" + dimension[0].toUpperCase() + dimension.slice(1)] - computedVal - delta - extra - 0.5));
      }

      return delta;
    }

    function getWidthOrHeight(elem, dimension, extra) {
      // Start with computed style
      var styles = getStyles(elem),
          val = curCSS(elem, dimension, styles),
          isBorderBox = jQuery.css(elem, "boxSizing", false, styles) === "border-box",
          valueIsBorderBox = isBorderBox; // Support: Firefox <=54
      // Return a confounding non-pixel value or feign ignorance, as appropriate.

      if (rnumnonpx.test(val)) {
        if (!extra) {
          return val;
        }

        val = "auto";
      } // Check for style in case a browser which returns unreliable values
      // for getComputedStyle silently falls back to the reliable elem.style


      valueIsBorderBox = valueIsBorderBox && (support.boxSizingReliable() || val === elem.style[dimension]); // Fall back to offsetWidth/offsetHeight when value is "auto"
      // This happens for inline elements with no explicit setting (gh-3571)
      // Support: Android <=4.1 - 4.3 only
      // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)

      if (val === "auto" || !parseFloat(val) && jQuery.css(elem, "display", false, styles) === "inline") {
        val = elem["offset" + dimension[0].toUpperCase() + dimension.slice(1)]; // offsetWidth/offsetHeight provide border-box values

        valueIsBorderBox = true;
      } // Normalize "" and auto


      val = parseFloat(val) || 0; // Adjust for the element's box model

      return val + boxModelAdjustment(elem, dimension, extra || (isBorderBox ? "border" : "content"), valueIsBorderBox, styles, // Provide the current computed size to request scroll gutter calculation (gh-3589)
      val) + "px";
    }

    jQuery.extend({
      // Add in style property hooks for overriding the default
      // behavior of getting and setting a style property
      cssHooks: {
        opacity: {
          get: function (elem, computed) {
            if (computed) {
              // We should always get a number back from opacity
              var ret = curCSS(elem, "opacity");
              return ret === "" ? "1" : ret;
            }
          }
        }
      },
      // Don't automatically add "px" to these possibly-unitless properties
      cssNumber: {
        "animationIterationCount": true,
        "columnCount": true,
        "fillOpacity": true,
        "flexGrow": true,
        "flexShrink": true,
        "fontWeight": true,
        "lineHeight": true,
        "opacity": true,
        "order": true,
        "orphans": true,
        "widows": true,
        "zIndex": true,
        "zoom": true
      },
      // Add in properties whose names you wish to fix before
      // setting or getting the value
      cssProps: {},
      // Get and set the style property on a DOM Node
      style: function (elem, name, value, extra) {
        // Don't set styles on text and comment nodes
        if (!elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style) {
          return;
        } // Make sure that we're working with the right name


        var ret,
            type,
            hooks,
            origName = camelCase(name),
            isCustomProp = rcustomProp.test(name),
            style = elem.style; // Make sure that we're working with the right name. We don't
        // want to query the value if it is a CSS custom property
        // since they are user-defined.

        if (!isCustomProp) {
          name = finalPropName(origName);
        } // Gets hook for the prefixed version, then unprefixed version


        hooks = jQuery.cssHooks[name] || jQuery.cssHooks[origName]; // Check if we're setting a value

        if (value !== undefined) {
          type = typeof value; // Convert "+=" or "-=" to relative numbers (#7345)

          if (type === "string" && (ret = rcssNum.exec(value)) && ret[1]) {
            value = adjustCSS(elem, name, ret); // Fixes bug #9237

            type = "number";
          } // Make sure that null and NaN values aren't set (#7116)


          if (value == null || value !== value) {
            return;
          } // If a number was passed in, add the unit (except for certain CSS properties)


          if (type === "number") {
            value += ret && ret[3] || (jQuery.cssNumber[origName] ? "" : "px");
          } // background-* props affect original clone's values


          if (!support.clearCloneStyle && value === "" && name.indexOf("background") === 0) {
            style[name] = "inherit";
          } // If a hook was provided, use that value, otherwise just set the specified value


          if (!hooks || !("set" in hooks) || (value = hooks.set(elem, value, extra)) !== undefined) {
            if (isCustomProp) {
              style.setProperty(name, value);
            } else {
              style[name] = value;
            }
          }
        } else {
          // If a hook was provided get the non-computed value from there
          if (hooks && "get" in hooks && (ret = hooks.get(elem, false, extra)) !== undefined) {
            return ret;
          } // Otherwise just get the value from the style object


          return style[name];
        }
      },
      css: function (elem, name, extra, styles) {
        var val,
            num,
            hooks,
            origName = camelCase(name),
            isCustomProp = rcustomProp.test(name); // Make sure that we're working with the right name. We don't
        // want to modify the value if it is a CSS custom property
        // since they are user-defined.

        if (!isCustomProp) {
          name = finalPropName(origName);
        } // Try prefixed name followed by the unprefixed name


        hooks = jQuery.cssHooks[name] || jQuery.cssHooks[origName]; // If a hook was provided get the computed value from there

        if (hooks && "get" in hooks) {
          val = hooks.get(elem, true, extra);
        } // Otherwise, if a way to get the computed value exists, use that


        if (val === undefined) {
          val = curCSS(elem, name, styles);
        } // Convert "normal" to computed value


        if (val === "normal" && name in cssNormalTransform) {
          val = cssNormalTransform[name];
        } // Make numeric if forced or a qualifier was provided and val looks numeric


        if (extra === "" || extra) {
          num = parseFloat(val);
          return extra === true || isFinite(num) ? num || 0 : val;
        }

        return val;
      }
    });
    jQuery.each(["height", "width"], function (i, dimension) {
      jQuery.cssHooks[dimension] = {
        get: function (elem, computed, extra) {
          if (computed) {
            // Certain elements can have dimension info if we invisibly show them
            // but it must have a current display style that would benefit
            return rdisplayswap.test(jQuery.css(elem, "display")) && ( // Support: Safari 8+
            // Table columns in Safari have non-zero offsetWidth & zero
            // getBoundingClientRect().width unless display is changed.
            // Support: IE <=11 only
            // Running getBoundingClientRect on a disconnected node
            // in IE throws an error.
            !elem.getClientRects().length || !elem.getBoundingClientRect().width) ? swap(elem, cssShow, function () {
              return getWidthOrHeight(elem, dimension, extra);
            }) : getWidthOrHeight(elem, dimension, extra);
          }
        },
        set: function (elem, value, extra) {
          var matches,
              styles = getStyles(elem),
              isBorderBox = jQuery.css(elem, "boxSizing", false, styles) === "border-box",
              subtract = extra && boxModelAdjustment(elem, dimension, extra, isBorderBox, styles); // Account for unreliable border-box dimensions by comparing offset* to computed and
          // faking a content-box to get border and padding (gh-3699)

          if (isBorderBox && support.scrollboxSize() === styles.position) {
            subtract -= Math.ceil(elem["offset" + dimension[0].toUpperCase() + dimension.slice(1)] - parseFloat(styles[dimension]) - boxModelAdjustment(elem, dimension, "border", false, styles) - 0.5);
          } // Convert to pixels if value adjustment is needed


          if (subtract && (matches = rcssNum.exec(value)) && (matches[3] || "px") !== "px") {
            elem.style[dimension] = value;
            value = jQuery.css(elem, dimension);
          }

          return setPositiveNumber(elem, value, subtract);
        }
      };
    });
    jQuery.cssHooks.marginLeft = addGetHookIf(support.reliableMarginLeft, function (elem, computed) {
      if (computed) {
        return (parseFloat(curCSS(elem, "marginLeft")) || elem.getBoundingClientRect().left - swap(elem, {
          marginLeft: 0
        }, function () {
          return elem.getBoundingClientRect().left;
        })) + "px";
      }
    }); // These hooks are used by animate to expand properties

    jQuery.each({
      margin: "",
      padding: "",
      border: "Width"
    }, function (prefix, suffix) {
      jQuery.cssHooks[prefix + suffix] = {
        expand: function (value) {
          var i = 0,
              expanded = {},
              // Assumes a single number if not a string
          parts = typeof value === "string" ? value.split(" ") : [value];

          for (; i < 4; i++) {
            expanded[prefix + cssExpand[i] + suffix] = parts[i] || parts[i - 2] || parts[0];
          }

          return expanded;
        }
      };

      if (prefix !== "margin") {
        jQuery.cssHooks[prefix + suffix].set = setPositiveNumber;
      }
    });
    jQuery.fn.extend({
      css: function (name, value) {
        return access(this, function (elem, name, value) {
          var styles,
              len,
              map = {},
              i = 0;

          if (Array.isArray(name)) {
            styles = getStyles(elem);
            len = name.length;

            for (; i < len; i++) {
              map[name[i]] = jQuery.css(elem, name[i], false, styles);
            }

            return map;
          }

          return value !== undefined ? jQuery.style(elem, name, value) : jQuery.css(elem, name);
        }, name, value, arguments.length > 1);
      }
    }); // Based off of the plugin by Clint Helfers, with permission.
    // https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/

    jQuery.fn.delay = function (time, type) {
      time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
      type = type || "fx";
      return this.queue(type, function (next, hooks) {
        var timeout = window.setTimeout(next, time);

        hooks.stop = function () {
          window.clearTimeout(timeout);
        };
      });
    };

    (function () {
      var input = document$2.createElement("input"),
          select = document$2.createElement("select"),
          opt = select.appendChild(document$2.createElement("option"));
      input.type = "checkbox"; // Support: Android <=4.3 only
      // Default value for a checkbox should be "on"

      support.checkOn = input.value !== ""; // Support: IE <=11 only
      // Must access selectedIndex to make default options select

      support.optSelected = opt.selected; // Support: IE <=11 only
      // An input loses its value after becoming a radio

      input = document$2.createElement("input");
      input.value = "t";
      input.type = "radio";
      support.radioValue = input.value === "t";
    })();

    var boolHook,
        attrHandle = jQuery.expr.attrHandle;
    jQuery.fn.extend({
      attr: function (name, value) {
        return access(this, jQuery.attr, name, value, arguments.length > 1);
      },
      removeAttr: function (name) {
        return this.each(function () {
          jQuery.removeAttr(this, name);
        });
      }
    });
    jQuery.extend({
      attr: function (elem, name, value) {
        var ret,
            hooks,
            nType = elem.nodeType; // Don't get/set attributes on text, comment and attribute nodes

        if (nType === 3 || nType === 8 || nType === 2) {
          return;
        } // Fallback to prop when attributes are not supported


        if (typeof elem.getAttribute === "undefined") {
          return jQuery.prop(elem, name, value);
        } // Attribute hooks are determined by the lowercase version
        // Grab necessary hook if one is defined


        if (nType !== 1 || !jQuery.isXMLDoc(elem)) {
          hooks = jQuery.attrHooks[name.toLowerCase()] || (jQuery.expr.match.bool.test(name) ? boolHook : undefined);
        }

        if (value !== undefined) {
          if (value === null) {
            jQuery.removeAttr(elem, name);
            return;
          }

          if (hooks && "set" in hooks && (ret = hooks.set(elem, value, name)) !== undefined) {
            return ret;
          }

          elem.setAttribute(name, value + "");
          return value;
        }

        if (hooks && "get" in hooks && (ret = hooks.get(elem, name)) !== null) {
          return ret;
        }

        ret = jQuery.find.attr(elem, name); // Non-existent attributes return null, we normalize to undefined

        return ret == null ? undefined : ret;
      },
      attrHooks: {
        type: {
          set: function (elem, value) {
            if (!support.radioValue && value === "radio" && nodeName(elem, "input")) {
              var val = elem.value;
              elem.setAttribute("type", value);

              if (val) {
                elem.value = val;
              }

              return value;
            }
          }
        }
      },
      removeAttr: function (elem, value) {
        var name,
            i = 0,
            // Attribute names can contain non-HTML whitespace characters
        // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
        attrNames = value && value.match(rnothtmlwhite);

        if (attrNames && elem.nodeType === 1) {
          while (name = attrNames[i++]) {
            elem.removeAttribute(name);
          }
        }
      }
    }); // Hooks for boolean attributes

    boolHook = {
      set: function (elem, value, name) {
        if (value === false) {
          // Remove boolean attributes when set to false
          jQuery.removeAttr(elem, name);
        } else {
          elem.setAttribute(name, name);
        }

        return name;
      }
    };
    jQuery.each(jQuery.expr.match.bool.source.match(/\w+/g), function (i, name) {
      var getter = attrHandle[name] || jQuery.find.attr;

      attrHandle[name] = function (elem, name, isXML) {
        var ret,
            handle,
            lowercaseName = name.toLowerCase();

        if (!isXML) {
          // Avoid an infinite loop by temporarily removing this function from the getter
          handle = attrHandle[lowercaseName];
          attrHandle[lowercaseName] = ret;
          ret = getter(elem, name, isXML) != null ? lowercaseName : null;
          attrHandle[lowercaseName] = handle;
        }

        return ret;
      };
    });
    var rfocusable = /^(?:input|select|textarea|button)$/i,
        rclickable = /^(?:a|area)$/i;
    jQuery.fn.extend({
      prop: function (name, value) {
        return access(this, jQuery.prop, name, value, arguments.length > 1);
      },
      removeProp: function (name) {
        return this.each(function () {
          delete this[jQuery.propFix[name] || name];
        });
      }
    });
    jQuery.extend({
      prop: function (elem, name, value) {
        var ret,
            hooks,
            nType = elem.nodeType; // Don't get/set properties on text, comment and attribute nodes

        if (nType === 3 || nType === 8 || nType === 2) {
          return;
        }

        if (nType !== 1 || !jQuery.isXMLDoc(elem)) {
          // Fix name and attach hooks
          name = jQuery.propFix[name] || name;
          hooks = jQuery.propHooks[name];
        }

        if (value !== undefined) {
          if (hooks && "set" in hooks && (ret = hooks.set(elem, value, name)) !== undefined) {
            return ret;
          }

          return elem[name] = value;
        }

        if (hooks && "get" in hooks && (ret = hooks.get(elem, name)) !== null) {
          return ret;
        }

        return elem[name];
      },
      propHooks: {
        tabIndex: {
          get: function (elem) {
            // Support: IE <=9 - 11 only
            // elem.tabIndex doesn't always return the
            // correct value when it hasn't been explicitly set
            // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
            // Use proper attribute retrieval(#12072)
            var tabindex = jQuery.find.attr(elem, "tabindex");

            if (tabindex) {
              return parseInt(tabindex, 10);
            }

            if (rfocusable.test(elem.nodeName) || rclickable.test(elem.nodeName) && elem.href) {
              return 0;
            }

            return -1;
          }
        }
      },
      propFix: {
        "for": "htmlFor",
        "class": "className"
      }
    }); // Support: IE <=11 only
    // Accessing the selectedIndex property
    // forces the browser to respect setting selected
    // on the option
    // The getter ensures a default option is selected
    // when in an optgroup
    // eslint rule "no-unused-expressions" is disabled for this code
    // since it considers such accessions noop

    if (!support.optSelected) {
      jQuery.propHooks.selected = {
        get: function (elem) {
          /* eslint no-unused-expressions: "off" */
          var parent = elem.parentNode;

          if (parent && parent.parentNode) {
            parent.parentNode.selectedIndex;
          }

          return null;
        },
        set: function (elem) {
          /* eslint no-unused-expressions: "off" */
          var parent = elem.parentNode;

          if (parent) {
            parent.selectedIndex;

            if (parent.parentNode) {
              parent.parentNode.selectedIndex;
            }
          }
        }
      };
    }

    jQuery.each(["tabIndex", "readOnly", "maxLength", "cellSpacing", "cellPadding", "rowSpan", "colSpan", "useMap", "frameBorder", "contentEditable"], function () {
      jQuery.propFix[this.toLowerCase()] = this;
    }); // Strip and collapse whitespace according to HTML spec
    // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace

    function stripAndCollapse(value) {
      var tokens = value.match(rnothtmlwhite) || [];
      return tokens.join(" ");
    }

    function getClass(elem) {
      return elem.getAttribute && elem.getAttribute("class") || "";
    }

    function classesToArray(value) {
      if (Array.isArray(value)) {
        return value;
      }

      if (typeof value === "string") {
        return value.match(rnothtmlwhite) || [];
      }

      return [];
    }

    jQuery.fn.extend({
      addClass: function (value) {
        var classes,
            elem,
            cur,
            curValue,
            clazz,
            j,
            finalValue,
            i = 0;

        if (isFunction(value)) {
          return this.each(function (j) {
            jQuery(this).addClass(value.call(this, j, getClass(this)));
          });
        }

        classes = classesToArray(value);

        if (classes.length) {
          while (elem = this[i++]) {
            curValue = getClass(elem);
            cur = elem.nodeType === 1 && " " + stripAndCollapse(curValue) + " ";

            if (cur) {
              j = 0;

              while (clazz = classes[j++]) {
                if (cur.indexOf(" " + clazz + " ") < 0) {
                  cur += clazz + " ";
                }
              } // Only assign if different to avoid unneeded rendering.


              finalValue = stripAndCollapse(cur);

              if (curValue !== finalValue) {
                elem.setAttribute("class", finalValue);
              }
            }
          }
        }

        return this;
      },
      removeClass: function (value) {
        var classes,
            elem,
            cur,
            curValue,
            clazz,
            j,
            finalValue,
            i = 0;

        if (isFunction(value)) {
          return this.each(function (j) {
            jQuery(this).removeClass(value.call(this, j, getClass(this)));
          });
        }

        if (!arguments.length) {
          return this.attr("class", "");
        }

        classes = classesToArray(value);

        if (classes.length) {
          while (elem = this[i++]) {
            curValue = getClass(elem); // This expression is here for better compressibility (see addClass)

            cur = elem.nodeType === 1 && " " + stripAndCollapse(curValue) + " ";

            if (cur) {
              j = 0;

              while (clazz = classes[j++]) {
                // Remove *all* instances
                while (cur.indexOf(" " + clazz + " ") > -1) {
                  cur = cur.replace(" " + clazz + " ", " ");
                }
              } // Only assign if different to avoid unneeded rendering.


              finalValue = stripAndCollapse(cur);

              if (curValue !== finalValue) {
                elem.setAttribute("class", finalValue);
              }
            }
          }
        }

        return this;
      },
      toggleClass: function (value, stateVal) {
        var type = typeof value,
            isValidValue = type === "string" || Array.isArray(value);

        if (typeof stateVal === "boolean" && isValidValue) {
          return stateVal ? this.addClass(value) : this.removeClass(value);
        }

        if (isFunction(value)) {
          return this.each(function (i) {
            jQuery(this).toggleClass(value.call(this, i, getClass(this), stateVal), stateVal);
          });
        }

        return this.each(function () {
          var className, i, self, classNames;

          if (isValidValue) {
            // Toggle individual class names
            i = 0;
            self = jQuery(this);
            classNames = classesToArray(value);

            while (className = classNames[i++]) {
              // Check each className given, space separated list
              if (self.hasClass(className)) {
                self.removeClass(className);
              } else {
                self.addClass(className);
              }
            } // Toggle whole class name

          } else if (value === undefined || type === "boolean") {
            className = getClass(this);

            if (className) {
              // Store className if set
              dataPriv.set(this, "__className__", className);
            } // If the element has a class name or if we're passed `false`,
            // then remove the whole classname (if there was one, the above saved it).
            // Otherwise bring back whatever was previously saved (if anything),
            // falling back to the empty string if nothing was stored.


            if (this.setAttribute) {
              this.setAttribute("class", className || value === false ? "" : dataPriv.get(this, "__className__") || "");
            }
          }
        });
      },
      hasClass: function (selector) {
        var className,
            elem,
            i = 0;
        className = " " + selector + " ";

        while (elem = this[i++]) {
          if (elem.nodeType === 1 && (" " + stripAndCollapse(getClass(elem)) + " ").indexOf(className) > -1) {
            return true;
          }
        }

        return false;
      }
    });
    var rreturn = /\r/g;
    jQuery.fn.extend({
      val: function (value) {
        var hooks,
            ret,
            valueIsFunction,
            elem = this[0];

        if (!arguments.length) {
          if (elem) {
            hooks = jQuery.valHooks[elem.type] || jQuery.valHooks[elem.nodeName.toLowerCase()];

            if (hooks && "get" in hooks && (ret = hooks.get(elem, "value")) !== undefined) {
              return ret;
            }

            ret = elem.value; // Handle most common string cases

            if (typeof ret === "string") {
              return ret.replace(rreturn, "");
            } // Handle cases where value is null/undef or number


            return ret == null ? "" : ret;
          }

          return;
        }

        valueIsFunction = isFunction(value);
        return this.each(function (i) {
          var val;

          if (this.nodeType !== 1) {
            return;
          }

          if (valueIsFunction) {
            val = value.call(this, i, jQuery(this).val());
          } else {
            val = value;
          } // Treat null/undefined as ""; convert numbers to string


          if (val == null) {
            val = "";
          } else if (typeof val === "number") {
            val += "";
          } else if (Array.isArray(val)) {
            val = jQuery.map(val, function (value) {
              return value == null ? "" : value + "";
            });
          }

          hooks = jQuery.valHooks[this.type] || jQuery.valHooks[this.nodeName.toLowerCase()]; // If set returns undefined, fall back to normal setting

          if (!hooks || !("set" in hooks) || hooks.set(this, val, "value") === undefined) {
            this.value = val;
          }
        });
      }
    });
    jQuery.extend({
      valHooks: {
        option: {
          get: function (elem) {
            var val = jQuery.find.attr(elem, "value");
            return val != null ? val : // Support: IE <=10 - 11 only
            // option.text throws exceptions (#14686, #14858)
            // Strip and collapse whitespace
            // https://html.spec.whatwg.org/#strip-and-collapse-whitespace
            stripAndCollapse(jQuery.text(elem));
          }
        },
        select: {
          get: function (elem) {
            var value,
                option,
                i,
                options = elem.options,
                index = elem.selectedIndex,
                one = elem.type === "select-one",
                values = one ? null : [],
                max = one ? index + 1 : options.length;

            if (index < 0) {
              i = max;
            } else {
              i = one ? index : 0;
            } // Loop through all the selected options


            for (; i < max; i++) {
              option = options[i]; // Support: IE <=9 only
              // IE8-9 doesn't update selected after form reset (#2551)

              if ((option.selected || i === index) && // Don't return options that are disabled or in a disabled optgroup
              !option.disabled && (!option.parentNode.disabled || !nodeName(option.parentNode, "optgroup"))) {
                // Get the specific value for the option
                value = jQuery(option).val(); // We don't need an array for one selects

                if (one) {
                  return value;
                } // Multi-Selects return an array


                values.push(value);
              }
            }

            return values;
          },
          set: function (elem, value) {
            var optionSet,
                option,
                options = elem.options,
                values = jQuery.makeArray(value),
                i = options.length;

            while (i--) {
              option = options[i];
              /* eslint-disable no-cond-assign */

              if (option.selected = jQuery.inArray(jQuery.valHooks.option.get(option), values) > -1) {
                optionSet = true;
              }
              /* eslint-enable no-cond-assign */

            } // Force browsers to behave consistently when non-matching value is set


            if (!optionSet) {
              elem.selectedIndex = -1;
            }

            return values;
          }
        }
      }
    }); // Radios and checkboxes getter/setter

    jQuery.each(["radio", "checkbox"], function () {
      jQuery.valHooks[this] = {
        set: function (elem, value) {
          if (Array.isArray(value)) {
            return elem.checked = jQuery.inArray(jQuery(elem).val(), value) > -1;
          }
        }
      };

      if (!support.checkOn) {
        jQuery.valHooks[this].get = function (elem) {
          return elem.getAttribute("value") === null ? "on" : elem.value;
        };
      }
    }); // Return jQuery for attributes-only inclusion

    support.focusin = "onfocusin" in window;

    var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
        stopPropagationCallback = function (e) {
      e.stopPropagation();
    };

    jQuery.extend(jQuery.event, {
      trigger: function (event, data, elem, onlyHandlers) {
        var i,
            cur,
            tmp,
            bubbleType,
            ontype,
            handle,
            special,
            lastElement,
            eventPath = [elem || document$2],
            type = hasOwn.call(event, "type") ? event.type : event,
            namespaces = hasOwn.call(event, "namespace") ? event.namespace.split(".") : [];
        cur = lastElement = tmp = elem = elem || document$2; // Don't do events on text and comment nodes

        if (elem.nodeType === 3 || elem.nodeType === 8) {
          return;
        } // focus/blur morphs to focusin/out; ensure we're not firing them right now


        if (rfocusMorph.test(type + jQuery.event.triggered)) {
          return;
        }

        if (type.indexOf(".") > -1) {
          // Namespaced trigger; create a regexp to match event type in handle()
          namespaces = type.split(".");
          type = namespaces.shift();
          namespaces.sort();
        }

        ontype = type.indexOf(":") < 0 && "on" + type; // Caller can pass in a jQuery.Event object, Object, or just an event type string

        event = event[jQuery.expando] ? event : new jQuery.Event(type, typeof event === "object" && event); // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)

        event.isTrigger = onlyHandlers ? 2 : 3;
        event.namespace = namespaces.join(".");
        event.rnamespace = event.namespace ? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null; // Clean up the event in case it is being reused

        event.result = undefined;

        if (!event.target) {
          event.target = elem;
        } // Clone any incoming data and prepend the event, creating the handler arg list


        data = data == null ? [event] : jQuery.makeArray(data, [event]); // Allow special events to draw outside the lines

        special = jQuery.event.special[type] || {};

        if (!onlyHandlers && special.trigger && special.trigger.apply(elem, data) === false) {
          return;
        } // Determine event propagation path in advance, per W3C events spec (#9951)
        // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)


        if (!onlyHandlers && !special.noBubble && !isWindow(elem)) {
          bubbleType = special.delegateType || type;

          if (!rfocusMorph.test(bubbleType + type)) {
            cur = cur.parentNode;
          }

          for (; cur; cur = cur.parentNode) {
            eventPath.push(cur);
            tmp = cur;
          } // Only add window if we got to document (e.g., not plain obj or detached DOM)


          if (tmp === (elem.ownerDocument || document$2)) {
            eventPath.push(tmp.defaultView || tmp.parentWindow || window);
          }
        } // Fire handlers on the event path


        i = 0;

        while ((cur = eventPath[i++]) && !event.isPropagationStopped()) {
          lastElement = cur;
          event.type = i > 1 ? bubbleType : special.bindType || type; // jQuery handler

          handle = (dataPriv.get(cur, "events") || {})[event.type] && dataPriv.get(cur, "handle");

          if (handle) {
            handle.apply(cur, data);
          } // Native handler


          handle = ontype && cur[ontype];

          if (handle && handle.apply && acceptData(cur)) {
            event.result = handle.apply(cur, data);

            if (event.result === false) {
              event.preventDefault();
            }
          }
        }

        event.type = type; // If nobody prevented the default action, do it now

        if (!onlyHandlers && !event.isDefaultPrevented()) {
          if ((!special._default || special._default.apply(eventPath.pop(), data) === false) && acceptData(elem)) {
            // Call a native DOM method on the target with the same name as the event.
            // Don't do default actions on window, that's where global variables be (#6170)
            if (ontype && isFunction(elem[type]) && !isWindow(elem)) {
              // Don't re-trigger an onFOO event when we call its FOO() method
              tmp = elem[ontype];

              if (tmp) {
                elem[ontype] = null;
              } // Prevent re-triggering of the same event, since we already bubbled it above


              jQuery.event.triggered = type;

              if (event.isPropagationStopped()) {
                lastElement.addEventListener(type, stopPropagationCallback);
              }

              elem[type]();

              if (event.isPropagationStopped()) {
                lastElement.removeEventListener(type, stopPropagationCallback);
              }

              jQuery.event.triggered = undefined;

              if (tmp) {
                elem[ontype] = tmp;
              }
            }
          }
        }

        return event.result;
      },
      // Piggyback on a donor event to simulate a different one
      // Used only for `focus(in | out)` events
      simulate: function (type, elem, event) {
        var e = jQuery.extend(new jQuery.Event(), event, {
          type: type,
          isSimulated: true
        });
        jQuery.event.trigger(e, null, elem);
      }
    });
    jQuery.fn.extend({
      trigger: function (type, data) {
        return this.each(function () {
          jQuery.event.trigger(type, data, this);
        });
      },
      triggerHandler: function (type, data) {
        var elem = this[0];

        if (elem) {
          return jQuery.event.trigger(type, data, elem, true);
        }
      }
    }); // Support: Firefox <=44
    // Firefox doesn't have focus(in | out) events
    // Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
    //
    // Support: Chrome <=48 - 49, Safari <=9.0 - 9.1
    // focus(in | out) events fire after focus & blur events,
    // which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
    // Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857

    if (!support.focusin) {
      jQuery.each({
        focus: "focusin",
        blur: "focusout"
      }, function (orig, fix) {
        // Attach a single capturing handler on the document while someone wants focusin/focusout
        var handler = function (event) {
          jQuery.event.simulate(fix, event.target, jQuery.event.fix(event));
        };

        jQuery.event.special[fix] = {
          setup: function () {
            var doc = this.ownerDocument || this,
                attaches = dataPriv.access(doc, fix);

            if (!attaches) {
              doc.addEventListener(orig, handler, true);
            }

            dataPriv.access(doc, fix, (attaches || 0) + 1);
          },
          teardown: function () {
            var doc = this.ownerDocument || this,
                attaches = dataPriv.access(doc, fix) - 1;

            if (!attaches) {
              doc.removeEventListener(orig, handler, true);
              dataPriv.remove(doc, fix);
            } else {
              dataPriv.access(doc, fix, attaches);
            }
          }
        };
      });
    }

    var rbracket = /\[\]$/,
        rCRLF = /\r?\n/g,
        rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
        rsubmittable = /^(?:input|select|textarea|keygen)/i;

    function buildParams(prefix, obj, traditional, add) {
      var name;

      if (Array.isArray(obj)) {
        // Serialize array item.
        jQuery.each(obj, function (i, v) {
          if (traditional || rbracket.test(prefix)) {
            // Treat each array item as a scalar.
            add(prefix, v);
          } else {
            // Item is non-scalar (array or object), encode its numeric index.
            buildParams(prefix + "[" + (typeof v === "object" && v != null ? i : "") + "]", v, traditional, add);
          }
        });
      } else if (!traditional && toType(obj) === "object") {
        // Serialize object item.
        for (name in obj) {
          buildParams(prefix + "[" + name + "]", obj[name], traditional, add);
        }
      } else {
        // Serialize scalar item.
        add(prefix, obj);
      }
    } // Serialize an array of form elements or a set of
    // key/values into a query string


    jQuery.param = function (a, traditional) {
      var prefix,
          s = [],
          add = function (key, valueOrFunction) {
        // If value is a function, invoke it and use its return value
        var value = isFunction(valueOrFunction) ? valueOrFunction() : valueOrFunction;
        s[s.length] = encodeURIComponent(key) + "=" + encodeURIComponent(value == null ? "" : value);
      }; // If an array was passed in, assume that it is an array of form elements.


      if (Array.isArray(a) || a.jquery && !jQuery.isPlainObject(a)) {
        // Serialize the form elements
        jQuery.each(a, function () {
          add(this.name, this.value);
        });
      } else {
        // If traditional, encode the "old" way (the way 1.3.2 or older
        // did it), otherwise encode params recursively.
        for (prefix in a) {
          buildParams(prefix, a[prefix], traditional, add);
        }
      } // Return the resulting serialization


      return s.join("&");
    };

    jQuery.fn.extend({
      serialize: function () {
        return jQuery.param(this.serializeArray());
      },
      serializeArray: function () {
        return this.map(function () {
          // Can add propHook for "elements" to filter or add form elements
          var elements = jQuery.prop(this, "elements");
          return elements ? jQuery.makeArray(elements) : this;
        }).filter(function () {
          var type = this.type; // Use .is( ":disabled" ) so that fieldset[disabled] works

          return this.name && !jQuery(this).is(":disabled") && rsubmittable.test(this.nodeName) && !rsubmitterTypes.test(type) && (this.checked || !rcheckableType.test(type));
        }).map(function (i, elem) {
          var val = jQuery(this).val();

          if (val == null) {
            return null;
          }

          if (Array.isArray(val)) {
            return jQuery.map(val, function (val) {
              return {
                name: elem.name,
                value: val.replace(rCRLF, "\r\n")
              };
            });
          }

          return {
            name: elem.name,
            value: val.replace(rCRLF, "\r\n")
          };
        }).get();
      }
    });
    jQuery.fn.extend({
      wrapAll: function (html) {
        var wrap;

        if (this[0]) {
          if (isFunction(html)) {
            html = html.call(this[0]);
          } // The elements to wrap the target around


          wrap = jQuery(html, this[0].ownerDocument).eq(0).clone(true);

          if (this[0].parentNode) {
            wrap.insertBefore(this[0]);
          }

          wrap.map(function () {
            var elem = this;

            while (elem.firstElementChild) {
              elem = elem.firstElementChild;
            }

            return elem;
          }).append(this);
        }

        return this;
      },
      wrapInner: function (html) {
        if (isFunction(html)) {
          return this.each(function (i) {
            jQuery(this).wrapInner(html.call(this, i));
          });
        }

        return this.each(function () {
          var self = jQuery(this),
              contents = self.contents();

          if (contents.length) {
            contents.wrapAll(html);
          } else {
            self.append(html);
          }
        });
      },
      wrap: function (html) {
        var htmlIsFunction = isFunction(html);
        return this.each(function (i) {
          jQuery(this).wrapAll(htmlIsFunction ? html.call(this, i) : html);
        });
      },
      unwrap: function (selector) {
        this.parent(selector).not("body").each(function () {
          jQuery(this).replaceWith(this.childNodes);
        });
        return this;
      }
    });

    jQuery.expr.pseudos.hidden = function (elem) {
      return !jQuery.expr.pseudos.visible(elem);
    };

    jQuery.expr.pseudos.visible = function (elem) {
      return !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length);
    }; // Support: Safari 8 only
    // In Safari 8 documents created via document.implementation.createHTMLDocument
    // collapse sibling forms: the second one becomes a child of the first one.
    // Because of that, this security measure has to be disabled in Safari 8.
    // https://bugs.webkit.org/show_bug.cgi?id=137337


    support.createHTMLDocument = function () {
      var body = document$2.implementation.createHTMLDocument("").body;
      body.innerHTML = "<form></form><form></form>";
      return body.childNodes.length === 2;
    }(); // Argument "data" should be string of html
    // context (optional): If specified, the fragment will be created in this context,
    // defaults to document
    // keepScripts (optional): If true, will include scripts passed in the html string


    jQuery.parseHTML = function (data, context, keepScripts) {
      if (typeof data !== "string") {
        return [];
      }

      if (typeof context === "boolean") {
        keepScripts = context;
        context = false;
      }

      var base, parsed, scripts;

      if (!context) {
        // Stop scripts or inline event handlers from being executed immediately
        // by using document.implementation
        if (support.createHTMLDocument) {
          context = document$2.implementation.createHTMLDocument(""); // Set the base href for the created document
          // so any parsed elements with URLs
          // are based on the document's URL (gh-2965)

          base = context.createElement("base");
          base.href = document$2.location.href;
          context.head.appendChild(base);
        } else {
          context = document$2;
        }
      }

      parsed = rsingleTag.exec(data);
      scripts = !keepScripts && []; // Single tag

      if (parsed) {
        return [context.createElement(parsed[1])];
      }

      parsed = buildFragment([data], context, scripts);

      if (scripts && scripts.length) {
        jQuery(scripts).remove();
      }

      return jQuery.merge([], parsed.childNodes);
    };

    jQuery.offset = {
      setOffset: function (elem, options, i) {
        var curPosition,
            curLeft,
            curCSSTop,
            curTop,
            curOffset,
            curCSSLeft,
            calculatePosition,
            position = jQuery.css(elem, "position"),
            curElem = jQuery(elem),
            props = {}; // Set position first, in-case top/left are set even on static elem

        if (position === "static") {
          elem.style.position = "relative";
        }

        curOffset = curElem.offset();
        curCSSTop = jQuery.css(elem, "top");
        curCSSLeft = jQuery.css(elem, "left");
        calculatePosition = (position === "absolute" || position === "fixed") && (curCSSTop + curCSSLeft).indexOf("auto") > -1; // Need to be able to calculate position if either
        // top or left is auto and position is either absolute or fixed

        if (calculatePosition) {
          curPosition = curElem.position();
          curTop = curPosition.top;
          curLeft = curPosition.left;
        } else {
          curTop = parseFloat(curCSSTop) || 0;
          curLeft = parseFloat(curCSSLeft) || 0;
        }

        if (isFunction(options)) {
          // Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
          options = options.call(elem, i, jQuery.extend({}, curOffset));
        }

        if (options.top != null) {
          props.top = options.top - curOffset.top + curTop;
        }

        if (options.left != null) {
          props.left = options.left - curOffset.left + curLeft;
        }

        if ("using" in options) {
          options.using.call(elem, props);
        } else {
          curElem.css(props);
        }
      }
    };
    jQuery.fn.extend({
      // offset() relates an element's border box to the document origin
      offset: function (options) {
        // Preserve chaining for setter
        if (arguments.length) {
          return options === undefined ? this : this.each(function (i) {
            jQuery.offset.setOffset(this, options, i);
          });
        }

        var rect,
            win,
            elem = this[0];

        if (!elem) {
          return;
        } // Return zeros for disconnected and hidden (display: none) elements (gh-2310)
        // Support: IE <=11 only
        // Running getBoundingClientRect on a
        // disconnected node in IE throws an error


        if (!elem.getClientRects().length) {
          return {
            top: 0,
            left: 0
          };
        } // Get document-relative position by adding viewport scroll to viewport-relative gBCR


        rect = elem.getBoundingClientRect();
        win = elem.ownerDocument.defaultView;
        return {
          top: rect.top + win.pageYOffset,
          left: rect.left + win.pageXOffset
        };
      },
      // position() relates an element's margin box to its offset parent's padding box
      // This corresponds to the behavior of CSS absolute positioning
      position: function () {
        if (!this[0]) {
          return;
        }

        var offsetParent,
            offset,
            doc,
            elem = this[0],
            parentOffset = {
          top: 0,
          left: 0
        }; // position:fixed elements are offset from the viewport, which itself always has zero offset

        if (jQuery.css(elem, "position") === "fixed") {
          // Assume position:fixed implies availability of getBoundingClientRect
          offset = elem.getBoundingClientRect();
        } else {
          offset = this.offset(); // Account for the *real* offset parent, which can be the document or its root element
          // when a statically positioned element is identified

          doc = elem.ownerDocument;
          offsetParent = elem.offsetParent || doc.documentElement;

          while (offsetParent && (offsetParent === doc.body || offsetParent === doc.documentElement) && jQuery.css(offsetParent, "position") === "static") {
            offsetParent = offsetParent.parentNode;
          }

          if (offsetParent && offsetParent !== elem && offsetParent.nodeType === 1) {
            // Incorporate borders into its offset, since they are outside its content origin
            parentOffset = jQuery(offsetParent).offset();
            parentOffset.top += jQuery.css(offsetParent, "borderTopWidth", true);
            parentOffset.left += jQuery.css(offsetParent, "borderLeftWidth", true);
          }
        } // Subtract parent offsets and element margins


        return {
          top: offset.top - parentOffset.top - jQuery.css(elem, "marginTop", true),
          left: offset.left - parentOffset.left - jQuery.css(elem, "marginLeft", true)
        };
      },
      // This method will return documentElement in the following cases:
      // 1) For the element inside the iframe without offsetParent, this method will return
      //    documentElement of the parent window
      // 2) For the hidden or detached element
      // 3) For body or html element, i.e. in case of the html node - it will return itself
      //
      // but those exceptions were never presented as a real life use-cases
      // and might be considered as more preferable results.
      //
      // This logic, however, is not guaranteed and can change at any point in the future
      offsetParent: function () {
        return this.map(function () {
          var offsetParent = this.offsetParent;

          while (offsetParent && jQuery.css(offsetParent, "position") === "static") {
            offsetParent = offsetParent.offsetParent;
          }

          return offsetParent || documentElement;
        });
      }
    }); // Create scrollLeft and scrollTop methods

    jQuery.each({
      scrollLeft: "pageXOffset",
      scrollTop: "pageYOffset"
    }, function (method, prop) {
      var top = "pageYOffset" === prop;

      jQuery.fn[method] = function (val) {
        return access(this, function (elem, method, val) {
          // Coalesce documents and windows
          var win;

          if (isWindow(elem)) {
            win = elem;
          } else if (elem.nodeType === 9) {
            win = elem.defaultView;
          }

          if (val === undefined) {
            return win ? win[prop] : elem[method];
          }

          if (win) {
            win.scrollTo(!top ? val : win.pageXOffset, top ? val : win.pageYOffset);
          } else {
            elem[method] = val;
          }
        }, method, val, arguments.length);
      };
    }); // Support: Safari <=7 - 9.1, Chrome <=37 - 49
    // Add the top/left cssHooks using jQuery.fn.position
    // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
    // Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347
    // getComputedStyle returns percent when specified for top/left/bottom/right;
    // rather than make the css module depend on the offset module, just check for it here

    jQuery.each(["top", "left"], function (i, prop) {
      jQuery.cssHooks[prop] = addGetHookIf(support.pixelPosition, function (elem, computed) {
        if (computed) {
          computed = curCSS(elem, prop); // If curCSS returns percentage, fallback to offset

          return rnumnonpx.test(computed) ? jQuery(elem).position()[prop] + "px" : computed;
        }
      });
    }); // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods

    jQuery.each({
      Height: "height",
      Width: "width"
    }, function (name, type) {
      jQuery.each({
        padding: "inner" + name,
        content: type,
        "": "outer" + name
      }, function (defaultExtra, funcName) {
        // Margin is only for outerHeight, outerWidth
        jQuery.fn[funcName] = function (margin, value) {
          var chainable = arguments.length && (defaultExtra || typeof margin !== "boolean"),
              extra = defaultExtra || (margin === true || value === true ? "margin" : "border");
          return access(this, function (elem, type, value) {
            var doc;

            if (isWindow(elem)) {
              // $( window ).outerWidth/Height return w/h including scrollbars (gh-1729)
              return funcName.indexOf("outer") === 0 ? elem["inner" + name] : elem.document.documentElement["client" + name];
            } // Get document width or height


            if (elem.nodeType === 9) {
              doc = elem.documentElement; // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
              // whichever is greatest

              return Math.max(elem.body["scroll" + name], doc["scroll" + name], elem.body["offset" + name], doc["offset" + name], doc["client" + name]);
            }

            return value === undefined ? // Get width or height on the element, requesting but not forcing parseFloat
            jQuery.css(elem, type, extra) : // Set width or height on the element
            jQuery.style(elem, type, value, extra);
          }, type, chainable ? margin : undefined, chainable);
        };
      });
    });
    jQuery.each(("blur focus focusin focusout resize scroll click dblclick " + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + "change select submit keydown keypress keyup contextmenu").split(" "), function (i, name) {
      // Handle event binding
      jQuery.fn[name] = function (data, fn) {
        return arguments.length > 0 ? this.on(name, null, data, fn) : this.trigger(name);
      };
    });
    jQuery.fn.extend({
      hover: function (fnOver, fnOut) {
        return this.mouseenter(fnOver).mouseleave(fnOut || fnOver);
      }
    });
    jQuery.fn.extend({
      bind: function (types, data, fn) {
        return this.on(types, null, data, fn);
      },
      unbind: function (types, fn) {
        return this.off(types, null, fn);
      },
      delegate: function (selector, types, data, fn) {
        return this.on(types, selector, data, fn);
      },
      undelegate: function (selector, types, fn) {
        // ( namespace ) or ( selector, types [, fn] )
        return arguments.length === 1 ? this.off(selector, "**") : this.off(types, selector || "**", fn);
      }
    }); // Bind a function to a context, optionally partially applying any
    // arguments.
    // jQuery.proxy is deprecated to promote standards (specifically Function#bind)
    // However, it is not slated for removal any time soon

    jQuery.proxy = function (fn, context) {
      var tmp, args, proxy;

      if (typeof context === "string") {
        tmp = fn[context];
        context = fn;
        fn = tmp;
      } // Quick check to determine if target is callable, in the spec
      // this throws a TypeError, but we will just return undefined.


      if (!isFunction(fn)) {
        return undefined;
      } // Simulated bind


      args = slice.call(arguments, 2);

      proxy = function () {
        return fn.apply(context || this, args.concat(slice.call(arguments)));
      }; // Set the guid of unique handler to the same of original handler, so it can be removed


      proxy.guid = fn.guid = fn.guid || jQuery.guid++;
      return proxy;
    };

    jQuery.holdReady = function (hold) {
      if (hold) {
        jQuery.readyWait++;
      } else {
        jQuery.ready(true);
      }
    };

    jQuery.isArray = Array.isArray;
    jQuery.parseJSON = JSON.parse;
    jQuery.nodeName = nodeName;
    jQuery.isFunction = isFunction;
    jQuery.isWindow = isWindow;
    jQuery.camelCase = camelCase;
    jQuery.type = toType;
    jQuery.now = Date.now;

    jQuery.isNumeric = function (obj) {
      // As of jQuery 3.0, isNumeric is limited to
      // strings and numbers (primitives or objects)
      // that can be coerced to finite numbers (gh-2662)
      var type = jQuery.type(obj);
      return (type === "number" || type === "string") && // parseFloat NaNs numeric-cast false positives ("")
      // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
      // subtraction forces infinities to NaN
      !isNaN(obj - parseFloat(obj));
    };

    const $$1 = jQuery;

    function div$1(options) {
      return create$1("div", options);
    }

    function create$1(tag, options) {
      const elem = document.createElement(tag);

      if (options) {
        if (options.class) {
          elem.classList.add(options.class);
        }

        if (options.id) {
          elem.id = options.id;
        }

        if (options.style) {
          applyStyle$1(elem, options.style);
        }
      }

      return elem;
    }

    function hide$1(elem) {
      const cssStyle = getComputedStyle(elem);

      if (cssStyle.display !== "none") {
        elem._initialDisplay = cssStyle.display;
      }

      elem.style.display = "none";
    }

    function offset$1(elem) {
      // Return zeros for disconnected and hidden (display: none) elements (gh-2310)
      // Support: IE <=11 only
      // Running getBoundingClientRect on a
      // disconnected node in IE throws an error
      if (!elem.getClientRects().length) {
        return {
          top: 0,
          left: 0
        };
      } // Get document-relative position by adding viewport scroll to viewport-relative gBCR


      const rect = elem.getBoundingClientRect();
      const win = elem.ownerDocument.defaultView;
      return {
        top: rect.top + win.pageYOffset,
        left: rect.left + win.pageXOffset
      };
    }

    function pageCoordinates$1(e) {
      if (e.type.startsWith("touch")) {
        const touch = e.touches[0];
        return {
          x: touch.pageX,
          y: touch.pageY
        };
      } else {
        return {
          x: e.pageX,
          y: e.pageY
        };
      }
    }

    const relativeDOMBBox = (parentElement, childElement) => {
      const {
        x: x_p,
        y: y_p,
        width: width_p,
        height: height_p
      } = parentElement.getBoundingClientRect();
      const {
        x: x_c,
        y: y_c,
        width: width_c,
        height: height_c
      } = childElement.getBoundingClientRect();
      return {
        x: x_c - x_p,
        y: y_c - y_p,
        width: width_c,
        height: height_c
      };
    };

    function applyStyle$1(elem, style) {
      for (let key of Object.keys(style)) {
        elem.style[key] = style[key];
      }
    }

    function guid$2() {
      return ("0000" + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4);
    }

    let getMouseXY$1 = (domElement, {
      clientX,
      clientY
    }) => {
      // DOMRect object with eight properties: left, top, right, bottom, x, y, width, height
      const {
        left,
        top,
        width,
        height
      } = domElement.getBoundingClientRect();
      const x = clientX - left;
      const y = clientY - top;
      return {
        x,
        y,
        xNormalized: x / width,
        yNormalized: y / height,
        width,
        height
      };
    };
    /**
     * Translate the mouse coordinates for the event to the coordinates for the given target element
     * @param event
     * @param domElement
     * @returns {{x: number, y: number}}
     */


    function translateMouseCoordinates$1(event, domElement) {
      const {
        clientX,
        clientY
      } = event;
      return getMouseXY$1(domElement, {
        clientX,
        clientY
      });
    }

    function createIcon$1(name, color) {
      return iconMarkup$1(name, color);
    }

    function iconMarkup$1(name, color) {
      color = color || "currentColor";
      let icon = icons$1[name];

      if (!icon) {
        console.error(`No icon named: ${name}`);
        icon = icons$1["question"];
      }

      const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
      svg.setAttributeNS(null, 'viewBox', '0 0 ' + icon[0] + ' ' + icon[1]);
      const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
      path.setAttributeNS(null, 'fill', color);
      path.setAttributeNS(null, 'd', icon[4]);
      svg.appendChild(path);
      return svg;
    }

    const icons$1 = {
      "check": [512, 512, [], "f00c", "M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"],
      "cog": [512, 512, [], "f013", "M444.788 291.1l42.616 24.599c4.867 2.809 7.126 8.618 5.459 13.985-11.07 35.642-29.97 67.842-54.689 94.586a12.016 12.016 0 0 1-14.832 2.254l-42.584-24.595a191.577 191.577 0 0 1-60.759 35.13v49.182a12.01 12.01 0 0 1-9.377 11.718c-34.956 7.85-72.499 8.256-109.219.007-5.49-1.233-9.403-6.096-9.403-11.723v-49.184a191.555 191.555 0 0 1-60.759-35.13l-42.584 24.595a12.016 12.016 0 0 1-14.832-2.254c-24.718-26.744-43.619-58.944-54.689-94.586-1.667-5.366.592-11.175 5.459-13.985L67.212 291.1a193.48 193.48 0 0 1 0-70.199l-42.616-24.599c-4.867-2.809-7.126-8.618-5.459-13.985 11.07-35.642 29.97-67.842 54.689-94.586a12.016 12.016 0 0 1 14.832-2.254l42.584 24.595a191.577 191.577 0 0 1 60.759-35.13V25.759a12.01 12.01 0 0 1 9.377-11.718c34.956-7.85 72.499-8.256 109.219-.007 5.49 1.233 9.403 6.096 9.403 11.723v49.184a191.555 191.555 0 0 1 60.759 35.13l42.584-24.595a12.016 12.016 0 0 1 14.832 2.254c24.718 26.744 43.619 58.944 54.689 94.586 1.667 5.366-.592 11.175-5.459 13.985L444.788 220.9a193.485 193.485 0 0 1 0 70.2zM336 256c0-44.112-35.888-80-80-80s-80 35.888-80 80 35.888 80 80 80 80-35.888 80-80z"],
      "exclamation": [192, 512, [], "f12a", "M176 432c0 44.112-35.888 80-80 80s-80-35.888-80-80 35.888-80 80-80 80 35.888 80 80zM25.26 25.199l13.6 272C39.499 309.972 50.041 320 62.83 320h66.34c12.789 0 23.331-10.028 23.97-22.801l13.6-272C167.425 11.49 156.496 0 142.77 0H49.23C35.504 0 24.575 11.49 25.26 25.199z"],
      "exclamation-circle": [512, 512, [], "f06a", "M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"],
      "exclamation-triangle": [576, 512, [], "f071", "M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"],
      "minus": [448, 512, [], "f068", "M424 318.2c13.3 0 24-10.7 24-24v-76.4c0-13.3-10.7-24-24-24H24c-13.3 0-24 10.7-24 24v76.4c0 13.3 10.7 24 24 24h400z"],
      "minus-circle": [512, 512, [], "f056", "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zM124 296c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h264c6.6 0 12 5.4 12 12v56c0 6.6-5.4 12-12 12H124z"],
      "minus-square": [448, 512, [], "f146", "M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zM92 296c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h264c6.6 0 12 5.4 12 12v56c0 6.6-5.4 12-12 12H92z"],
      "plus": [448, 512, [], "f067", "M448 294.2v-76.4c0-13.3-10.7-24-24-24H286.2V56c0-13.3-10.7-24-24-24h-76.4c-13.3 0-24 10.7-24 24v137.8H24c-13.3 0-24 10.7-24 24v76.4c0 13.3 10.7 24 24 24h137.8V456c0 13.3 10.7 24 24 24h76.4c13.3 0 24-10.7 24-24V318.2H424c13.3 0 24-10.7 24-24z"],
      "plus-circle": [512, 512, [], "f055", "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm144 276c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92h-92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"],
      "plus-square": [448, 512, [], "f0fe", "M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-32 252c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92H92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"],
      "question": [384, 512, [], "f128", "M202.021 0C122.202 0 70.503 32.703 29.914 91.026c-7.363 10.58-5.093 25.086 5.178 32.874l43.138 32.709c10.373 7.865 25.132 6.026 33.253-4.148 25.049-31.381 43.63-49.449 82.757-49.449 30.764 0 68.816 19.799 68.816 49.631 0 22.552-18.617 34.134-48.993 51.164-35.423 19.86-82.299 44.576-82.299 106.405V320c0 13.255 10.745 24 24 24h72.471c13.255 0 24-10.745 24-24v-5.773c0-42.86 125.268-44.645 125.268-160.627C377.504 66.256 286.902 0 202.021 0zM192 373.459c-38.196 0-69.271 31.075-69.271 69.271 0 38.195 31.075 69.27 69.271 69.27s69.271-31.075 69.271-69.271-31.075-69.27-69.271-69.27z"],
      "save": [448, 512, [], "f0c7", "M433.941 129.941l-83.882-83.882A48 48 0 0 0 316.118 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V163.882a48 48 0 0 0-14.059-33.941zM224 416c-35.346 0-64-28.654-64-64 0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64zm96-304.52V212c0 6.627-5.373 12-12 12H76c-6.627 0-12-5.373-12-12V108c0-6.627 5.373-12 12-12h228.52c3.183 0 6.235 1.264 8.485 3.515l3.48 3.48A11.996 11.996 0 0 1 320 111.48z"],
      "search": [512, 512, [], "f002", "M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"],
      "share": [512, 512, [], "f064", "M503.691 189.836L327.687 37.851C312.281 24.546 288 35.347 288 56.015v80.053C127.371 137.907 0 170.1 0 322.326c0 61.441 39.581 122.309 83.333 154.132 13.653 9.931 33.111-2.533 28.077-18.631C66.066 312.814 132.917 274.316 288 272.085V360c0 20.7 24.3 31.453 39.687 18.164l176.004-152c11.071-9.562 11.086-26.753 0-36.328z"],
      "spinner": [512, 512, [], "f110", "M304 48c0 26.51-21.49 48-48 48s-48-21.49-48-48 21.49-48 48-48 48 21.49 48 48zm-48 368c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zm208-208c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zM96 256c0-26.51-21.49-48-48-48S0 229.49 0 256s21.49 48 48 48 48-21.49 48-48zm12.922 99.078c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.491-48-48-48zm294.156 0c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.49-48-48-48zM108.922 60.922c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.491-48-48-48z"],
      "square": [448, 512, [], "f0c8", "M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48z"],
      "square-full": [512, 512, [], "f45c", "M512 512H0V0h512v512z"],
      "times": [384, 512, [], "f00d", "M323.1 441l53.9-53.9c9.4-9.4 9.4-24.5 0-33.9L279.8 256l97.2-97.2c9.4-9.4 9.4-24.5 0-33.9L323.1 71c-9.4-9.4-24.5-9.4-33.9 0L192 168.2 94.8 71c-9.4-9.4-24.5-9.4-33.9 0L7 124.9c-9.4 9.4-9.4 24.5 0 33.9l97.2 97.2L7 353.2c-9.4 9.4-9.4 24.5 0 33.9L60.9 441c9.4 9.4 24.5 9.4 33.9 0l97.2-97.2 97.2 97.2c9.3 9.3 24.5 9.3 33.9 0z"],
      "times-circle": [512, 512, [], "f057", "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"],
      "wrench": [512, 512, [], "f0ad", "M481.156 200c9.3 0 15.12 10.155 10.325 18.124C466.295 259.992 420.419 288 368 288c-79.222 0-143.501-63.974-143.997-143.079C223.505 65.469 288.548-.001 368.002 0c52.362.001 98.196 27.949 123.4 69.743C496.24 77.766 490.523 88 481.154 88H376l-40 56 40 56h105.156zm-171.649 93.003L109.255 493.255c-24.994 24.993-65.515 24.994-90.51 0-24.993-24.994-24.993-65.516 0-90.51L218.991 202.5c16.16 41.197 49.303 74.335 90.516 90.503zM104 432c0-13.255-10.745-24-24-24s-24 10.745-24 24 10.745 24 24 24 24-10.745 24-24z"]
    };

    function attachDialogCloseHandlerWithParent$1(parent, closeHandler) {
      var container = document.createElement("div");
      parent.appendChild(container);
      container.appendChild(createIcon$1("times"));
      container.addEventListener('click', function (e) {
        e.preventDefault();
        e.stopPropagation();
        closeHandler();
      });
    }

    /**
     * Covers string literals and String objects
     * @param x
     * @returns {boolean}
     */
    function isString$3(x) {
      return typeof x === "string" || x instanceof String;
    } // StackOverflow: http://stackoverflow.com/a/10810674/116169


    function numberFormatter$1(rawNumber) {
      var dec = String(rawNumber).split(/[.,]/),
          sep = ',',
          decsep = '.';
      return dec[0].split('').reverse().reduce(function (prev, now, i) {
        return i % 3 === 0 ? prev + sep + now : prev + now;
      }).split('').reverse().join('') + (dec[1] ? decsep + dec[1] : '');
    }

    const splitLines$5 = function (string) {
      return string.split(/\n|\r\n|\r/g);
    };

    function splitStringRespectingQuotes(string, delim) {
      var tokens = [],
          len = string.length,
          i,
          n = 0,
          quote = false,
          c;

      if (len > 0) {
        tokens[n] = string.charAt(0);

        for (i = 1; i < len; i++) {
          c = string.charAt(i);

          if (c === '"') {
            quote = !quote;
          } else if (!quote && c === delim) {
            n++;
            tokens[n] = "";
          } else {
            tokens[n] += c;
          }
        }
      }

      return tokens;
    }

    function stripQuotes$1(str) {
      if (str === undefined) {
        return str;
      }

      if (str.startsWith("'") || str.startsWith('"')) {
        str = str.substring(1);
      }

      if (str.endsWith("'") || str.endsWith('"')) {
        str = str.substring(0, str.length - 1);
      }

      return str;
    }

    function hashCode$1(s) {
      return s.split("").reduce(function (a, b) {
        a = (a << 5) - a + b.charCodeAt(0);
        return a & a;
      }, 0);
    }

    function capitalize(str) {
      return str.length > 0 ? str.charAt(0).toUpperCase() + str.slice(1) : str;
    }
    /**
     * Parse a locus string and return a range object.  Locus string is of the form chr:start-end.  End is optional
     *
     */


    function parseLocusString$1(string) {
      const t1 = string.split(":");
      const t2 = t1[1].split("-");
      const range = {
        chr: t1[0],
        start: Number.parseInt(t2[0].replace(/,/g, '')) - 1
      };

      if (t2.length > 1) {
        range.end = Number.parseInt(t2[1].replace(/,/g, ''));
      } else {
        range.end = range.start + 1;
      }

      return range;
    }

    function isGoogleURL(url) {
      return url.includes("googleapis") && !url.includes("urlshortener") || isGoogleStorageURL(url) || isGoogleDriveURL(url);
    }

    function isGoogleStorageURL(url) {
      return url.startsWith("gs://") || url.startsWith("https://www.googleapis.com/storage") || url.startsWith("https://storage.cloud.google.com") || url.startsWith("https://storage.googleapis.com");
    }

    function isGoogleDriveURL(url) {
      return url.indexOf("drive.google.com") >= 0 || url.indexOf("www.googleapis.com/drive") > 0;
    }
    /**
     * Translate gs:// urls to https
     * See https://cloud.google.com/storage/docs/json_api/v1
     * @param gsUrl
     * @returns {string|*}
     */


    function translateGoogleCloudURL(gsUrl) {
      let {
        bucket,
        object
      } = parseBucketName(gsUrl);
      object = encode(object);
      const qIdx = gsUrl.indexOf('?');
      const paramString = qIdx > 0 ? gsUrl.substring(qIdx) + "&alt=media" : "?alt=media";
      return `https://storage.googleapis.com/storage/v1/b/${bucket}/o/${object}${paramString}`;
    }
    /**
     * Parse a google bucket and object name from a google storage URL.  Known forms include
     *
     * gs://BUCKET_NAME/OBJECT_NAME
     * https://storage.googleapis.com/BUCKET_NAME/OBJECT_NAME
     * https://storage.googleapis.com/storage/v1/b/BUCKET_NAME/o/OBJECT_NAME
     * https://www.googleapis.com/storage/v1/b/BUCKET_NAME/o/OBJECT_NAME"
     * https://storage.googleapis.com/download/storage/v1/b/BUCKET_NAME/o/OBJECT_NAME
     *
     * @param url
     */


    function parseBucketName(url) {
      let bucket;
      let object;

      if (url.startsWith("gs://")) {
        const i = url.indexOf('/', 5);

        if (i >= 0) {
          bucket = url.substring(5, i);
          const qIdx = url.indexOf('?');
          object = qIdx < 0 ? url.substring(i + 1) : url.substring(i + 1, qIdx);
        }
      } else if (url.startsWith("https://storage.googleapis.com") || url.startsWith("https://storage.cloud.google.com")) {
        const bucketIdx = url.indexOf("/v1/b/", 8);

        if (bucketIdx > 0) {
          const objIdx = url.indexOf("/o/", bucketIdx);

          if (objIdx > 0) {
            const queryIdx = url.indexOf("?", objIdx);
            bucket = url.substring(bucketIdx + 6, objIdx);
            object = queryIdx > 0 ? url.substring(objIdx + 3, queryIdx) : url.substring(objIdx + 3);
          }
        } else {
          const idx1 = url.indexOf("/", 8);
          const idx2 = url.indexOf("/", idx1 + 1);
          const idx3 = url.indexOf("?", idx2);

          if (idx2 > 0) {
            bucket = url.substring(idx1 + 1, idx2);
            object = idx3 < 0 ? url.substring(idx2 + 1) : url.substring(idx2 + 1, idx3);
          }
        }
      } else if (url.startsWith("https://www.googleapis.com/storage/v1/b")) {
        const bucketIdx = url.indexOf("/v1/b/", 8);
        const objIdx = url.indexOf("/o/", bucketIdx);

        if (objIdx > 0) {
          const queryIdx = url.indexOf("?", objIdx);
          bucket = url.substring(bucketIdx + 6, objIdx);
          object = queryIdx > 0 ? url.substring(objIdx + 3, queryIdx) : url.substring(objIdx + 3);
        }
      }

      if (bucket && object) {
        return {
          bucket,
          object
        };
      } else {
        throw Error(`Unrecognized Google Storage URI: ${url}`);
      }
    }

    function driveDownloadURL(link) {
      // Return a google drive download url for the sharable link
      //https://drive.google.com/open?id=0B-lleX9c2pZFbDJ4VVRxakJzVGM
      //https://drive.google.com/file/d/1_FC4kCeO8E3V4dJ1yIW7A0sn1yURKIX-/view?usp=sharing
      var id = getGoogleDriveFileID(link);
      return id ? "https://www.googleapis.com/drive/v3/files/" + id + "?alt=media&supportsTeamDrives=true" : link;
    }

    function getGoogleDriveFileID(link) {
      //https://drive.google.com/file/d/1_FC4kCeO8E3V4dJ1yIW7A0sn1yURKIX-/view?usp=sharing
      //https://www.googleapis.com/drive/v3/files/1w-tvo6p1SH4p1OaQSVxpkV_EJgGIstWF?alt=media&supportsTeamDrives=true"
      if (link.includes("/open?id=")) {
        const i1 = link.indexOf("/open?id=") + 9;
        const i2 = link.indexOf("&");

        if (i1 > 0 && i2 > i1) {
          return link.substring(i1, i2);
        } else if (i1 > 0) {
          return link.substring(i1);
        }
      } else if (link.includes("/file/d/")) {
        const i1 = link.indexOf("/file/d/") + 8;
        const i2 = link.lastIndexOf("/");
        return link.substring(i1, i2);
      } else if (link.startsWith("https://www.googleapis.com/drive")) {
        let i1 = link.indexOf("/files/");
        const i2 = link.indexOf("?");

        if (i1 > 0) {
          i1 += 7;
          return i2 > 0 ? link.substring(i1, i2) : link.substring(i1);
        }
      }

      throw Error("Unknown Google Drive url format: " + link);
    }
    /**
     * Percent a GCS object name.  See https://cloud.google.com/storage/docs/request-endpoints
     * Specific characters to encode:
     *   !, #, $, &, ', (, ), *, +, ,, /, :, ;, =, ?, @, [, ], and space characters.
     * @param obj
     */


    function encode(objectName) {
      let result = '';
      objectName.split('').forEach(function (letter) {
        if (encodings.has(letter)) {
          result += encodings.get(letter);
        } else {
          result += letter;
        }
      });
      return result;
    } //	%23	%24	%25	%26	%27	%28	%29	%2A	%2B	%2C	%2F	%3A	%3B	%3D	%3F	%40	%5B	%5D


    const encodings = new Map();
    encodings.set("!", "%21");
    encodings.set("#", "%23");
    encodings.set("$", "%24");
    encodings.set("%", "%25");
    encodings.set("&", "%26");
    encodings.set("'", "%27");
    encodings.set("(", "%28");
    encodings.set(")", "%29");
    encodings.set("*", "%2A");
    encodings.set("+", "%2B");
    encodings.set(",", "%2C");
    encodings.set("/", "%2F");
    encodings.set(":", "%3A");
    encodings.set(";", "%3B");
    encodings.set("=", "%3D");
    encodings.set("?", "%3F");
    encodings.set("@", "%40");
    encodings.set("[", "%5B");
    encodings.set("]", "%5D");
    encodings.set(" ", "%20"); // For testing

    // Convenience functions for the gapi oAuth library.
    const FIVE_MINUTES = 5 * 60 * 1000;

    async function load$1(library) {
      return new Promise(function (resolve, reject) {
        gapi.load(library, {
          callback: resolve,
          onerror: reject
        });
      });
    }

    async function init$1(config) {
      if (isInitialized()) {
        console.warn("oAuth has already been initialized");
        return;
      }

      gapi.apiKey = config.apiKey; // copy config, gapi will modify it

      const configCopy = Object.assign({}, config);

      if (!configCopy.scope) {
        configCopy.scope = 'profile';
      }

      if (!config.client_id) {
        config.client_id = config.clientId;
      }

      await load$1("auth2");
      return new Promise(function (resolve, reject) {
        gapi.auth2.init(configCopy).then(resolve, reject);
      });
    }

    function isInitialized() {
      return typeof gapi !== "undefined" && gapi.auth2 && gapi.auth2.getAuthInstance();
    }

    let inProgress = false;

    async function getAccessToken(scope) {
      if (typeof gapi === "undefined") {
        throw Error("Google authentication requires the 'gapi' library");
      }

      if (!gapi.auth2) {
        throw Error("Google 'auth2' has not been initialized");
      }

      if (inProgress) {
        return new Promise(function (resolve, reject) {
          let intervalID;

          const checkForToken = () => {
            // Wait for inProgress to equal "false"
            try {
              if (inProgress === false) {
                //console.log("Delayed resolution for " + scope);
                resolve(getAccessToken(scope));
                clearInterval(intervalID);
              }
            } catch (e) {
              clearInterval(intervalID);
              reject(e);
            }
          };

          intervalID = setInterval(checkForToken, 100);
        });
      } else {
        inProgress = true;

        try {
          let currentUser = gapi.auth2.getAuthInstance().currentUser.get();
          let token;

          if (currentUser.isSignedIn()) {
            if (!currentUser.hasGrantedScopes(scope)) {
              await currentUser.grant({
                scope
              });
            }

            const {
              access_token,
              expires_at
            } = currentUser.getAuthResponse();

            if (Date.now() < expires_at - FIVE_MINUTES) {
              token = {
                access_token,
                expires_at
              };
            } else {
              const {
                access_token,
                expires_at
              } = currentUser.reloadAuthResponse();
              token = {
                access_token,
                expires_at
              };
            }
          } else {
            currentUser = await signIn(scope);
            const {
              access_token,
              expires_at
            } = currentUser.getAuthResponse();
            token = {
              access_token,
              expires_at
            };
          }

          return token;
        } finally {
          inProgress = false;
        }
      }
    }
    /**
     * Return the current access token if the user is signed in, or undefined otherwise.  This function does not
     * attempt a signIn or request any specfic scopes.
     *
     * @returns access_token || undefined
     */


    function getCurrentAccessToken() {
      let currentUser = gapi.auth2.getAuthInstance().currentUser.get();

      if (currentUser && currentUser.isSignedIn()) {
        const {
          access_token,
          expires_at
        } = currentUser.getAuthResponse();
        return {
          access_token,
          expires_at
        };
      } else {
        return undefined;
      }
    }

    async function signIn(scope) {
      const options = new gapi.auth2.SigninOptionsBuilder();
      options.setPrompt('select_account');
      options.setScope(scope);
      return gapi.auth2.getAuthInstance().signIn(options);
    }

    function getScopeForURL(url) {
      if (isGoogleDriveURL(url)) {
        return "https://www.googleapis.com/auth/drive.file";
      } else if (isGoogleStorageURL(url)) {
        return "https://www.googleapis.com/auth/devstorage.read_only";
      } else {
        return 'https://www.googleapis.com/auth/userinfo.profile';
      }
    }

    function getApiKey() {
      return gapi.apiKey;
    }

    async function getDriveFileInfo(googleDriveURL) {
      const id = getGoogleDriveFileID(googleDriveURL);
      let endPoint = "https://www.googleapis.com/drive/v3/files/" + id + "?supportsTeamDrives=true";
      const apiKey = getApiKey();

      if (apiKey) {
        endPoint += "&key=" + apiKey;
      }

      const response = await fetch(endPoint);
      let json = await response.json();

      if (json.error && json.error.code === 404) {
        const {
          access_token
        } = await getAccessToken("https://www.googleapis.com/auth/drive.readonly");

        if (access_token) {
          const response = await fetch(endPoint, {
            headers: {
              'Authorization': `Bearer ${access_token}`
            }
          });
          json = await response.json();

          if (json.error) {
            throw Error(json.error);
          }
        } else {
          throw Error(json.error);
        }
      }

      return json;
    }

    if (typeof process === 'object' && typeof window === 'undefined') {
      global.atob = function (str) {
        return Buffer.from(str, 'base64').toString('binary');
      };
    }

    function parseUri(str) {
      var o = options,
          m = o.parser["loose"].exec(str),
          uri = {},
          i = 14;

      while (i--) uri[o.key[i]] = m[i] || "";

      uri[o.q.name] = {};
      uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
        if ($1) uri[o.q.name][$1] = $2;
      });
      return uri;
    }

    const options = {
      strictMode: false,
      key: ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "query", "anchor"],
      q: {
        name: "queryKey",
        parser: /(?:^|&)([^&=]*)=?([^&]*)/g
      },
      parser: {
        strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
        loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
      }
    };
    /**
     * Resolve a url, which might be a string, function (that returns a string or Promse), or Promise (that resolves to a string)
     *
     * @param url
     * @returns {Promise<*>}
     */


    async function resolveURL(url) {
      return typeof url === 'function' ? url() : url;
    }

    /**
     * Return the filename from the path.   Example
     *   https://foo.com/bar.bed?param=2   => bar.bed
     * @param urlOrFile
     */


    function getFilename$1(urlOrFile) {
      if (urlOrFile.name !== undefined) {
        return urlOrFile.name;
      } else if (isString$3(urlOrFile)) {
        let index = urlOrFile.lastIndexOf("/");
        let filename = index < 0 ? urlOrFile : urlOrFile.substr(index + 1); //Strip parameters -- handle local files later

        index = filename.indexOf("?");

        if (index > 0) {
          filename = filename.substr(0, index);
        }

        return filename;
      } else {
        throw Error(`Expected File or string, got ${typeof urlOrFile}`);
      }
    }
    /**
     * Test if object is a File or File-like object.
     *
     * @param object
     */


    function isFile(object) {
      if (!object) {
        return false;
      }

      return typeof object !== 'function' && (object instanceof File || object.hasOwnProperty("name") && typeof object.slice === 'function' && typeof object.arrayBuffer === 'function');
    }

    const isFilePath = isFile; // deprecated

    function download(filename, data) {
      const element = document.createElement('a');
      element.setAttribute('href', data);
      element.setAttribute('download', filename);
      element.style.display = 'none';
      document.body.appendChild(element);
      element.click();
      document.body.removeChild(element);
    }

    // Ths file is a modification of the pako distribution https://github.com/nodeca/pako.   The modifications
    // consists of simple changes to create an ES6 module, specifically commenting out the function wrapper and
    // adding an ES6 export statement.
    // **************  COMMENTED OUT BY JTR ******************//

    /*! pako 2.0.4 https://github.com/nodeca/pako @license (MIT AND Zlib) */
    // (function (global, factory) {
    //   typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    //   typeof define === 'function' && define.amd ? define(['exports'], factory) :
    //   (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.pako = {}));
    // }(this, (function (exports) { 'use strict';
    // **************  COMMENTED OUT BY JTR ******************//
    // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    /* eslint-disable space-unary-ops */

    /* Public constants ==========================================================*/

    /* ===========================================================================*/
    //const Z_FILTERED          = 1;
    //const Z_HUFFMAN_ONLY      = 2;
    //const Z_RLE               = 3;
    const Z_FIXED$1 = 4; //const Z_DEFAULT_STRATEGY  = 0;

    /* Possible values of the data_type field (though see inflate()) */

    const Z_BINARY = 0;
    const Z_TEXT = 1; //const Z_ASCII             = 1; // = Z_TEXT

    const Z_UNKNOWN$1 = 2;
    /*============================================================================*/

    function zero$1(buf) {
      let len = buf.length;

      while (--len >= 0) {
        buf[len] = 0;
      }
    } // From zutil.h


    const STORED_BLOCK = 0;
    const STATIC_TREES = 1;
    const DYN_TREES = 2;
    /* The three kinds of block type */

    const MIN_MATCH$1 = 3;
    const MAX_MATCH$1 = 258;
    /* The minimum and maximum match lengths */
    // From deflate.h

    /* ===========================================================================
     * Internal compression state.
     */

    const LENGTH_CODES$1 = 29;
    /* number of length codes, not counting the special END_BLOCK code */

    const LITERALS$1 = 256;
    /* number of literal bytes 0..255 */

    const L_CODES$1 = LITERALS$1 + 1 + LENGTH_CODES$1;
    /* number of Literal or Length codes, including the END_BLOCK code */

    const D_CODES$1 = 30;
    /* number of distance codes */

    const BL_CODES$1 = 19;
    /* number of codes used to transfer the bit lengths */

    const HEAP_SIZE$1 = 2 * L_CODES$1 + 1;
    /* maximum heap size */

    const MAX_BITS$1 = 15;
    /* All codes must not exceed MAX_BITS bits */

    const Buf_size = 16;
    /* size of bit buffer in bi_buf */

    /* ===========================================================================
     * Constants
     */

    const MAX_BL_BITS = 7;
    /* Bit length codes must not exceed MAX_BL_BITS bits */

    const END_BLOCK = 256;
    /* end of block literal code */

    const REP_3_6 = 16;
    /* repeat previous bit length 3-6 times (2 bits of repeat count) */

    const REPZ_3_10 = 17;
    /* repeat a zero length 3-10 times  (3 bits of repeat count) */

    const REPZ_11_138 = 18;
    /* repeat a zero length 11-138 times  (7 bits of repeat count) */

    /* eslint-disable comma-spacing,array-bracket-spacing */

    const extra_lbits =
    /* extra bits for each length code */
    new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0]);
    const extra_dbits =
    /* extra bits for each distance code */
    new Uint8Array([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13]);
    const extra_blbits =
    /* extra bits for each bit length code */
    new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7]);
    const bl_order = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
    /* eslint-enable comma-spacing,array-bracket-spacing */

    /* The lengths of the bit length codes are sent in order of decreasing
     * probability, to avoid transmitting the lengths for unused bit length codes.
     */

    /* ===========================================================================
     * Local data. These are initialized only once.
     */
    // We pre-fill arrays with 0 to avoid uninitialized gaps

    const DIST_CODE_LEN = 512;
    /* see definition of array dist_code below */
    // !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1

    const static_ltree = new Array((L_CODES$1 + 2) * 2);
    zero$1(static_ltree);
    /* The static literal tree. Since the bit lengths are imposed, there is no
     * need for the L_CODES extra codes used during heap construction. However
     * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
     * below).
     */

    const static_dtree = new Array(D_CODES$1 * 2);
    zero$1(static_dtree);
    /* The static distance tree. (Actually a trivial tree since all codes use
     * 5 bits.)
     */

    const _dist_code = new Array(DIST_CODE_LEN);

    zero$1(_dist_code);
    /* Distance codes. The first 256 values correspond to the distances
     * 3 .. 258, the last 256 values correspond to the top 8 bits of
     * the 15 bit distances.
     */

    const _length_code = new Array(MAX_MATCH$1 - MIN_MATCH$1 + 1);

    zero$1(_length_code);
    /* length code for each normalized match length (0 == MIN_MATCH) */

    const base_length = new Array(LENGTH_CODES$1);
    zero$1(base_length);
    /* First normalized length for each code (0 = MIN_MATCH) */

    const base_dist = new Array(D_CODES$1);
    zero$1(base_dist);
    /* First normalized distance for each code (0 = distance of 1) */

    function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) {
      this.static_tree = static_tree;
      /* static tree or NULL */

      this.extra_bits = extra_bits;
      /* extra bits for each code or NULL */

      this.extra_base = extra_base;
      /* base index for extra_bits */

      this.elems = elems;
      /* max number of elements in the tree */

      this.max_length = max_length;
      /* max bit length for the codes */
      // show if `static_tree` has data or dummy - needed for monomorphic objects

      this.has_stree = static_tree && static_tree.length;
    }

    let static_l_desc;
    let static_d_desc;
    let static_bl_desc;

    function TreeDesc(dyn_tree, stat_desc) {
      this.dyn_tree = dyn_tree;
      /* the dynamic tree */

      this.max_code = 0;
      /* largest code with non zero frequency */

      this.stat_desc = stat_desc;
      /* the corresponding static tree */
    }

    const d_code = dist => {
      return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)];
    };
    /* ===========================================================================
     * Output a short LSB first on the stream.
     * IN assertion: there is enough room in pendingBuf.
     */


    const put_short = (s, w) => {
      //    put_byte(s, (uch)((w) & 0xff));
      //    put_byte(s, (uch)((ush)(w) >> 8));
      s.pending_buf[s.pending++] = w & 0xff;
      s.pending_buf[s.pending++] = w >>> 8 & 0xff;
    };
    /* ===========================================================================
     * Send a value on a given number of bits.
     * IN assertion: length <= 16 and value fits in length bits.
     */


    const send_bits = (s, value, length) => {
      if (s.bi_valid > Buf_size - length) {
        s.bi_buf |= value << s.bi_valid & 0xffff;
        put_short(s, s.bi_buf);
        s.bi_buf = value >> Buf_size - s.bi_valid;
        s.bi_valid += length - Buf_size;
      } else {
        s.bi_buf |= value << s.bi_valid & 0xffff;
        s.bi_valid += length;
      }
    };

    const send_code = (s, c, tree) => {
      send_bits(s, tree[c * 2]
      /*.Code*/
      , tree[c * 2 + 1]
      /*.Len*/
      );
    };
    /* ===========================================================================
     * Reverse the first len bits of a code, using straightforward code (a faster
     * method would use a table)
     * IN assertion: 1 <= len <= 15
     */


    const bi_reverse = (code, len) => {
      let res = 0;

      do {
        res |= code & 1;
        code >>>= 1;
        res <<= 1;
      } while (--len > 0);

      return res >>> 1;
    };
    /* ===========================================================================
     * Flush the bit buffer, keeping at most 7 bits in it.
     */


    const bi_flush = s => {
      if (s.bi_valid === 16) {
        put_short(s, s.bi_buf);
        s.bi_buf = 0;
        s.bi_valid = 0;
      } else if (s.bi_valid >= 8) {
        s.pending_buf[s.pending++] = s.bi_buf & 0xff;
        s.bi_buf >>= 8;
        s.bi_valid -= 8;
      }
    };
    /* ===========================================================================
     * Compute the optimal bit lengths for a tree and update the total bit length
     * for the current block.
     * IN assertion: the fields freq and dad are set, heap[heap_max] and
     *    above are the tree nodes sorted by increasing frequency.
     * OUT assertions: the field len is set to the optimal bit length, the
     *     array bl_count contains the frequencies for each bit length.
     *     The length opt_len is updated; static_len is also updated if stree is
     *     not null.
     */


    const gen_bitlen = (s, desc) => //    deflate_state *s;
    //    tree_desc *desc;    /* the tree descriptor */
    {
      const tree = desc.dyn_tree;
      const max_code = desc.max_code;
      const stree = desc.stat_desc.static_tree;
      const has_stree = desc.stat_desc.has_stree;
      const extra = desc.stat_desc.extra_bits;
      const base = desc.stat_desc.extra_base;
      const max_length = desc.stat_desc.max_length;
      let h;
      /* heap index */

      let n, m;
      /* iterate over the tree elements */

      let bits;
      /* bit length */

      let xbits;
      /* extra bits */

      let f;
      /* frequency */

      let overflow = 0;
      /* number of elements with bit length too large */

      for (bits = 0; bits <= MAX_BITS$1; bits++) {
        s.bl_count[bits] = 0;
      }
      /* In a first pass, compute the optimal bit lengths (which may
       * overflow in the case of the bit length tree).
       */


      tree[s.heap[s.heap_max] * 2 + 1]
      /*.Len*/
      = 0;
      /* root of the heap */

      for (h = s.heap_max + 1; h < HEAP_SIZE$1; h++) {
        n = s.heap[h];
        bits = tree[tree[n * 2 + 1]
        /*.Dad*/
        * 2 + 1]
        /*.Len*/
        + 1;

        if (bits > max_length) {
          bits = max_length;
          overflow++;
        }

        tree[n * 2 + 1]
        /*.Len*/
        = bits;
        /* We overwrite tree[n].Dad which is no longer needed */

        if (n > max_code) {
          continue;
        }
        /* not a leaf node */


        s.bl_count[bits]++;
        xbits = 0;

        if (n >= base) {
          xbits = extra[n - base];
        }

        f = tree[n * 2]
        /*.Freq*/
        ;
        s.opt_len += f * (bits + xbits);

        if (has_stree) {
          s.static_len += f * (stree[n * 2 + 1]
          /*.Len*/
          + xbits);
        }
      }

      if (overflow === 0) {
        return;
      } // Trace((stderr,"\nbit length overflow\n"));

      /* This happens for example on obj2 and pic of the Calgary corpus */

      /* Find the first bit length which could increase: */


      do {
        bits = max_length - 1;

        while (s.bl_count[bits] === 0) {
          bits--;
        }

        s.bl_count[bits]--;
        /* move one leaf down the tree */

        s.bl_count[bits + 1] += 2;
        /* move one overflow item as its brother */

        s.bl_count[max_length]--;
        /* The brother of the overflow item also moves one step up,
         * but this does not affect bl_count[max_length]
         */

        overflow -= 2;
      } while (overflow > 0);
      /* Now recompute all bit lengths, scanning in increasing frequency.
       * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
       * lengths instead of fixing only the wrong ones. This idea is taken
       * from 'ar' written by Haruhiko Okumura.)
       */


      for (bits = max_length; bits !== 0; bits--) {
        n = s.bl_count[bits];

        while (n !== 0) {
          m = s.heap[--h];

          if (m > max_code) {
            continue;
          }

          if (tree[m * 2 + 1]
          /*.Len*/
          !== bits) {
            // Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
            s.opt_len += (bits - tree[m * 2 + 1]
            /*.Len*/
            ) * tree[m * 2]
            /*.Freq*/
            ;
            tree[m * 2 + 1]
            /*.Len*/
            = bits;
          }

          n--;
        }
      }
    };
    /* ===========================================================================
     * Generate the codes for a given tree and bit counts (which need not be
     * optimal).
     * IN assertion: the array bl_count contains the bit length statistics for
     * the given tree and the field len is set for all tree elements.
     * OUT assertion: the field code is set for all tree elements of non
     *     zero code length.
     */


    const gen_codes = (tree, max_code, bl_count) => //    ct_data *tree;             /* the tree to decorate */
    //    int max_code;              /* largest code with non zero frequency */
    //    ushf *bl_count;            /* number of codes at each bit length */
    {
      const next_code = new Array(MAX_BITS$1 + 1);
      /* next code value for each bit length */

      let code = 0;
      /* running code value */

      let bits;
      /* bit index */

      let n;
      /* code index */

      /* The distribution counts are first used to generate the code values
       * without bit reversal.
       */

      for (bits = 1; bits <= MAX_BITS$1; bits++) {
        next_code[bits] = code = code + bl_count[bits - 1] << 1;
      }
      /* Check that the bit counts in bl_count are consistent. The last code
       * must be all ones.
       */
      //Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
      //        "inconsistent bit counts");
      //Tracev((stderr,"\ngen_codes: max_code %d ", max_code));


      for (n = 0; n <= max_code; n++) {
        let len = tree[n * 2 + 1]
        /*.Len*/
        ;

        if (len === 0) {
          continue;
        }
        /* Now reverse the bits */


        tree[n * 2]
        /*.Code*/
        = bi_reverse(next_code[len]++, len); //Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
        //     n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
      }
    };
    /* ===========================================================================
     * Initialize the various 'constant' tables.
     */


    const tr_static_init = () => {
      let n;
      /* iterates over tree elements */

      let bits;
      /* bit counter */

      let length;
      /* length value */

      let code;
      /* code value */

      let dist;
      /* distance index */

      const bl_count = new Array(MAX_BITS$1 + 1);
      /* number of codes at each bit length for an optimal tree */
      // do check in _tr_init()
      //if (static_init_done) return;

      /* For some embedded targets, global variables are not initialized: */

      /*#ifdef NO_INIT_GLOBAL_POINTERS
        static_l_desc.static_tree = static_ltree;
        static_l_desc.extra_bits = extra_lbits;
        static_d_desc.static_tree = static_dtree;
        static_d_desc.extra_bits = extra_dbits;
        static_bl_desc.extra_bits = extra_blbits;
      #endif*/

      /* Initialize the mapping length (0..255) -> length code (0..28) */

      length = 0;

      for (code = 0; code < LENGTH_CODES$1 - 1; code++) {
        base_length[code] = length;

        for (n = 0; n < 1 << extra_lbits[code]; n++) {
          _length_code[length++] = code;
        }
      } //Assert (length == 256, "tr_static_init: length != 256");

      /* Note that the length 255 (match length 258) can be represented
       * in two different ways: code 284 + 5 bits or code 285, so we
       * overwrite length_code[255] to use the best encoding:
       */


      _length_code[length - 1] = code;
      /* Initialize the mapping dist (0..32K) -> dist code (0..29) */

      dist = 0;

      for (code = 0; code < 16; code++) {
        base_dist[code] = dist;

        for (n = 0; n < 1 << extra_dbits[code]; n++) {
          _dist_code[dist++] = code;
        }
      } //Assert (dist == 256, "tr_static_init: dist != 256");


      dist >>= 7;
      /* from now on, all distances are divided by 128 */

      for (; code < D_CODES$1; code++) {
        base_dist[code] = dist << 7;

        for (n = 0; n < 1 << extra_dbits[code] - 7; n++) {
          _dist_code[256 + dist++] = code;
        }
      } //Assert (dist == 256, "tr_static_init: 256+dist != 512");

      /* Construct the codes of the static literal tree */


      for (bits = 0; bits <= MAX_BITS$1; bits++) {
        bl_count[bits] = 0;
      }

      n = 0;

      while (n <= 143) {
        static_ltree[n * 2 + 1]
        /*.Len*/
        = 8;
        n++;
        bl_count[8]++;
      }

      while (n <= 255) {
        static_ltree[n * 2 + 1]
        /*.Len*/
        = 9;
        n++;
        bl_count[9]++;
      }

      while (n <= 279) {
        static_ltree[n * 2 + 1]
        /*.Len*/
        = 7;
        n++;
        bl_count[7]++;
      }

      while (n <= 287) {
        static_ltree[n * 2 + 1]
        /*.Len*/
        = 8;
        n++;
        bl_count[8]++;
      }
      /* Codes 286 and 287 do not exist, but we must include them in the
       * tree construction to get a canonical Huffman tree (longest code
       * all ones)
       */


      gen_codes(static_ltree, L_CODES$1 + 1, bl_count);
      /* The static distance tree is trivial: */

      for (n = 0; n < D_CODES$1; n++) {
        static_dtree[n * 2 + 1]
        /*.Len*/
        = 5;
        static_dtree[n * 2]
        /*.Code*/
        = bi_reverse(n, 5);
      } // Now data ready and we can init static trees


      static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS$1 + 1, L_CODES$1, MAX_BITS$1);
      static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES$1, MAX_BITS$1);
      static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES$1, MAX_BL_BITS); //static_init_done = true;
    };
    /* ===========================================================================
     * Initialize a new block.
     */


    const init_block = s => {
      let n;
      /* iterates over tree elements */

      /* Initialize the trees. */

      for (n = 0; n < L_CODES$1; n++) {
        s.dyn_ltree[n * 2]
        /*.Freq*/
        = 0;
      }

      for (n = 0; n < D_CODES$1; n++) {
        s.dyn_dtree[n * 2]
        /*.Freq*/
        = 0;
      }

      for (n = 0; n < BL_CODES$1; n++) {
        s.bl_tree[n * 2]
        /*.Freq*/
        = 0;
      }

      s.dyn_ltree[END_BLOCK * 2]
      /*.Freq*/
      = 1;
      s.opt_len = s.static_len = 0;
      s.last_lit = s.matches = 0;
    };
    /* ===========================================================================
     * Flush the bit buffer and align the output on a byte boundary
     */


    const bi_windup = s => {
      if (s.bi_valid > 8) {
        put_short(s, s.bi_buf);
      } else if (s.bi_valid > 0) {
        //put_byte(s, (Byte)s->bi_buf);
        s.pending_buf[s.pending++] = s.bi_buf;
      }

      s.bi_buf = 0;
      s.bi_valid = 0;
    };
    /* ===========================================================================
     * Copy a stored block, storing first the length and its
     * one's complement if requested.
     */


    const copy_block = (s, buf, len, header) => //DeflateState *s;
    //charf    *buf;    /* the input data */
    //unsigned len;     /* its length */
    //int      header;  /* true if block header must be written */
    {
      bi_windup(s);
      /* align on byte boundary */

      if (header) {
        put_short(s, len);
        put_short(s, ~len);
      } //  while (len--) {
      //    put_byte(s, *buf++);
      //  }


      s.pending_buf.set(s.window.subarray(buf, buf + len), s.pending);
      s.pending += len;
    };
    /* ===========================================================================
     * Compares to subtrees, using the tree depth as tie breaker when
     * the subtrees have equal frequency. This minimizes the worst case length.
     */


    const smaller = (tree, n, m, depth) => {
      const _n2 = n * 2;

      const _m2 = m * 2;

      return tree[_n2]
      /*.Freq*/
      < tree[_m2]
      /*.Freq*/
      || tree[_n2]
      /*.Freq*/
      === tree[_m2]
      /*.Freq*/
      && depth[n] <= depth[m];
    };
    /* ===========================================================================
     * Restore the heap property by moving down the tree starting at node k,
     * exchanging a node with the smallest of its two sons if necessary, stopping
     * when the heap property is re-established (each father smaller than its
     * two sons).
     */


    const pqdownheap = (s, tree, k) => //    deflate_state *s;
    //    ct_data *tree;  /* the tree to restore */
    //    int k;               /* node to move down */
    {
      const v = s.heap[k];
      let j = k << 1;
      /* left son of k */

      while (j <= s.heap_len) {
        /* Set j to the smallest of the two sons: */
        if (j < s.heap_len && smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) {
          j++;
        }
        /* Exit if v is smaller than both sons */


        if (smaller(tree, v, s.heap[j], s.depth)) {
          break;
        }
        /* Exchange v with the smallest son */


        s.heap[k] = s.heap[j];
        k = j;
        /* And continue down the tree, setting j to the left son of k */

        j <<= 1;
      }

      s.heap[k] = v;
    }; // inlined manually
    // const SMALLEST = 1;

    /* ===========================================================================
     * Send the block data compressed using the given Huffman trees
     */


    const compress_block = (s, ltree, dtree) => //    deflate_state *s;
    //    const ct_data *ltree; /* literal tree */
    //    const ct_data *dtree; /* distance tree */
    {
      let dist;
      /* distance of matched string */

      let lc;
      /* match length or unmatched char (if dist == 0) */

      let lx = 0;
      /* running index in l_buf */

      let code;
      /* the code to send */

      let extra;
      /* number of extra bits to send */

      if (s.last_lit !== 0) {
        do {
          dist = s.pending_buf[s.d_buf + lx * 2] << 8 | s.pending_buf[s.d_buf + lx * 2 + 1];
          lc = s.pending_buf[s.l_buf + lx];
          lx++;

          if (dist === 0) {
            send_code(s, lc, ltree);
            /* send a literal byte */
            //Tracecv(isgraph(lc), (stderr," '%c' ", lc));
          } else {
            /* Here, lc is the match length - MIN_MATCH */
            code = _length_code[lc];
            send_code(s, code + LITERALS$1 + 1, ltree);
            /* send the length code */

            extra = extra_lbits[code];

            if (extra !== 0) {
              lc -= base_length[code];
              send_bits(s, lc, extra);
              /* send the extra length bits */
            }

            dist--;
            /* dist is now the match distance - 1 */

            code = d_code(dist); //Assert (code < D_CODES, "bad d_code");

            send_code(s, code, dtree);
            /* send the distance code */

            extra = extra_dbits[code];

            if (extra !== 0) {
              dist -= base_dist[code];
              send_bits(s, dist, extra);
              /* send the extra distance bits */
            }
          }
          /* literal or match pair ? */

          /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
          //Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
          //       "pendingBuf overflow");

        } while (lx < s.last_lit);
      }

      send_code(s, END_BLOCK, ltree);
    };
    /* ===========================================================================
     * Construct one Huffman tree and assigns the code bit strings and lengths.
     * Update the total bit length for the current block.
     * IN assertion: the field freq is set for all tree elements.
     * OUT assertions: the fields len and code are set to the optimal bit length
     *     and corresponding code. The length opt_len is updated; static_len is
     *     also updated if stree is not null. The field max_code is set.
     */


    const build_tree = (s, desc) => //    deflate_state *s;
    //    tree_desc *desc; /* the tree descriptor */
    {
      const tree = desc.dyn_tree;
      const stree = desc.stat_desc.static_tree;
      const has_stree = desc.stat_desc.has_stree;
      const elems = desc.stat_desc.elems;
      let n, m;
      /* iterate over heap elements */

      let max_code = -1;
      /* largest code with non zero frequency */

      let node;
      /* new node being created */

      /* Construct the initial heap, with least frequent element in
       * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
       * heap[0] is not used.
       */

      s.heap_len = 0;
      s.heap_max = HEAP_SIZE$1;

      for (n = 0; n < elems; n++) {
        if (tree[n * 2]
        /*.Freq*/
        !== 0) {
          s.heap[++s.heap_len] = max_code = n;
          s.depth[n] = 0;
        } else {
          tree[n * 2 + 1]
          /*.Len*/
          = 0;
        }
      }
      /* The pkzip format requires that at least one distance code exists,
       * and that at least one bit should be sent even if there is only one
       * possible code. So to avoid special checks later on we force at least
       * two codes of non zero frequency.
       */


      while (s.heap_len < 2) {
        node = s.heap[++s.heap_len] = max_code < 2 ? ++max_code : 0;
        tree[node * 2]
        /*.Freq*/
        = 1;
        s.depth[node] = 0;
        s.opt_len--;

        if (has_stree) {
          s.static_len -= stree[node * 2 + 1]
          /*.Len*/
          ;
        }
        /* node is 0 or 1 so it does not have extra bits */

      }

      desc.max_code = max_code;
      /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
       * establish sub-heaps of increasing lengths:
       */

      for (n = s.heap_len >> 1
      /*int /2*/
      ; n >= 1; n--) {
        pqdownheap(s, tree, n);
      }
      /* Construct the Huffman tree by repeatedly combining the least two
       * frequent nodes.
       */


      node = elems;
      /* next internal node of the tree */

      do {
        //pqremove(s, tree, n);  /* n = node of least frequency */

        /*** pqremove ***/
        n = s.heap[1
        /*SMALLEST*/
        ];
        s.heap[1
        /*SMALLEST*/
        ] = s.heap[s.heap_len--];
        pqdownheap(s, tree, 1
        /*SMALLEST*/
        );
        /***/

        m = s.heap[1
        /*SMALLEST*/
        ];
        /* m = node of next least frequency */

        s.heap[--s.heap_max] = n;
        /* keep the nodes sorted by frequency */

        s.heap[--s.heap_max] = m;
        /* Create a new node father of n and m */

        tree[node * 2]
        /*.Freq*/
        = tree[n * 2]
        /*.Freq*/
        + tree[m * 2]
        /*.Freq*/
        ;
        s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1;
        tree[n * 2 + 1]
        /*.Dad*/
        = tree[m * 2 + 1]
        /*.Dad*/
        = node;
        /* and insert the new node in the heap */

        s.heap[1
        /*SMALLEST*/
        ] = node++;
        pqdownheap(s, tree, 1
        /*SMALLEST*/
        );
      } while (s.heap_len >= 2);

      s.heap[--s.heap_max] = s.heap[1
      /*SMALLEST*/
      ];
      /* At this point, the fields freq and dad are set. We can now
       * generate the bit lengths.
       */

      gen_bitlen(s, desc);
      /* The field len is now set, we can generate the bit codes */

      gen_codes(tree, max_code, s.bl_count);
    };
    /* ===========================================================================
     * Scan a literal or distance tree to determine the frequencies of the codes
     * in the bit length tree.
     */


    const scan_tree = (s, tree, max_code) => //    deflate_state *s;
    //    ct_data *tree;   /* the tree to be scanned */
    //    int max_code;    /* and its largest code of non zero frequency */
    {
      let n;
      /* iterates over all tree elements */

      let prevlen = -1;
      /* last emitted length */

      let curlen;
      /* length of current code */

      let nextlen = tree[0 * 2 + 1]
      /*.Len*/
      ;
      /* length of next code */

      let count = 0;
      /* repeat count of the current code */

      let max_count = 7;
      /* max repeat count */

      let min_count = 4;
      /* min repeat count */

      if (nextlen === 0) {
        max_count = 138;
        min_count = 3;
      }

      tree[(max_code + 1) * 2 + 1]
      /*.Len*/
      = 0xffff;
      /* guard */

      for (n = 0; n <= max_code; n++) {
        curlen = nextlen;
        nextlen = tree[(n + 1) * 2 + 1]
        /*.Len*/
        ;

        if (++count < max_count && curlen === nextlen) {
          continue;
        } else if (count < min_count) {
          s.bl_tree[curlen * 2]
          /*.Freq*/
          += count;
        } else if (curlen !== 0) {
          if (curlen !== prevlen) {
            s.bl_tree[curlen * 2] /*.Freq*/++;
          }

          s.bl_tree[REP_3_6 * 2] /*.Freq*/++;
        } else if (count <= 10) {
          s.bl_tree[REPZ_3_10 * 2] /*.Freq*/++;
        } else {
          s.bl_tree[REPZ_11_138 * 2] /*.Freq*/++;
        }

        count = 0;
        prevlen = curlen;

        if (nextlen === 0) {
          max_count = 138;
          min_count = 3;
        } else if (curlen === nextlen) {
          max_count = 6;
          min_count = 3;
        } else {
          max_count = 7;
          min_count = 4;
        }
      }
    };
    /* ===========================================================================
     * Send a literal or distance tree in compressed form, using the codes in
     * bl_tree.
     */


    const send_tree = (s, tree, max_code) => //    deflate_state *s;
    //    ct_data *tree; /* the tree to be scanned */
    //    int max_code;       /* and its largest code of non zero frequency */
    {
      let n;
      /* iterates over all tree elements */

      let prevlen = -1;
      /* last emitted length */

      let curlen;
      /* length of current code */

      let nextlen = tree[0 * 2 + 1]
      /*.Len*/
      ;
      /* length of next code */

      let count = 0;
      /* repeat count of the current code */

      let max_count = 7;
      /* max repeat count */

      let min_count = 4;
      /* min repeat count */

      /* tree[max_code+1].Len = -1; */

      /* guard already set */

      if (nextlen === 0) {
        max_count = 138;
        min_count = 3;
      }

      for (n = 0; n <= max_code; n++) {
        curlen = nextlen;
        nextlen = tree[(n + 1) * 2 + 1]
        /*.Len*/
        ;

        if (++count < max_count && curlen === nextlen) {
          continue;
        } else if (count < min_count) {
          do {
            send_code(s, curlen, s.bl_tree);
          } while (--count !== 0);
        } else if (curlen !== 0) {
          if (curlen !== prevlen) {
            send_code(s, curlen, s.bl_tree);
            count--;
          } //Assert(count >= 3 && count <= 6, " 3_6?");


          send_code(s, REP_3_6, s.bl_tree);
          send_bits(s, count - 3, 2);
        } else if (count <= 10) {
          send_code(s, REPZ_3_10, s.bl_tree);
          send_bits(s, count - 3, 3);
        } else {
          send_code(s, REPZ_11_138, s.bl_tree);
          send_bits(s, count - 11, 7);
        }

        count = 0;
        prevlen = curlen;

        if (nextlen === 0) {
          max_count = 138;
          min_count = 3;
        } else if (curlen === nextlen) {
          max_count = 6;
          min_count = 3;
        } else {
          max_count = 7;
          min_count = 4;
        }
      }
    };
    /* ===========================================================================
     * Construct the Huffman tree for the bit lengths and return the index in
     * bl_order of the last bit length code to send.
     */


    const build_bl_tree = s => {
      let max_blindex;
      /* index of last bit length code of non zero freq */

      /* Determine the bit length frequencies for literal and distance trees */

      scan_tree(s, s.dyn_ltree, s.l_desc.max_code);
      scan_tree(s, s.dyn_dtree, s.d_desc.max_code);
      /* Build the bit length tree: */

      build_tree(s, s.bl_desc);
      /* opt_len now includes the length of the tree representations, except
       * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
       */

      /* Determine the number of bit length codes to send. The pkzip format
       * requires that at least 4 bit length codes be sent. (appnote.txt says
       * 3 but the actual value used is 4.)
       */

      for (max_blindex = BL_CODES$1 - 1; max_blindex >= 3; max_blindex--) {
        if (s.bl_tree[bl_order[max_blindex] * 2 + 1]
        /*.Len*/
        !== 0) {
          break;
        }
      }
      /* Update opt_len to include the bit length tree and counts */


      s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; //Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
      //        s->opt_len, s->static_len));

      return max_blindex;
    };
    /* ===========================================================================
     * Send the header for a block using dynamic Huffman trees: the counts, the
     * lengths of the bit length codes, the literal tree and the distance tree.
     * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
     */


    const send_all_trees = (s, lcodes, dcodes, blcodes) => //    deflate_state *s;
    //    int lcodes, dcodes, blcodes; /* number of codes for each tree */
    {
      let rank;
      /* index in bl_order */
      //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
      //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
      //        "too many codes");
      //Tracev((stderr, "\nbl counts: "));

      send_bits(s, lcodes - 257, 5);
      /* not +255 as stated in appnote.txt */

      send_bits(s, dcodes - 1, 5);
      send_bits(s, blcodes - 4, 4);
      /* not -3 as stated in appnote.txt */

      for (rank = 0; rank < blcodes; rank++) {
        //Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
        send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]
        /*.Len*/
        , 3);
      } //Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));


      send_tree(s, s.dyn_ltree, lcodes - 1);
      /* literal tree */
      //Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));

      send_tree(s, s.dyn_dtree, dcodes - 1);
      /* distance tree */
      //Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
    };
    /* ===========================================================================
     * Check if the data type is TEXT or BINARY, using the following algorithm:
     * - TEXT if the two conditions below are satisfied:
     *    a) There are no non-portable control characters belonging to the
     *       "black list" (0..6, 14..25, 28..31).
     *    b) There is at least one printable character belonging to the
     *       "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
     * - BINARY otherwise.
     * - The following partially-portable control characters form a
     *   "gray list" that is ignored in this detection algorithm:
     *   (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
     * IN assertion: the fields Freq of dyn_ltree are set.
     */


    const detect_data_type = s => {
      /* black_mask is the bit mask of black-listed bytes
       * set bits 0..6, 14..25, and 28..31
       * 0xf3ffc07f = binary 11110011111111111100000001111111
       */
      let black_mask = 0xf3ffc07f;
      let n;
      /* Check for non-textual ("black-listed") bytes. */

      for (n = 0; n <= 31; n++, black_mask >>>= 1) {
        if (black_mask & 1 && s.dyn_ltree[n * 2]
        /*.Freq*/
        !== 0) {
          return Z_BINARY;
        }
      }
      /* Check for textual ("white-listed") bytes. */


      if (s.dyn_ltree[9 * 2]
      /*.Freq*/
      !== 0 || s.dyn_ltree[10 * 2]
      /*.Freq*/
      !== 0 || s.dyn_ltree[13 * 2]
      /*.Freq*/
      !== 0) {
        return Z_TEXT;
      }

      for (n = 32; n < LITERALS$1; n++) {
        if (s.dyn_ltree[n * 2]
        /*.Freq*/
        !== 0) {
          return Z_TEXT;
        }
      }
      /* There are no "black-listed" or "white-listed" bytes:
       * this stream either is empty or has tolerated ("gray-listed") bytes only.
       */


      return Z_BINARY;
    };

    let static_init_done = false;
    /* ===========================================================================
     * Initialize the tree data structures for a new zlib stream.
     */

    const _tr_init$1 = s => {
      if (!static_init_done) {
        tr_static_init();
        static_init_done = true;
      }

      s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc);
      s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc);
      s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc);
      s.bi_buf = 0;
      s.bi_valid = 0;
      /* Initialize the first block of the first file: */

      init_block(s);
    };
    /* ===========================================================================
     * Send a stored block
     */


    const _tr_stored_block$1 = (s, buf, stored_len, last) => //DeflateState *s;
    //charf *buf;       /* input block */
    //ulg stored_len;   /* length of input block */
    //int last;         /* one if this is the last block for a file */
    {
      send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3);
      /* send block type */

      copy_block(s, buf, stored_len, true);
      /* with header */
    };
    /* ===========================================================================
     * Send one empty static block to give enough lookahead for inflate.
     * This takes 10 bits, of which 7 may remain in the bit buffer.
     */


    const _tr_align$1 = s => {
      send_bits(s, STATIC_TREES << 1, 3);
      send_code(s, END_BLOCK, static_ltree);
      bi_flush(s);
    };
    /* ===========================================================================
     * Determine the best encoding for the current block: dynamic trees, static
     * trees or store, and output the encoded block to the zip file.
     */


    const _tr_flush_block$1 = (s, buf, stored_len, last) => //DeflateState *s;
    //charf *buf;       /* input block, or NULL if too old */
    //ulg stored_len;   /* length of input block */
    //int last;         /* one if this is the last block for a file */
    {
      let opt_lenb, static_lenb;
      /* opt_len and static_len in bytes */

      let max_blindex = 0;
      /* index of last bit length code of non zero freq */

      /* Build the Huffman trees unless a stored block is forced */

      if (s.level > 0) {
        /* Check if the file is binary or text */
        if (s.strm.data_type === Z_UNKNOWN$1) {
          s.strm.data_type = detect_data_type(s);
        }
        /* Construct the literal and distance trees */


        build_tree(s, s.l_desc); // Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
        //        s->static_len));

        build_tree(s, s.d_desc); // Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
        //        s->static_len));

        /* At this point, opt_len and static_len are the total bit lengths of
         * the compressed block data, excluding the tree representations.
         */

        /* Build the bit length tree for the above two trees, and get the index
         * in bl_order of the last bit length code to send.
         */

        max_blindex = build_bl_tree(s);
        /* Determine the best encoding. Compute the block lengths in bytes. */

        opt_lenb = s.opt_len + 3 + 7 >>> 3;
        static_lenb = s.static_len + 3 + 7 >>> 3; // Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
        //        opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
        //        s->last_lit));

        if (static_lenb <= opt_lenb) {
          opt_lenb = static_lenb;
        }
      } else {
        // Assert(buf != (char*)0, "lost buf");
        opt_lenb = static_lenb = stored_len + 5;
        /* force a stored block */
      }

      if (stored_len + 4 <= opt_lenb && buf !== -1) {
        /* 4: two words for the lengths */

        /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
         * Otherwise we can't have processed more than WSIZE input bytes since
         * the last block flush, because compression would have been
         * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
         * transform a block into a stored block.
         */
        _tr_stored_block$1(s, buf, stored_len, last);
      } else if (s.strategy === Z_FIXED$1 || static_lenb === opt_lenb) {
        send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3);
        compress_block(s, static_ltree, static_dtree);
      } else {
        send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3);
        send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1);
        compress_block(s, s.dyn_ltree, s.dyn_dtree);
      } // Assert (s->compressed_len == s->bits_sent, "bad compressed size");

      /* The above check is made mod 2^32, for files larger than 512 MB
       * and uLong implemented on 32 bits.
       */


      init_block(s);

      if (last) {
        bi_windup(s);
      } // Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
      //       s->compressed_len-7*last));

    };
    /* ===========================================================================
     * Save the match info and tally the frequency counts. Return true if
     * the current block must be flushed.
     */


    const _tr_tally$1 = (s, dist, lc) => //    deflate_state *s;
    //    unsigned dist;  /* distance of matched string */
    //    unsigned lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */
    {
      //let out_length, in_length, dcode;
      s.pending_buf[s.d_buf + s.last_lit * 2] = dist >>> 8 & 0xff;
      s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff;
      s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff;
      s.last_lit++;

      if (dist === 0) {
        /* lc is the unmatched char */
        s.dyn_ltree[lc * 2] /*.Freq*/++;
      } else {
        s.matches++;
        /* Here, lc is the match length - MIN_MATCH */

        dist--;
        /* dist = match distance - 1 */
        //Assert((ush)dist < (ush)MAX_DIST(s) &&
        //       (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
        //       (ush)d_code(dist) < (ush)D_CODES,  "_tr_tally: bad match");

        s.dyn_ltree[(_length_code[lc] + LITERALS$1 + 1) * 2] /*.Freq*/++;
        s.dyn_dtree[d_code(dist) * 2] /*.Freq*/++;
      } // (!) This block is disabled in zlib defaults,
      // don't enable it for binary compatibility
      //#ifdef TRUNCATE_BLOCK
      //  /* Try to guess if it is profitable to stop the current block here */
      //  if ((s.last_lit & 0x1fff) === 0 && s.level > 2) {
      //    /* Compute an upper bound for the compressed length */
      //    out_length = s.last_lit*8;
      //    in_length = s.strstart - s.block_start;
      //
      //    for (dcode = 0; dcode < D_CODES; dcode++) {
      //      out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]);
      //    }
      //    out_length >>>= 3;
      //    //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
      //    //       s->last_lit, in_length, out_length,
      //    //       100L - out_length*100L/in_length));
      //    if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) {
      //      return true;
      //    }
      //  }
      //#endif


      return s.last_lit === s.lit_bufsize - 1;
      /* We avoid equality with lit_bufsize because of wraparound at 64K
       * on 16 bit machines and because stored blocks are restricted to
       * 64K-1 bytes.
       */
    };

    var _tr_init_1 = _tr_init$1;
    var _tr_stored_block_1 = _tr_stored_block$1;
    var _tr_flush_block_1 = _tr_flush_block$1;
    var _tr_tally_1 = _tr_tally$1;
    var _tr_align_1 = _tr_align$1;
    var trees = {
      _tr_init: _tr_init_1,
      _tr_stored_block: _tr_stored_block_1,
      _tr_flush_block: _tr_flush_block_1,
      _tr_tally: _tr_tally_1,
      _tr_align: _tr_align_1
    }; // Note: adler32 takes 12% for level 0 and 2% for level 6.
    // It isn't worth it to make additional optimizations as in original.
    // Small size is preferable.
    // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    const adler32 = (adler, buf, len, pos) => {
      let s1 = adler & 0xffff | 0,
          s2 = adler >>> 16 & 0xffff | 0,
          n = 0;

      while (len !== 0) {
        // Set limit ~ twice less than 5552, to keep
        // s2 in 31-bits, because we force signed ints.
        // in other case %= will fail.
        n = len > 2000 ? 2000 : len;
        len -= n;

        do {
          s1 = s1 + buf[pos++] | 0;
          s2 = s2 + s1 | 0;
        } while (--n);

        s1 %= 65521;
        s2 %= 65521;
      }

      return s1 | s2 << 16 | 0;
    };

    var adler32_1 = adler32; // Note: we can't get significant speed boost here.
    // So write code to minimize size - no pregenerated tables
    // and array tools dependencies.
    // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.
    // Use ordinary array, since untyped makes no boost here

    const makeTable = () => {
      let c,
          table = [];

      for (var n = 0; n < 256; n++) {
        c = n;

        for (var k = 0; k < 8; k++) {
          c = c & 1 ? 0xEDB88320 ^ c >>> 1 : c >>> 1;
        }

        table[n] = c;
      }

      return table;
    }; // Create table on load. Just 255 signed longs. Not a problem.


    const crcTable = new Uint32Array(makeTable());

    const crc32 = (crc, buf, len, pos) => {
      const t = crcTable;
      const end = pos + len;
      crc ^= -1;

      for (let i = pos; i < end; i++) {
        crc = crc >>> 8 ^ t[(crc ^ buf[i]) & 0xFF];
      }

      return crc ^ -1; // >>> 0;
    };

    var crc32_1 = crc32; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    var messages = {
      2: 'need dictionary',

      /* Z_NEED_DICT       2  */
      1: 'stream end',

      /* Z_STREAM_END      1  */
      0: '',

      /* Z_OK              0  */
      '-1': 'file error',

      /* Z_ERRNO         (-1) */
      '-2': 'stream error',

      /* Z_STREAM_ERROR  (-2) */
      '-3': 'data error',

      /* Z_DATA_ERROR    (-3) */
      '-4': 'insufficient memory',

      /* Z_MEM_ERROR     (-4) */
      '-5': 'buffer error',

      /* Z_BUF_ERROR     (-5) */
      '-6': 'incompatible version'
      /* Z_VERSION_ERROR (-6) */

    }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    var constants$2 = {
      /* Allowed flush values; see deflate() and inflate() below for details */
      Z_NO_FLUSH: 0,
      Z_PARTIAL_FLUSH: 1,
      Z_SYNC_FLUSH: 2,
      Z_FULL_FLUSH: 3,
      Z_FINISH: 4,
      Z_BLOCK: 5,
      Z_TREES: 6,

      /* Return codes for the compression/decompression functions. Negative values
      * are errors, positive values are used for special but normal events.
      */
      Z_OK: 0,
      Z_STREAM_END: 1,
      Z_NEED_DICT: 2,
      Z_ERRNO: -1,
      Z_STREAM_ERROR: -2,
      Z_DATA_ERROR: -3,
      Z_MEM_ERROR: -4,
      Z_BUF_ERROR: -5,
      //Z_VERSION_ERROR: -6,

      /* compression levels */
      Z_NO_COMPRESSION: 0,
      Z_BEST_SPEED: 1,
      Z_BEST_COMPRESSION: 9,
      Z_DEFAULT_COMPRESSION: -1,
      Z_FILTERED: 1,
      Z_HUFFMAN_ONLY: 2,
      Z_RLE: 3,
      Z_FIXED: 4,
      Z_DEFAULT_STRATEGY: 0,

      /* Possible values of the data_type field (though see inflate()) */
      Z_BINARY: 0,
      Z_TEXT: 1,
      //Z_ASCII:                1, // = Z_TEXT (deprecated)
      Z_UNKNOWN: 2,

      /* The deflate compression method */
      Z_DEFLATED: 8 //Z_NULL:                 null // Use -1 or null inline, depending on var type

    }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    const {
      _tr_init,
      _tr_stored_block,
      _tr_flush_block,
      _tr_tally,
      _tr_align
    } = trees;
    /* Public constants ==========================================================*/

    /* ===========================================================================*/

    const {
      Z_NO_FLUSH: Z_NO_FLUSH$2,
      Z_PARTIAL_FLUSH,
      Z_FULL_FLUSH: Z_FULL_FLUSH$1,
      Z_FINISH: Z_FINISH$3,
      Z_BLOCK: Z_BLOCK$1,
      Z_OK: Z_OK$3,
      Z_STREAM_END: Z_STREAM_END$3,
      Z_STREAM_ERROR: Z_STREAM_ERROR$2,
      Z_DATA_ERROR: Z_DATA_ERROR$2,
      Z_BUF_ERROR: Z_BUF_ERROR$1,
      Z_DEFAULT_COMPRESSION: Z_DEFAULT_COMPRESSION$1,
      Z_FILTERED,
      Z_HUFFMAN_ONLY,
      Z_RLE,
      Z_FIXED,
      Z_DEFAULT_STRATEGY: Z_DEFAULT_STRATEGY$1,
      Z_UNKNOWN,
      Z_DEFLATED: Z_DEFLATED$2
    } = constants$2;
    /*============================================================================*/

    const MAX_MEM_LEVEL = 9;
    /* Maximum value for memLevel in deflateInit2 */

    const MAX_WBITS$1 = 15;
    /* 32K LZ77 window */

    const DEF_MEM_LEVEL = 8;
    const LENGTH_CODES = 29;
    /* number of length codes, not counting the special END_BLOCK code */

    const LITERALS = 256;
    /* number of literal bytes 0..255 */

    const L_CODES = LITERALS + 1 + LENGTH_CODES;
    /* number of Literal or Length codes, including the END_BLOCK code */

    const D_CODES = 30;
    /* number of distance codes */

    const BL_CODES = 19;
    /* number of codes used to transfer the bit lengths */

    const HEAP_SIZE = 2 * L_CODES + 1;
    /* maximum heap size */

    const MAX_BITS = 15;
    /* All codes must not exceed MAX_BITS bits */

    const MIN_MATCH = 3;
    const MAX_MATCH = 258;
    const MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1;
    const PRESET_DICT = 0x20;
    const INIT_STATE = 42;
    const EXTRA_STATE = 69;
    const NAME_STATE = 73;
    const COMMENT_STATE = 91;
    const HCRC_STATE = 103;
    const BUSY_STATE = 113;
    const FINISH_STATE = 666;
    const BS_NEED_MORE = 1;
    /* block not completed, need more input or more output */

    const BS_BLOCK_DONE = 2;
    /* block flush performed */

    const BS_FINISH_STARTED = 3;
    /* finish started, need only more output at next deflate */

    const BS_FINISH_DONE = 4;
    /* finish done, accept no more input or output */

    const OS_CODE = 0x03; // Unix :) . Don't detect, use this default.

    const err = (strm, errorCode) => {
      strm.msg = messages[errorCode];
      return errorCode;
    };

    const rank = f => {
      return (f << 1) - (f > 4 ? 9 : 0);
    };

    const zero = buf => {
      let len = buf.length;

      while (--len >= 0) {
        buf[len] = 0;
      }
    };
    /* eslint-disable new-cap */


    let HASH_ZLIB = (s, prev, data) => (prev << s.hash_shift ^ data) & s.hash_mask; // This hash causes less collisions, https://github.com/nodeca/pako/issues/135
    // But breaks binary compatibility
    //let HASH_FAST = (s, prev, data) => ((prev << 8) + (prev >> 8) + (data << 4)) & s.hash_mask;


    let HASH = HASH_ZLIB;
    /* =========================================================================
     * Flush as much pending output as possible. All deflate() output goes
     * through this function so some applications may wish to modify it
     * to avoid allocating a large strm->output buffer and copying into it.
     * (See also read_buf()).
     */

    const flush_pending = strm => {
      const s = strm.state; //_tr_flush_bits(s);

      let len = s.pending;

      if (len > strm.avail_out) {
        len = strm.avail_out;
      }

      if (len === 0) {
        return;
      }

      strm.output.set(s.pending_buf.subarray(s.pending_out, s.pending_out + len), strm.next_out);
      strm.next_out += len;
      s.pending_out += len;
      strm.total_out += len;
      strm.avail_out -= len;
      s.pending -= len;

      if (s.pending === 0) {
        s.pending_out = 0;
      }
    };

    const flush_block_only = (s, last) => {
      _tr_flush_block(s, s.block_start >= 0 ? s.block_start : -1, s.strstart - s.block_start, last);

      s.block_start = s.strstart;
      flush_pending(s.strm);
    };

    const put_byte = (s, b) => {
      s.pending_buf[s.pending++] = b;
    };
    /* =========================================================================
     * Put a short in the pending buffer. The 16-bit value is put in MSB order.
     * IN assertion: the stream state is correct and there is enough room in
     * pending_buf.
     */


    const putShortMSB = (s, b) => {
      //  put_byte(s, (Byte)(b >> 8));
      //  put_byte(s, (Byte)(b & 0xff));
      s.pending_buf[s.pending++] = b >>> 8 & 0xff;
      s.pending_buf[s.pending++] = b & 0xff;
    };
    /* ===========================================================================
     * Read a new buffer from the current input stream, update the adler32
     * and total number of bytes read.  All deflate() input goes through
     * this function so some applications may wish to modify it to avoid
     * allocating a large strm->input buffer and copying from it.
     * (See also flush_pending()).
     */


    const read_buf = (strm, buf, start, size) => {
      let len = strm.avail_in;

      if (len > size) {
        len = size;
      }

      if (len === 0) {
        return 0;
      }

      strm.avail_in -= len; // zmemcpy(buf, strm->next_in, len);

      buf.set(strm.input.subarray(strm.next_in, strm.next_in + len), start);

      if (strm.state.wrap === 1) {
        strm.adler = adler32_1(strm.adler, buf, len, start);
      } else if (strm.state.wrap === 2) {
        strm.adler = crc32_1(strm.adler, buf, len, start);
      }

      strm.next_in += len;
      strm.total_in += len;
      return len;
    };
    /* ===========================================================================
     * Set match_start to the longest match starting at the given string and
     * return its length. Matches shorter or equal to prev_length are discarded,
     * in which case the result is equal to prev_length and match_start is
     * garbage.
     * IN assertions: cur_match is the head of the hash chain for the current
     *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
     * OUT assertion: the match length is not greater than s->lookahead.
     */


    const longest_match = (s, cur_match) => {
      let chain_length = s.max_chain_length;
      /* max hash chain length */

      let scan = s.strstart;
      /* current string */

      let match;
      /* matched string */

      let len;
      /* length of current match */

      let best_len = s.prev_length;
      /* best match length so far */

      let nice_match = s.nice_match;
      /* stop if match long enough */

      const limit = s.strstart > s.w_size - MIN_LOOKAHEAD ? s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0
      /*NIL*/
      ;
      const _win = s.window; // shortcut

      const wmask = s.w_mask;
      const prev = s.prev;
      /* Stop when cur_match becomes <= limit. To simplify the code,
       * we prevent matches with the string of window index 0.
       */

      const strend = s.strstart + MAX_MATCH;
      let scan_end1 = _win[scan + best_len - 1];
      let scan_end = _win[scan + best_len];
      /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
       * It is easy to get rid of this optimization if necessary.
       */
      // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");

      /* Do not waste too much time if we already have a good match: */

      if (s.prev_length >= s.good_match) {
        chain_length >>= 2;
      }
      /* Do not look for matches beyond the end of the input. This is necessary
       * to make deflate deterministic.
       */


      if (nice_match > s.lookahead) {
        nice_match = s.lookahead;
      } // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");


      do {
        // Assert(cur_match < s->strstart, "no future");
        match = cur_match;
        /* Skip to next match if the match length cannot increase
         * or if the match length is less than 2.  Note that the checks below
         * for insufficient lookahead only occur occasionally for performance
         * reasons.  Therefore uninitialized memory will be accessed, and
         * conditional jumps will be made that depend on those values.
         * However the length of the match is limited to the lookahead, so
         * the output of deflate is not affected by the uninitialized values.
         */

        if (_win[match + best_len] !== scan_end || _win[match + best_len - 1] !== scan_end1 || _win[match] !== _win[scan] || _win[++match] !== _win[scan + 1]) {
          continue;
        }
        /* The check at best_len-1 can be removed because it will be made
         * again later. (This heuristic is not always a win.)
         * It is not necessary to compare scan[2] and match[2] since they
         * are always equal when the other bytes match, given that
         * the hash keys are equal and that HASH_BITS >= 8.
         */


        scan += 2;
        match++; // Assert(*scan == *match, "match[2]?");

        /* We check for insufficient lookahead only every 8th comparison;
         * the 256th check will be made at strstart+258.
         */

        do {
          /*jshint noempty:false*/
        } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && scan < strend); // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");


        len = MAX_MATCH - (strend - scan);
        scan = strend - MAX_MATCH;

        if (len > best_len) {
          s.match_start = cur_match;
          best_len = len;

          if (len >= nice_match) {
            break;
          }

          scan_end1 = _win[scan + best_len - 1];
          scan_end = _win[scan + best_len];
        }
      } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0);

      if (best_len <= s.lookahead) {
        return best_len;
      }

      return s.lookahead;
    };
    /* ===========================================================================
     * Fill the window when the lookahead becomes insufficient.
     * Updates strstart and lookahead.
     *
     * IN assertion: lookahead < MIN_LOOKAHEAD
     * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
     *    At least one byte has been read, or avail_in == 0; reads are
     *    performed for at least two bytes (required for the zip translate_eol
     *    option -- not supported here).
     */


    const fill_window = s => {
      const _w_size = s.w_size;
      let p, n, m, more, str; //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");

      do {
        more = s.window_size - s.lookahead - s.strstart; // JS ints have 32 bit, block below not needed

        /* Deal with !@#$% 64K limit: */
        //if (sizeof(int) <= 2) {
        //    if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
        //        more = wsize;
        //
        //  } else if (more == (unsigned)(-1)) {
        //        /* Very unlikely, but possible on 16 bit machine if
        //         * strstart == 0 && lookahead == 1 (input done a byte at time)
        //         */
        //        more--;
        //    }
        //}

        /* If the window is almost full and there is insufficient lookahead,
         * move the upper half to the lower one to make room in the upper half.
         */

        if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) {
          s.window.set(s.window.subarray(_w_size, _w_size + _w_size), 0);
          s.match_start -= _w_size;
          s.strstart -= _w_size;
          /* we now have strstart >= MAX_DIST */

          s.block_start -= _w_size;
          /* Slide the hash table (could be avoided with 32 bit values
           at the expense of memory usage). We slide even when level == 0
           to keep the hash table consistent if we switch back to level > 0
           later. (Using level 0 permanently is not an optimal usage of
           zlib, so we don't care about this pathological case.)
           */

          n = s.hash_size;
          p = n;

          do {
            m = s.head[--p];
            s.head[p] = m >= _w_size ? m - _w_size : 0;
          } while (--n);

          n = _w_size;
          p = n;

          do {
            m = s.prev[--p];
            s.prev[p] = m >= _w_size ? m - _w_size : 0;
            /* If n is not on any hash chain, prev[n] is garbage but
             * its value will never be used.
             */
          } while (--n);

          more += _w_size;
        }

        if (s.strm.avail_in === 0) {
          break;
        }
        /* If there was no sliding:
         *    strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
         *    more == window_size - lookahead - strstart
         * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
         * => more >= window_size - 2*WSIZE + 2
         * In the BIG_MEM or MMAP case (not yet supported),
         *   window_size == input_size + MIN_LOOKAHEAD  &&
         *   strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
         * Otherwise, window_size == 2*WSIZE so more >= 2.
         * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
         */
        //Assert(more >= 2, "more < 2");


        n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more);
        s.lookahead += n;
        /* Initialize the hash value now that we have some input: */

        if (s.lookahead + s.insert >= MIN_MATCH) {
          str = s.strstart - s.insert;
          s.ins_h = s.window[str];
          /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */

          s.ins_h = HASH(s, s.ins_h, s.window[str + 1]); //#if MIN_MATCH != 3
          //        Call update_hash() MIN_MATCH-3 more times
          //#endif

          while (s.insert) {
            /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
            s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]);
            s.prev[str & s.w_mask] = s.head[s.ins_h];
            s.head[s.ins_h] = str;
            str++;
            s.insert--;

            if (s.lookahead + s.insert < MIN_MATCH) {
              break;
            }
          }
        }
        /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
         * but this is not important since only literal bytes will be emitted.
         */

      } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0);
      /* If the WIN_INIT bytes after the end of the current data have never been
       * written, then zero those bytes in order to avoid memory check reports of
       * the use of uninitialized (or uninitialised as Julian writes) bytes by
       * the longest match routines.  Update the high water mark for the next
       * time through here.  WIN_INIT is set to MAX_MATCH since the longest match
       * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
       */
      //  if (s.high_water < s.window_size) {
      //    const curr = s.strstart + s.lookahead;
      //    let init = 0;
      //
      //    if (s.high_water < curr) {
      //      /* Previous high water mark below current data -- zero WIN_INIT
      //       * bytes or up to end of window, whichever is less.
      //       */
      //      init = s.window_size - curr;
      //      if (init > WIN_INIT)
      //        init = WIN_INIT;
      //      zmemzero(s->window + curr, (unsigned)init);
      //      s->high_water = curr + init;
      //    }
      //    else if (s->high_water < (ulg)curr + WIN_INIT) {
      //      /* High water mark at or above current data, but below current data
      //       * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
      //       * to end of window, whichever is less.
      //       */
      //      init = (ulg)curr + WIN_INIT - s->high_water;
      //      if (init > s->window_size - s->high_water)
      //        init = s->window_size - s->high_water;
      //      zmemzero(s->window + s->high_water, (unsigned)init);
      //      s->high_water += init;
      //    }
      //  }
      //
      //  Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
      //    "not enough room for search");

    };
    /* ===========================================================================
     * Copy without compression as much as possible from the input stream, return
     * the current block state.
     * This function does not insert new strings in the dictionary since
     * uncompressible data is probably not useful. This function is used
     * only for the level=0 compression option.
     * NOTE: this function should be optimized to avoid extra copying from
     * window to pending_buf.
     */


    const deflate_stored = (s, flush) => {
      /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
       * to pending_buf_size, and each stored block has a 5 byte header:
       */
      let max_block_size = 0xffff;

      if (max_block_size > s.pending_buf_size - 5) {
        max_block_size = s.pending_buf_size - 5;
      }
      /* Copy as much as possible from input to output: */


      for (;;) {
        /* Fill the window as much as possible: */
        if (s.lookahead <= 1) {
          //Assert(s->strstart < s->w_size+MAX_DIST(s) ||
          //  s->block_start >= (long)s->w_size, "slide too late");
          //      if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) ||
          //        s.block_start >= s.w_size)) {
          //        throw  new Error("slide too late");
          //      }
          fill_window(s);

          if (s.lookahead === 0 && flush === Z_NO_FLUSH$2) {
            return BS_NEED_MORE;
          }

          if (s.lookahead === 0) {
            break;
          }
          /* flush the current block */

        } //Assert(s->block_start >= 0L, "block gone");
        //    if (s.block_start < 0) throw new Error("block gone");


        s.strstart += s.lookahead;
        s.lookahead = 0;
        /* Emit a stored block if pending_buf will be full: */

        const max_start = s.block_start + max_block_size;

        if (s.strstart === 0 || s.strstart >= max_start) {
          /* strstart == 0 is possible when wraparound on 16-bit machine */
          s.lookahead = s.strstart - max_start;
          s.strstart = max_start;
          /*** FLUSH_BLOCK(s, 0); ***/

          flush_block_only(s, false);

          if (s.strm.avail_out === 0) {
            return BS_NEED_MORE;
          }
          /***/

        }
        /* Flush if we may have to slide, otherwise block_start may become
         * negative and the data will be gone:
         */


        if (s.strstart - s.block_start >= s.w_size - MIN_LOOKAHEAD) {
          /*** FLUSH_BLOCK(s, 0); ***/
          flush_block_only(s, false);

          if (s.strm.avail_out === 0) {
            return BS_NEED_MORE;
          }
          /***/

        }
      }

      s.insert = 0;

      if (flush === Z_FINISH$3) {
        /*** FLUSH_BLOCK(s, 1); ***/
        flush_block_only(s, true);

        if (s.strm.avail_out === 0) {
          return BS_FINISH_STARTED;
        }
        /***/


        return BS_FINISH_DONE;
      }

      if (s.strstart > s.block_start) {
        /*** FLUSH_BLOCK(s, 0); ***/
        flush_block_only(s, false);

        if (s.strm.avail_out === 0) {
          return BS_NEED_MORE;
        }
        /***/

      }

      return BS_NEED_MORE;
    };
    /* ===========================================================================
     * Compress as much as possible from the input stream, return the current
     * block state.
     * This function does not perform lazy evaluation of matches and inserts
     * new strings in the dictionary only for unmatched strings or for short
     * matches. It is used only for the fast compression options.
     */


    const deflate_fast = (s, flush) => {
      let hash_head;
      /* head of the hash chain */

      let bflush;
      /* set if current block must be flushed */

      for (;;) {
        /* Make sure that we always have enough lookahead, except
         * at the end of the input file. We need MAX_MATCH bytes
         * for the next match, plus MIN_MATCH bytes to insert the
         * string following the next match.
         */
        if (s.lookahead < MIN_LOOKAHEAD) {
          fill_window(s);

          if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) {
            return BS_NEED_MORE;
          }

          if (s.lookahead === 0) {
            break;
            /* flush the current block */
          }
        }
        /* Insert the string window[strstart .. strstart+2] in the
         * dictionary, and set hash_head to the head of the hash chain:
         */


        hash_head = 0
        /*NIL*/
        ;

        if (s.lookahead >= MIN_MATCH) {
          /*** INSERT_STRING(s, s.strstart, hash_head); ***/
          s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);
          hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
          s.head[s.ins_h] = s.strstart;
          /***/
        }
        /* Find the longest match, discarding those <= prev_length.
         * At this point we have always match_length < MIN_MATCH
         */


        if (hash_head !== 0
        /*NIL*/
        && s.strstart - hash_head <= s.w_size - MIN_LOOKAHEAD) {
          /* To simplify the code, we prevent matches with the string
           * of window index 0 (in particular we have to avoid a match
           * of the string with itself at the start of the input file).
           */
          s.match_length = longest_match(s, hash_head);
          /* longest_match() sets match_start */
        }

        if (s.match_length >= MIN_MATCH) {
          // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only

          /*** _tr_tally_dist(s, s.strstart - s.match_start,
                         s.match_length - MIN_MATCH, bflush); ***/
          bflush = _tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH);
          s.lookahead -= s.match_length;
          /* Insert new strings in the hash table only if the match length
           * is not too large. This saves time but degrades compression.
           */

          if (s.match_length <= s.max_lazy_match
          /*max_insert_length*/
          && s.lookahead >= MIN_MATCH) {
            s.match_length--;
            /* string at strstart already in table */

            do {
              s.strstart++;
              /*** INSERT_STRING(s, s.strstart, hash_head); ***/

              s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);
              hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
              s.head[s.ins_h] = s.strstart;
              /***/

              /* strstart never exceeds WSIZE-MAX_MATCH, so there are
               * always MIN_MATCH bytes ahead.
               */
            } while (--s.match_length !== 0);

            s.strstart++;
          } else {
            s.strstart += s.match_length;
            s.match_length = 0;
            s.ins_h = s.window[s.strstart];
            /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */

            s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + 1]); //#if MIN_MATCH != 3
            //                Call UPDATE_HASH() MIN_MATCH-3 more times
            //#endif

            /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
             * matter since it will be recomputed at next deflate call.
             */
          }
        } else {
          /* No match, output a literal byte */
          //Tracevv((stderr,"%c", s.window[s.strstart]));

          /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
          bflush = _tr_tally(s, 0, s.window[s.strstart]);
          s.lookahead--;
          s.strstart++;
        }

        if (bflush) {
          /*** FLUSH_BLOCK(s, 0); ***/
          flush_block_only(s, false);

          if (s.strm.avail_out === 0) {
            return BS_NEED_MORE;
          }
          /***/

        }
      }

      s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1;

      if (flush === Z_FINISH$3) {
        /*** FLUSH_BLOCK(s, 1); ***/
        flush_block_only(s, true);

        if (s.strm.avail_out === 0) {
          return BS_FINISH_STARTED;
        }
        /***/


        return BS_FINISH_DONE;
      }

      if (s.last_lit) {
        /*** FLUSH_BLOCK(s, 0); ***/
        flush_block_only(s, false);

        if (s.strm.avail_out === 0) {
          return BS_NEED_MORE;
        }
        /***/

      }

      return BS_BLOCK_DONE;
    };
    /* ===========================================================================
     * Same as above, but achieves better compression. We use a lazy
     * evaluation for matches: a match is finally adopted only if there is
     * no better match at the next window position.
     */


    const deflate_slow = (s, flush) => {
      let hash_head;
      /* head of hash chain */

      let bflush;
      /* set if current block must be flushed */

      let max_insert;
      /* Process the input block. */

      for (;;) {
        /* Make sure that we always have enough lookahead, except
         * at the end of the input file. We need MAX_MATCH bytes
         * for the next match, plus MIN_MATCH bytes to insert the
         * string following the next match.
         */
        if (s.lookahead < MIN_LOOKAHEAD) {
          fill_window(s);

          if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) {
            return BS_NEED_MORE;
          }

          if (s.lookahead === 0) {
            break;
          }
          /* flush the current block */

        }
        /* Insert the string window[strstart .. strstart+2] in the
         * dictionary, and set hash_head to the head of the hash chain:
         */


        hash_head = 0
        /*NIL*/
        ;

        if (s.lookahead >= MIN_MATCH) {
          /*** INSERT_STRING(s, s.strstart, hash_head); ***/
          s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);
          hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
          s.head[s.ins_h] = s.strstart;
          /***/
        }
        /* Find the longest match, discarding those <= prev_length.
         */


        s.prev_length = s.match_length;
        s.prev_match = s.match_start;
        s.match_length = MIN_MATCH - 1;

        if (hash_head !== 0
        /*NIL*/
        && s.prev_length < s.max_lazy_match && s.strstart - hash_head <= s.w_size - MIN_LOOKAHEAD
        /*MAX_DIST(s)*/
        ) {
          /* To simplify the code, we prevent matches with the string
           * of window index 0 (in particular we have to avoid a match
           * of the string with itself at the start of the input file).
           */
          s.match_length = longest_match(s, hash_head);
          /* longest_match() sets match_start */

          if (s.match_length <= 5 && (s.strategy === Z_FILTERED || s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096
          /*TOO_FAR*/
          )) {
            /* If prev_match is also MIN_MATCH, match_start is garbage
             * but we will ignore the current match anyway.
             */
            s.match_length = MIN_MATCH - 1;
          }
        }
        /* If there was a match at the previous step and the current
         * match is not better, output the previous match:
         */


        if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) {
          max_insert = s.strstart + s.lookahead - MIN_MATCH;
          /* Do not insert strings in hash table beyond this. */
          //check_match(s, s.strstart-1, s.prev_match, s.prev_length);

          /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match,
                         s.prev_length - MIN_MATCH, bflush);***/

          bflush = _tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH);
          /* Insert in hash table all strings up to the end of the match.
           * strstart-1 and strstart are already inserted. If there is not
           * enough lookahead, the last two strings are not inserted in
           * the hash table.
           */

          s.lookahead -= s.prev_length - 1;
          s.prev_length -= 2;

          do {
            if (++s.strstart <= max_insert) {
              /*** INSERT_STRING(s, s.strstart, hash_head); ***/
              s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);
              hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
              s.head[s.ins_h] = s.strstart;
              /***/
            }
          } while (--s.prev_length !== 0);

          s.match_available = 0;
          s.match_length = MIN_MATCH - 1;
          s.strstart++;

          if (bflush) {
            /*** FLUSH_BLOCK(s, 0); ***/
            flush_block_only(s, false);

            if (s.strm.avail_out === 0) {
              return BS_NEED_MORE;
            }
            /***/

          }
        } else if (s.match_available) {
          /* If there was no match at the previous position, output a
           * single literal. If there was a match but the current match
           * is longer, truncate the previous match to a single literal.
           */
          //Tracevv((stderr,"%c", s->window[s->strstart-1]));

          /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
          bflush = _tr_tally(s, 0, s.window[s.strstart - 1]);

          if (bflush) {
            /*** FLUSH_BLOCK_ONLY(s, 0) ***/
            flush_block_only(s, false);
            /***/
          }

          s.strstart++;
          s.lookahead--;

          if (s.strm.avail_out === 0) {
            return BS_NEED_MORE;
          }
        } else {
          /* There is no previous match to compare with, wait for
           * the next step to decide.
           */
          s.match_available = 1;
          s.strstart++;
          s.lookahead--;
        }
      } //Assert (flush != Z_NO_FLUSH, "no flush?");


      if (s.match_available) {
        //Tracevv((stderr,"%c", s->window[s->strstart-1]));

        /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
        bflush = _tr_tally(s, 0, s.window[s.strstart - 1]);
        s.match_available = 0;
      }

      s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1;

      if (flush === Z_FINISH$3) {
        /*** FLUSH_BLOCK(s, 1); ***/
        flush_block_only(s, true);

        if (s.strm.avail_out === 0) {
          return BS_FINISH_STARTED;
        }
        /***/


        return BS_FINISH_DONE;
      }

      if (s.last_lit) {
        /*** FLUSH_BLOCK(s, 0); ***/
        flush_block_only(s, false);

        if (s.strm.avail_out === 0) {
          return BS_NEED_MORE;
        }
        /***/

      }

      return BS_BLOCK_DONE;
    };
    /* ===========================================================================
     * For Z_RLE, simply look for runs of bytes, generate matches only of distance
     * one.  Do not maintain a hash table.  (It will be regenerated if this run of
     * deflate switches away from Z_RLE.)
     */


    const deflate_rle = (s, flush) => {
      let bflush;
      /* set if current block must be flushed */

      let prev;
      /* byte at distance one to match */

      let scan, strend;
      /* scan goes up to strend for length of run */

      const _win = s.window;

      for (;;) {
        /* Make sure that we always have enough lookahead, except
         * at the end of the input file. We need MAX_MATCH bytes
         * for the longest run, plus one for the unrolled loop.
         */
        if (s.lookahead <= MAX_MATCH) {
          fill_window(s);

          if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH$2) {
            return BS_NEED_MORE;
          }

          if (s.lookahead === 0) {
            break;
          }
          /* flush the current block */

        }
        /* See how many times the previous byte repeats */


        s.match_length = 0;

        if (s.lookahead >= MIN_MATCH && s.strstart > 0) {
          scan = s.strstart - 1;
          prev = _win[scan];

          if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) {
            strend = s.strstart + MAX_MATCH;

            do {
              /*jshint noempty:false*/
            } while (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && scan < strend);

            s.match_length = MAX_MATCH - (strend - scan);

            if (s.match_length > s.lookahead) {
              s.match_length = s.lookahead;
            }
          } //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");

        }
        /* Emit match if have run of MIN_MATCH or longer, else emit literal */


        if (s.match_length >= MIN_MATCH) {
          //check_match(s, s.strstart, s.strstart - 1, s.match_length);

          /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/
          bflush = _tr_tally(s, 1, s.match_length - MIN_MATCH);
          s.lookahead -= s.match_length;
          s.strstart += s.match_length;
          s.match_length = 0;
        } else {
          /* No match, output a literal byte */
          //Tracevv((stderr,"%c", s->window[s->strstart]));

          /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
          bflush = _tr_tally(s, 0, s.window[s.strstart]);
          s.lookahead--;
          s.strstart++;
        }

        if (bflush) {
          /*** FLUSH_BLOCK(s, 0); ***/
          flush_block_only(s, false);

          if (s.strm.avail_out === 0) {
            return BS_NEED_MORE;
          }
          /***/

        }
      }

      s.insert = 0;

      if (flush === Z_FINISH$3) {
        /*** FLUSH_BLOCK(s, 1); ***/
        flush_block_only(s, true);

        if (s.strm.avail_out === 0) {
          return BS_FINISH_STARTED;
        }
        /***/


        return BS_FINISH_DONE;
      }

      if (s.last_lit) {
        /*** FLUSH_BLOCK(s, 0); ***/
        flush_block_only(s, false);

        if (s.strm.avail_out === 0) {
          return BS_NEED_MORE;
        }
        /***/

      }

      return BS_BLOCK_DONE;
    };
    /* ===========================================================================
     * For Z_HUFFMAN_ONLY, do not look for matches.  Do not maintain a hash table.
     * (It will be regenerated if this run of deflate switches away from Huffman.)
     */


    const deflate_huff = (s, flush) => {
      let bflush;
      /* set if current block must be flushed */

      for (;;) {
        /* Make sure that we have a literal to write. */
        if (s.lookahead === 0) {
          fill_window(s);

          if (s.lookahead === 0) {
            if (flush === Z_NO_FLUSH$2) {
              return BS_NEED_MORE;
            }

            break;
            /* flush the current block */
          }
        }
        /* Output a literal byte */


        s.match_length = 0; //Tracevv((stderr,"%c", s->window[s->strstart]));

        /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/

        bflush = _tr_tally(s, 0, s.window[s.strstart]);
        s.lookahead--;
        s.strstart++;

        if (bflush) {
          /*** FLUSH_BLOCK(s, 0); ***/
          flush_block_only(s, false);

          if (s.strm.avail_out === 0) {
            return BS_NEED_MORE;
          }
          /***/

        }
      }

      s.insert = 0;

      if (flush === Z_FINISH$3) {
        /*** FLUSH_BLOCK(s, 1); ***/
        flush_block_only(s, true);

        if (s.strm.avail_out === 0) {
          return BS_FINISH_STARTED;
        }
        /***/


        return BS_FINISH_DONE;
      }

      if (s.last_lit) {
        /*** FLUSH_BLOCK(s, 0); ***/
        flush_block_only(s, false);

        if (s.strm.avail_out === 0) {
          return BS_NEED_MORE;
        }
        /***/

      }

      return BS_BLOCK_DONE;
    };
    /* Values for max_lazy_match, good_match and max_chain_length, depending on
     * the desired pack level (0..9). The values given below have been tuned to
     * exclude worst case performance for pathological files. Better values may be
     * found for specific files.
     */


    function Config(good_length, max_lazy, nice_length, max_chain, func) {
      this.good_length = good_length;
      this.max_lazy = max_lazy;
      this.nice_length = nice_length;
      this.max_chain = max_chain;
      this.func = func;
    }

    const configuration_table = [
    /*      good lazy nice chain */
    new Config(0, 0, 0, 0, deflate_stored),
    /* 0 store only */
    new Config(4, 4, 8, 4, deflate_fast),
    /* 1 max speed, no lazy matches */
    new Config(4, 5, 16, 8, deflate_fast),
    /* 2 */
    new Config(4, 6, 32, 32, deflate_fast),
    /* 3 */
    new Config(4, 4, 16, 16, deflate_slow),
    /* 4 lazy matches */
    new Config(8, 16, 32, 32, deflate_slow),
    /* 5 */
    new Config(8, 16, 128, 128, deflate_slow),
    /* 6 */
    new Config(8, 32, 128, 256, deflate_slow),
    /* 7 */
    new Config(32, 128, 258, 1024, deflate_slow),
    /* 8 */
    new Config(32, 258, 258, 4096, deflate_slow)
    /* 9 max compression */
    ];
    /* ===========================================================================
     * Initialize the "longest match" routines for a new zlib stream
     */

    const lm_init = s => {
      s.window_size = 2 * s.w_size;
      /*** CLEAR_HASH(s); ***/

      zero(s.head); // Fill with NIL (= 0);

      /* Set the default configuration parameters:
       */

      s.max_lazy_match = configuration_table[s.level].max_lazy;
      s.good_match = configuration_table[s.level].good_length;
      s.nice_match = configuration_table[s.level].nice_length;
      s.max_chain_length = configuration_table[s.level].max_chain;
      s.strstart = 0;
      s.block_start = 0;
      s.lookahead = 0;
      s.insert = 0;
      s.match_length = s.prev_length = MIN_MATCH - 1;
      s.match_available = 0;
      s.ins_h = 0;
    };

    function DeflateState() {
      this.strm = null;
      /* pointer back to this zlib stream */

      this.status = 0;
      /* as the name implies */

      this.pending_buf = null;
      /* output still pending */

      this.pending_buf_size = 0;
      /* size of pending_buf */

      this.pending_out = 0;
      /* next pending byte to output to the stream */

      this.pending = 0;
      /* nb of bytes in the pending buffer */

      this.wrap = 0;
      /* bit 0 true for zlib, bit 1 true for gzip */

      this.gzhead = null;
      /* gzip header information to write */

      this.gzindex = 0;
      /* where in extra, name, or comment */

      this.method = Z_DEFLATED$2;
      /* can only be DEFLATED */

      this.last_flush = -1;
      /* value of flush param for previous deflate call */

      this.w_size = 0;
      /* LZ77 window size (32K by default) */

      this.w_bits = 0;
      /* log2(w_size)  (8..16) */

      this.w_mask = 0;
      /* w_size - 1 */

      this.window = null;
      /* Sliding window. Input bytes are read into the second half of the window,
       * and move to the first half later to keep a dictionary of at least wSize
       * bytes. With this organization, matches are limited to a distance of
       * wSize-MAX_MATCH bytes, but this ensures that IO is always
       * performed with a length multiple of the block size.
       */

      this.window_size = 0;
      /* Actual size of window: 2*wSize, except when the user input buffer
       * is directly used as sliding window.
       */

      this.prev = null;
      /* Link to older string with same hash index. To limit the size of this
       * array to 64K, this link is maintained only for the last 32K strings.
       * An index in this array is thus a window index modulo 32K.
       */

      this.head = null;
      /* Heads of the hash chains or NIL. */

      this.ins_h = 0;
      /* hash index of string to be inserted */

      this.hash_size = 0;
      /* number of elements in hash table */

      this.hash_bits = 0;
      /* log2(hash_size) */

      this.hash_mask = 0;
      /* hash_size-1 */

      this.hash_shift = 0;
      /* Number of bits by which ins_h must be shifted at each input
       * step. It must be such that after MIN_MATCH steps, the oldest
       * byte no longer takes part in the hash key, that is:
       *   hash_shift * MIN_MATCH >= hash_bits
       */

      this.block_start = 0;
      /* Window position at the beginning of the current output block. Gets
       * negative when the window is moved backwards.
       */

      this.match_length = 0;
      /* length of best match */

      this.prev_match = 0;
      /* previous match */

      this.match_available = 0;
      /* set if previous match exists */

      this.strstart = 0;
      /* start of string to insert */

      this.match_start = 0;
      /* start of matching string */

      this.lookahead = 0;
      /* number of valid bytes ahead in window */

      this.prev_length = 0;
      /* Length of the best match at previous step. Matches not greater than this
       * are discarded. This is used in the lazy match evaluation.
       */

      this.max_chain_length = 0;
      /* To speed up deflation, hash chains are never searched beyond this
       * length.  A higher limit improves compression ratio but degrades the
       * speed.
       */

      this.max_lazy_match = 0;
      /* Attempt to find a better match only when the current match is strictly
       * smaller than this value. This mechanism is used only for compression
       * levels >= 4.
       */
      // That's alias to max_lazy_match, don't use directly
      //this.max_insert_length = 0;

      /* Insert new strings in the hash table only if the match length is not
       * greater than this length. This saves time but degrades compression.
       * max_insert_length is used only for compression levels <= 3.
       */

      this.level = 0;
      /* compression level (1..9) */

      this.strategy = 0;
      /* favor or force Huffman coding*/

      this.good_match = 0;
      /* Use a faster search when the previous match is longer than this */

      this.nice_match = 0;
      /* Stop searching when current match exceeds this */

      /* used by trees.c: */

      /* Didn't use ct_data typedef below to suppress compiler warning */
      // struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */
      // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
      // struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */
      // Use flat array of DOUBLE size, with interleaved fata,
      // because JS does not support effective

      this.dyn_ltree = new Uint16Array(HEAP_SIZE * 2);
      this.dyn_dtree = new Uint16Array((2 * D_CODES + 1) * 2);
      this.bl_tree = new Uint16Array((2 * BL_CODES + 1) * 2);
      zero(this.dyn_ltree);
      zero(this.dyn_dtree);
      zero(this.bl_tree);
      this.l_desc = null;
      /* desc. for literal tree */

      this.d_desc = null;
      /* desc. for distance tree */

      this.bl_desc = null;
      /* desc. for bit length tree */
      //ush bl_count[MAX_BITS+1];

      this.bl_count = new Uint16Array(MAX_BITS + 1);
      /* number of codes at each bit length for an optimal tree */
      //int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */

      this.heap = new Uint16Array(2 * L_CODES + 1);
      /* heap used to build the Huffman trees */

      zero(this.heap);
      this.heap_len = 0;
      /* number of elements in the heap */

      this.heap_max = 0;
      /* element of largest frequency */

      /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
       * The same heap array is used to build all trees.
       */

      this.depth = new Uint16Array(2 * L_CODES + 1); //uch depth[2*L_CODES+1];

      zero(this.depth);
      /* Depth of each subtree used as tie breaker for trees of equal frequency
       */

      this.l_buf = 0;
      /* buffer index for literals or lengths */

      this.lit_bufsize = 0;
      /* Size of match buffer for literals/lengths.  There are 4 reasons for
       * limiting lit_bufsize to 64K:
       *   - frequencies can be kept in 16 bit counters
       *   - if compression is not successful for the first block, all input
       *     data is still in the window so we can still emit a stored block even
       *     when input comes from standard input.  (This can also be done for
       *     all blocks if lit_bufsize is not greater than 32K.)
       *   - if compression is not successful for a file smaller than 64K, we can
       *     even emit a stored file instead of a stored block (saving 5 bytes).
       *     This is applicable only for zip (not gzip or zlib).
       *   - creating new Huffman trees less frequently may not provide fast
       *     adaptation to changes in the input data statistics. (Take for
       *     example a binary file with poorly compressible code followed by
       *     a highly compressible string table.) Smaller buffer sizes give
       *     fast adaptation but have of course the overhead of transmitting
       *     trees more frequently.
       *   - I can't count above 4
       */

      this.last_lit = 0;
      /* running index in l_buf */

      this.d_buf = 0;
      /* Buffer index for distances. To simplify the code, d_buf and l_buf have
       * the same number of elements. To use different lengths, an extra flag
       * array would be necessary.
       */

      this.opt_len = 0;
      /* bit length of current block with optimal trees */

      this.static_len = 0;
      /* bit length of current block with static trees */

      this.matches = 0;
      /* number of string matches in current block */

      this.insert = 0;
      /* bytes at end of window left to insert */

      this.bi_buf = 0;
      /* Output buffer. bits are inserted starting at the bottom (least
       * significant bits).
       */

      this.bi_valid = 0;
      /* Number of valid bits in bi_buf.  All bits above the last valid bit
       * are always zero.
       */
      // Used for window memory init. We safely ignore it for JS. That makes
      // sense only for pointers and memory check tools.
      //this.high_water = 0;

      /* High water mark offset in window for initialized bytes -- bytes above
       * this are set to zero in order to avoid memory check warnings when
       * longest match routines access bytes past the input.  This is then
       * updated to the new high water mark.
       */
    }

    const deflateResetKeep = strm => {
      if (!strm || !strm.state) {
        return err(strm, Z_STREAM_ERROR$2);
      }

      strm.total_in = strm.total_out = 0;
      strm.data_type = Z_UNKNOWN;
      const s = strm.state;
      s.pending = 0;
      s.pending_out = 0;

      if (s.wrap < 0) {
        s.wrap = -s.wrap;
        /* was made negative by deflate(..., Z_FINISH); */
      }

      s.status = s.wrap ? INIT_STATE : BUSY_STATE;
      strm.adler = s.wrap === 2 ? 0 // crc32(0, Z_NULL, 0)
      : 1; // adler32(0, Z_NULL, 0)

      s.last_flush = Z_NO_FLUSH$2;

      _tr_init(s);

      return Z_OK$3;
    };

    const deflateReset = strm => {
      const ret = deflateResetKeep(strm);

      if (ret === Z_OK$3) {
        lm_init(strm.state);
      }

      return ret;
    };

    const deflateSetHeader = (strm, head) => {
      if (!strm || !strm.state) {
        return Z_STREAM_ERROR$2;
      }

      if (strm.state.wrap !== 2) {
        return Z_STREAM_ERROR$2;
      }

      strm.state.gzhead = head;
      return Z_OK$3;
    };

    const deflateInit2 = (strm, level, method, windowBits, memLevel, strategy) => {
      if (!strm) {
        // === Z_NULL
        return Z_STREAM_ERROR$2;
      }

      let wrap = 1;

      if (level === Z_DEFAULT_COMPRESSION$1) {
        level = 6;
      }

      if (windowBits < 0) {
        /* suppress zlib wrapper */
        wrap = 0;
        windowBits = -windowBits;
      } else if (windowBits > 15) {
        wrap = 2;
        /* write gzip wrapper instead */

        windowBits -= 16;
      }

      if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED$2 || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) {
        return err(strm, Z_STREAM_ERROR$2);
      }

      if (windowBits === 8) {
        windowBits = 9;
      }
      /* until 256-byte window bug fixed */


      const s = new DeflateState();
      strm.state = s;
      s.strm = strm;
      s.wrap = wrap;
      s.gzhead = null;
      s.w_bits = windowBits;
      s.w_size = 1 << s.w_bits;
      s.w_mask = s.w_size - 1;
      s.hash_bits = memLevel + 7;
      s.hash_size = 1 << s.hash_bits;
      s.hash_mask = s.hash_size - 1;
      s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH);
      s.window = new Uint8Array(s.w_size * 2);
      s.head = new Uint16Array(s.hash_size);
      s.prev = new Uint16Array(s.w_size); // Don't need mem init magic for JS.
      //s.high_water = 0;  /* nothing written to s->window yet */

      s.lit_bufsize = 1 << memLevel + 6;
      /* 16K elements by default */

      s.pending_buf_size = s.lit_bufsize * 4; //overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
      //s->pending_buf = (uchf *) overlay;

      s.pending_buf = new Uint8Array(s.pending_buf_size); // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`)
      //s->d_buf = overlay + s->lit_bufsize/sizeof(ush);

      s.d_buf = 1 * s.lit_bufsize; //s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;

      s.l_buf = (1 + 2) * s.lit_bufsize;
      s.level = level;
      s.strategy = strategy;
      s.method = method;
      return deflateReset(strm);
    };

    const deflateInit = (strm, level) => {
      return deflateInit2(strm, level, Z_DEFLATED$2, MAX_WBITS$1, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY$1);
    };

    const deflate$2 = (strm, flush) => {
      let beg, val; // for gzip header write only

      if (!strm || !strm.state || flush > Z_BLOCK$1 || flush < 0) {
        return strm ? err(strm, Z_STREAM_ERROR$2) : Z_STREAM_ERROR$2;
      }

      const s = strm.state;

      if (!strm.output || !strm.input && strm.avail_in !== 0 || s.status === FINISH_STATE && flush !== Z_FINISH$3) {
        return err(strm, strm.avail_out === 0 ? Z_BUF_ERROR$1 : Z_STREAM_ERROR$2);
      }

      s.strm = strm;
      /* just in case */

      const old_flush = s.last_flush;
      s.last_flush = flush;
      /* Write the header */

      if (s.status === INIT_STATE) {
        if (s.wrap === 2) {
          // GZIP header
          strm.adler = 0; //crc32(0L, Z_NULL, 0);

          put_byte(s, 31);
          put_byte(s, 139);
          put_byte(s, 8);

          if (!s.gzhead) {
            // s->gzhead == Z_NULL
            put_byte(s, 0);
            put_byte(s, 0);
            put_byte(s, 0);
            put_byte(s, 0);
            put_byte(s, 0);
            put_byte(s, s.level === 9 ? 2 : s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0);
            put_byte(s, OS_CODE);
            s.status = BUSY_STATE;
          } else {
            put_byte(s, (s.gzhead.text ? 1 : 0) + (s.gzhead.hcrc ? 2 : 0) + (!s.gzhead.extra ? 0 : 4) + (!s.gzhead.name ? 0 : 8) + (!s.gzhead.comment ? 0 : 16));
            put_byte(s, s.gzhead.time & 0xff);
            put_byte(s, s.gzhead.time >> 8 & 0xff);
            put_byte(s, s.gzhead.time >> 16 & 0xff);
            put_byte(s, s.gzhead.time >> 24 & 0xff);
            put_byte(s, s.level === 9 ? 2 : s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0);
            put_byte(s, s.gzhead.os & 0xff);

            if (s.gzhead.extra && s.gzhead.extra.length) {
              put_byte(s, s.gzhead.extra.length & 0xff);
              put_byte(s, s.gzhead.extra.length >> 8 & 0xff);
            }

            if (s.gzhead.hcrc) {
              strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending, 0);
            }

            s.gzindex = 0;
            s.status = EXTRA_STATE;
          }
        } else // DEFLATE header
          {
            let header = Z_DEFLATED$2 + (s.w_bits - 8 << 4) << 8;
            let level_flags = -1;

            if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) {
              level_flags = 0;
            } else if (s.level < 6) {
              level_flags = 1;
            } else if (s.level === 6) {
              level_flags = 2;
            } else {
              level_flags = 3;
            }

            header |= level_flags << 6;

            if (s.strstart !== 0) {
              header |= PRESET_DICT;
            }

            header += 31 - header % 31;
            s.status = BUSY_STATE;
            putShortMSB(s, header);
            /* Save the adler32 of the preset dictionary: */

            if (s.strstart !== 0) {
              putShortMSB(s, strm.adler >>> 16);
              putShortMSB(s, strm.adler & 0xffff);
            }

            strm.adler = 1; // adler32(0L, Z_NULL, 0);
          }
      } //#ifdef GZIP


      if (s.status === EXTRA_STATE) {
        if (s.gzhead.extra
        /* != Z_NULL*/
        ) {
          beg = s.pending;
          /* start of bytes to update crc */

          while (s.gzindex < (s.gzhead.extra.length & 0xffff)) {
            if (s.pending === s.pending_buf_size) {
              if (s.gzhead.hcrc && s.pending > beg) {
                strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
              }

              flush_pending(strm);
              beg = s.pending;

              if (s.pending === s.pending_buf_size) {
                break;
              }
            }

            put_byte(s, s.gzhead.extra[s.gzindex] & 0xff);
            s.gzindex++;
          }

          if (s.gzhead.hcrc && s.pending > beg) {
            strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
          }

          if (s.gzindex === s.gzhead.extra.length) {
            s.gzindex = 0;
            s.status = NAME_STATE;
          }
        } else {
          s.status = NAME_STATE;
        }
      }

      if (s.status === NAME_STATE) {
        if (s.gzhead.name
        /* != Z_NULL*/
        ) {
          beg = s.pending;
          /* start of bytes to update crc */
          //int val;

          do {
            if (s.pending === s.pending_buf_size) {
              if (s.gzhead.hcrc && s.pending > beg) {
                strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
              }

              flush_pending(strm);
              beg = s.pending;

              if (s.pending === s.pending_buf_size) {
                val = 1;
                break;
              }
            } // JS specific: little magic to add zero terminator to end of string


            if (s.gzindex < s.gzhead.name.length) {
              val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff;
            } else {
              val = 0;
            }

            put_byte(s, val);
          } while (val !== 0);

          if (s.gzhead.hcrc && s.pending > beg) {
            strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
          }

          if (val === 0) {
            s.gzindex = 0;
            s.status = COMMENT_STATE;
          }
        } else {
          s.status = COMMENT_STATE;
        }
      }

      if (s.status === COMMENT_STATE) {
        if (s.gzhead.comment
        /* != Z_NULL*/
        ) {
          beg = s.pending;
          /* start of bytes to update crc */
          //int val;

          do {
            if (s.pending === s.pending_buf_size) {
              if (s.gzhead.hcrc && s.pending > beg) {
                strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
              }

              flush_pending(strm);
              beg = s.pending;

              if (s.pending === s.pending_buf_size) {
                val = 1;
                break;
              }
            } // JS specific: little magic to add zero terminator to end of string


            if (s.gzindex < s.gzhead.comment.length) {
              val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff;
            } else {
              val = 0;
            }

            put_byte(s, val);
          } while (val !== 0);

          if (s.gzhead.hcrc && s.pending > beg) {
            strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
          }

          if (val === 0) {
            s.status = HCRC_STATE;
          }
        } else {
          s.status = HCRC_STATE;
        }
      }

      if (s.status === HCRC_STATE) {
        if (s.gzhead.hcrc) {
          if (s.pending + 2 > s.pending_buf_size) {
            flush_pending(strm);
          }

          if (s.pending + 2 <= s.pending_buf_size) {
            put_byte(s, strm.adler & 0xff);
            put_byte(s, strm.adler >> 8 & 0xff);
            strm.adler = 0; //crc32(0L, Z_NULL, 0);

            s.status = BUSY_STATE;
          }
        } else {
          s.status = BUSY_STATE;
        }
      } //#endif

      /* Flush as much pending output as possible */


      if (s.pending !== 0) {
        flush_pending(strm);

        if (strm.avail_out === 0) {
          /* Since avail_out is 0, deflate will be called again with
           * more output space, but possibly with both pending and
           * avail_in equal to zero. There won't be anything to do,
           * but this is not an error situation so make sure we
           * return OK instead of BUF_ERROR at next call of deflate:
           */
          s.last_flush = -1;
          return Z_OK$3;
        }
        /* Make sure there is something to do and avoid duplicate consecutive
         * flushes. For repeated and useless calls with Z_FINISH, we keep
         * returning Z_STREAM_END instead of Z_BUF_ERROR.
         */

      } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) && flush !== Z_FINISH$3) {
        return err(strm, Z_BUF_ERROR$1);
      }
      /* User must not provide more input after the first FINISH: */


      if (s.status === FINISH_STATE && strm.avail_in !== 0) {
        return err(strm, Z_BUF_ERROR$1);
      }
      /* Start a new block or continue the current one.
       */


      if (strm.avail_in !== 0 || s.lookahead !== 0 || flush !== Z_NO_FLUSH$2 && s.status !== FINISH_STATE) {
        let bstate = s.strategy === Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : s.strategy === Z_RLE ? deflate_rle(s, flush) : configuration_table[s.level].func(s, flush);

        if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) {
          s.status = FINISH_STATE;
        }

        if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) {
          if (strm.avail_out === 0) {
            s.last_flush = -1;
            /* avoid BUF_ERROR next call, see above */
          }

          return Z_OK$3;
          /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
           * of deflate should use the same flush parameter to make sure
           * that the flush is complete. So we don't have to output an
           * empty block here, this will be done at next call. This also
           * ensures that for a very small output buffer, we emit at most
           * one empty block.
           */
        }

        if (bstate === BS_BLOCK_DONE) {
          if (flush === Z_PARTIAL_FLUSH) {
            _tr_align(s);
          } else if (flush !== Z_BLOCK$1) {
            /* FULL_FLUSH or SYNC_FLUSH */
            _tr_stored_block(s, 0, 0, false);
            /* For a full flush, this empty block will be recognized
             * as a special marker by inflate_sync().
             */


            if (flush === Z_FULL_FLUSH$1) {
              /*** CLEAR_HASH(s); ***/

              /* forget history */
              zero(s.head); // Fill with NIL (= 0);

              if (s.lookahead === 0) {
                s.strstart = 0;
                s.block_start = 0;
                s.insert = 0;
              }
            }
          }

          flush_pending(strm);

          if (strm.avail_out === 0) {
            s.last_flush = -1;
            /* avoid BUF_ERROR at next call, see above */

            return Z_OK$3;
          }
        }
      } //Assert(strm->avail_out > 0, "bug2");
      //if (strm.avail_out <= 0) { throw new Error("bug2");}


      if (flush !== Z_FINISH$3) {
        return Z_OK$3;
      }

      if (s.wrap <= 0) {
        return Z_STREAM_END$3;
      }
      /* Write the trailer */


      if (s.wrap === 2) {
        put_byte(s, strm.adler & 0xff);
        put_byte(s, strm.adler >> 8 & 0xff);
        put_byte(s, strm.adler >> 16 & 0xff);
        put_byte(s, strm.adler >> 24 & 0xff);
        put_byte(s, strm.total_in & 0xff);
        put_byte(s, strm.total_in >> 8 & 0xff);
        put_byte(s, strm.total_in >> 16 & 0xff);
        put_byte(s, strm.total_in >> 24 & 0xff);
      } else {
        putShortMSB(s, strm.adler >>> 16);
        putShortMSB(s, strm.adler & 0xffff);
      }

      flush_pending(strm);
      /* If avail_out is zero, the application will call deflate again
       * to flush the rest.
       */

      if (s.wrap > 0) {
        s.wrap = -s.wrap;
      }
      /* write the trailer only once! */


      return s.pending !== 0 ? Z_OK$3 : Z_STREAM_END$3;
    };

    const deflateEnd = strm => {
      if (!strm
      /*== Z_NULL*/
      || !strm.state
      /*== Z_NULL*/
      ) {
        return Z_STREAM_ERROR$2;
      }

      const status = strm.state.status;

      if (status !== INIT_STATE && status !== EXTRA_STATE && status !== NAME_STATE && status !== COMMENT_STATE && status !== HCRC_STATE && status !== BUSY_STATE && status !== FINISH_STATE) {
        return err(strm, Z_STREAM_ERROR$2);
      }

      strm.state = null;
      return status === BUSY_STATE ? err(strm, Z_DATA_ERROR$2) : Z_OK$3;
    };
    /* =========================================================================
     * Initializes the compression dictionary from the given byte
     * sequence without producing any compressed output.
     */


    const deflateSetDictionary = (strm, dictionary) => {
      let dictLength = dictionary.length;

      if (!strm
      /*== Z_NULL*/
      || !strm.state
      /*== Z_NULL*/
      ) {
        return Z_STREAM_ERROR$2;
      }

      const s = strm.state;
      const wrap = s.wrap;

      if (wrap === 2 || wrap === 1 && s.status !== INIT_STATE || s.lookahead) {
        return Z_STREAM_ERROR$2;
      }
      /* when using zlib wrappers, compute Adler-32 for provided dictionary */


      if (wrap === 1) {
        /* adler32(strm->adler, dictionary, dictLength); */
        strm.adler = adler32_1(strm.adler, dictionary, dictLength, 0);
      }

      s.wrap = 0;
      /* avoid computing Adler-32 in read_buf */

      /* if dictionary would fill window, just replace the history */

      if (dictLength >= s.w_size) {
        if (wrap === 0) {
          /* already empty otherwise */

          /*** CLEAR_HASH(s); ***/
          zero(s.head); // Fill with NIL (= 0);

          s.strstart = 0;
          s.block_start = 0;
          s.insert = 0;
        }
        /* use the tail */
        // dictionary = dictionary.slice(dictLength - s.w_size);


        let tmpDict = new Uint8Array(s.w_size);
        tmpDict.set(dictionary.subarray(dictLength - s.w_size, dictLength), 0);
        dictionary = tmpDict;
        dictLength = s.w_size;
      }
      /* insert dictionary into window and hash */


      const avail = strm.avail_in;
      const next = strm.next_in;
      const input = strm.input;
      strm.avail_in = dictLength;
      strm.next_in = 0;
      strm.input = dictionary;
      fill_window(s);

      while (s.lookahead >= MIN_MATCH) {
        let str = s.strstart;
        let n = s.lookahead - (MIN_MATCH - 1);

        do {
          /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
          s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]);
          s.prev[str & s.w_mask] = s.head[s.ins_h];
          s.head[s.ins_h] = str;
          str++;
        } while (--n);

        s.strstart = str;
        s.lookahead = MIN_MATCH - 1;
        fill_window(s);
      }

      s.strstart += s.lookahead;
      s.block_start = s.strstart;
      s.insert = s.lookahead;
      s.lookahead = 0;
      s.match_length = s.prev_length = MIN_MATCH - 1;
      s.match_available = 0;
      strm.next_in = next;
      strm.input = input;
      strm.avail_in = avail;
      s.wrap = wrap;
      return Z_OK$3;
    };

    var deflateInit_1 = deflateInit;
    var deflateInit2_1 = deflateInit2;
    var deflateReset_1 = deflateReset;
    var deflateResetKeep_1 = deflateResetKeep;
    var deflateSetHeader_1 = deflateSetHeader;
    var deflate_2$1 = deflate$2;
    var deflateEnd_1 = deflateEnd;
    var deflateSetDictionary_1 = deflateSetDictionary;
    var deflateInfo = 'pako deflate (from Nodeca project)';
    /* Not implemented
    module.exports.deflateBound = deflateBound;
    module.exports.deflateCopy = deflateCopy;
    module.exports.deflateParams = deflateParams;
    module.exports.deflatePending = deflatePending;
    module.exports.deflatePrime = deflatePrime;
    module.exports.deflateTune = deflateTune;
    */

    var deflate_1$2 = {
      deflateInit: deflateInit_1,
      deflateInit2: deflateInit2_1,
      deflateReset: deflateReset_1,
      deflateResetKeep: deflateResetKeep_1,
      deflateSetHeader: deflateSetHeader_1,
      deflate: deflate_2$1,
      deflateEnd: deflateEnd_1,
      deflateSetDictionary: deflateSetDictionary_1,
      deflateInfo: deflateInfo
    };

    const _has = (obj, key) => {
      return Object.prototype.hasOwnProperty.call(obj, key);
    };

    var assign = function (obj
    /*from1, from2, from3, ...*/
    ) {
      const sources = Array.prototype.slice.call(arguments, 1);

      while (sources.length) {
        const source = sources.shift();

        if (!source) {
          continue;
        }

        if (typeof source !== 'object') {
          throw new TypeError(source + 'must be non-object');
        }

        for (const p in source) {
          if (_has(source, p)) {
            obj[p] = source[p];
          }
        }
      }

      return obj;
    }; // Join array of chunks to single array.


    var flattenChunks = chunks => {
      // calculate data length
      let len = 0;

      for (let i = 0, l = chunks.length; i < l; i++) {
        len += chunks[i].length;
      } // join chunks


      const result = new Uint8Array(len);

      for (let i = 0, pos = 0, l = chunks.length; i < l; i++) {
        let chunk = chunks[i];
        result.set(chunk, pos);
        pos += chunk.length;
      }

      return result;
    };

    var common = {
      assign: assign,
      flattenChunks: flattenChunks
    }; // String encode/decode helpers
    // Quick check if we can use fast array to bin string conversion
    //
    // - apply(Array) can fail on Android 2.2
    // - apply(Uint8Array) can fail on iOS 5.1 Safari
    //

    let STR_APPLY_UIA_OK = true;

    try {
      String.fromCharCode.apply(null, new Uint8Array(1));
    } catch (__) {
      STR_APPLY_UIA_OK = false;
    } // Table with utf8 lengths (calculated by first byte of sequence)
    // Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS,
    // because max possible codepoint is 0x10ffff


    const _utf8len = new Uint8Array(256);

    for (let q = 0; q < 256; q++) {
      _utf8len[q] = q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1;
    }

    _utf8len[254] = _utf8len[254] = 1; // Invalid sequence start
    // convert string to array (typed, when possible)

    var string2buf = str => {
      if (typeof TextEncoder === 'function' && TextEncoder.prototype.encode) {
        return new TextEncoder().encode(str);
      }

      let buf,
          c,
          c2,
          m_pos,
          i,
          str_len = str.length,
          buf_len = 0; // count binary size

      for (m_pos = 0; m_pos < str_len; m_pos++) {
        c = str.charCodeAt(m_pos);

        if ((c & 0xfc00) === 0xd800 && m_pos + 1 < str_len) {
          c2 = str.charCodeAt(m_pos + 1);

          if ((c2 & 0xfc00) === 0xdc00) {
            c = 0x10000 + (c - 0xd800 << 10) + (c2 - 0xdc00);
            m_pos++;
          }
        }

        buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;
      } // allocate buffer


      buf = new Uint8Array(buf_len); // convert

      for (i = 0, m_pos = 0; i < buf_len; m_pos++) {
        c = str.charCodeAt(m_pos);

        if ((c & 0xfc00) === 0xd800 && m_pos + 1 < str_len) {
          c2 = str.charCodeAt(m_pos + 1);

          if ((c2 & 0xfc00) === 0xdc00) {
            c = 0x10000 + (c - 0xd800 << 10) + (c2 - 0xdc00);
            m_pos++;
          }
        }

        if (c < 0x80) {
          /* one byte */
          buf[i++] = c;
        } else if (c < 0x800) {
          /* two bytes */
          buf[i++] = 0xC0 | c >>> 6;
          buf[i++] = 0x80 | c & 0x3f;
        } else if (c < 0x10000) {
          /* three bytes */
          buf[i++] = 0xE0 | c >>> 12;
          buf[i++] = 0x80 | c >>> 6 & 0x3f;
          buf[i++] = 0x80 | c & 0x3f;
        } else {
          /* four bytes */
          buf[i++] = 0xf0 | c >>> 18;
          buf[i++] = 0x80 | c >>> 12 & 0x3f;
          buf[i++] = 0x80 | c >>> 6 & 0x3f;
          buf[i++] = 0x80 | c & 0x3f;
        }
      }

      return buf;
    }; // Helper


    const buf2binstring = (buf, len) => {
      // On Chrome, the arguments in a function call that are allowed is `65534`.
      // If the length of the buffer is smaller than that, we can use this optimization,
      // otherwise we will take a slower path.
      if (len < 65534) {
        if (buf.subarray && STR_APPLY_UIA_OK) {
          return String.fromCharCode.apply(null, buf.length === len ? buf : buf.subarray(0, len));
        }
      }

      let result = '';

      for (let i = 0; i < len; i++) {
        result += String.fromCharCode(buf[i]);
      }

      return result;
    }; // convert array to string


    var buf2string = (buf, max) => {
      const len = max || buf.length;

      if (typeof TextDecoder === 'function' && TextDecoder.prototype.decode) {
        return new TextDecoder().decode(buf.subarray(0, max));
      }

      let i, out; // Reserve max possible length (2 words per char)
      // NB: by unknown reasons, Array is significantly faster for
      //     String.fromCharCode.apply than Uint16Array.

      const utf16buf = new Array(len * 2);

      for (out = 0, i = 0; i < len;) {
        let c = buf[i++]; // quick process ascii

        if (c < 0x80) {
          utf16buf[out++] = c;
          continue;
        }

        let c_len = _utf8len[c]; // skip 5 & 6 byte codes

        if (c_len > 4) {
          utf16buf[out++] = 0xfffd;
          i += c_len - 1;
          continue;
        } // apply mask on first byte


        c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07; // join the rest

        while (c_len > 1 && i < len) {
          c = c << 6 | buf[i++] & 0x3f;
          c_len--;
        } // terminated by end of string?


        if (c_len > 1) {
          utf16buf[out++] = 0xfffd;
          continue;
        }

        if (c < 0x10000) {
          utf16buf[out++] = c;
        } else {
          c -= 0x10000;
          utf16buf[out++] = 0xd800 | c >> 10 & 0x3ff;
          utf16buf[out++] = 0xdc00 | c & 0x3ff;
        }
      }

      return buf2binstring(utf16buf, out);
    }; // Calculate max possible position in utf8 buffer,
    // that will not break sequence. If that's not possible
    // - (very small limits) return max size as is.
    //
    // buf[] - utf8 bytes array
    // max   - length limit (mandatory);


    var utf8border = (buf, max) => {
      max = max || buf.length;

      if (max > buf.length) {
        max = buf.length;
      } // go back from last position, until start of sequence found


      let pos = max - 1;

      while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) {
        pos--;
      } // Very small and broken sequence,
      // return max, because we should return something anyway.


      if (pos < 0) {
        return max;
      } // If we came to start of buffer - that means buffer is too small,
      // return max too.


      if (pos === 0) {
        return max;
      }

      return pos + _utf8len[buf[pos]] > max ? pos : max;
    };

    var strings = {
      string2buf: string2buf,
      buf2string: buf2string,
      utf8border: utf8border
    }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    function ZStream() {
      /* next input byte */
      this.input = null; // JS specific, because we have no pointers

      this.next_in = 0;
      /* number of bytes available at input */

      this.avail_in = 0;
      /* total number of input bytes read so far */

      this.total_in = 0;
      /* next output byte should be put there */

      this.output = null; // JS specific, because we have no pointers

      this.next_out = 0;
      /* remaining free space at output */

      this.avail_out = 0;
      /* total number of bytes output so far */

      this.total_out = 0;
      /* last error message, NULL if no error */

      this.msg = ''
      /*Z_NULL*/
      ;
      /* not visible by applications */

      this.state = null;
      /* best guess about the data type: binary or text */

      this.data_type = 2
      /*Z_UNKNOWN*/
      ;
      /* adler32 value of the uncompressed data */

      this.adler = 0;
    }

    var zstream = ZStream;
    const toString$1$1 = Object.prototype.toString;
    /* Public constants ==========================================================*/

    /* ===========================================================================*/

    const {
      Z_NO_FLUSH: Z_NO_FLUSH$1,
      Z_SYNC_FLUSH,
      Z_FULL_FLUSH,
      Z_FINISH: Z_FINISH$2,
      Z_OK: Z_OK$2,
      Z_STREAM_END: Z_STREAM_END$2,
      Z_DEFAULT_COMPRESSION,
      Z_DEFAULT_STRATEGY,
      Z_DEFLATED: Z_DEFLATED$1
    } = constants$2;
    /* ===========================================================================*/

    /**
     * class Deflate
     *
     * Generic JS-style wrapper for zlib calls. If you don't need
     * streaming behaviour - use more simple functions: [[deflate]],
     * [[deflateRaw]] and [[gzip]].
     **/

    /* internal
     * Deflate.chunks -> Array
     *
     * Chunks of output data, if [[Deflate#onData]] not overridden.
     **/

    /**
     * Deflate.result -> Uint8Array
     *
     * Compressed result, generated by default [[Deflate#onData]]
     * and [[Deflate#onEnd]] handlers. Filled after you push last chunk
     * (call [[Deflate#push]] with `Z_FINISH` / `true` param).
     **/

    /**
     * Deflate.err -> Number
     *
     * Error code after deflate finished. 0 (Z_OK) on success.
     * You will not need it in real life, because deflate errors
     * are possible only on wrong options or bad `onData` / `onEnd`
     * custom handlers.
     **/

    /**
     * Deflate.msg -> String
     *
     * Error message, if [[Deflate.err]] != 0
     **/

    /**
     * new Deflate(options)
     * - options (Object): zlib deflate options.
     *
     * Creates new deflator instance with specified params. Throws exception
     * on bad params. Supported options:
     *
     * - `level`
     * - `windowBits`
     * - `memLevel`
     * - `strategy`
     * - `dictionary`
     *
     * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
     * for more information on these.
     *
     * Additional options, for internal needs:
     *
     * - `chunkSize` - size of generated data chunks (16K by default)
     * - `raw` (Boolean) - do raw deflate
     * - `gzip` (Boolean) - create gzip wrapper
     * - `header` (Object) - custom header for gzip
     *   - `text` (Boolean) - true if compressed data believed to be text
     *   - `time` (Number) - modification time, unix timestamp
     *   - `os` (Number) - operation system code
     *   - `extra` (Array) - array of bytes with extra data (max 65536)
     *   - `name` (String) - file name (binary string)
     *   - `comment` (String) - comment (binary string)
     *   - `hcrc` (Boolean) - true if header crc should be added
     *
     * ##### Example:
     *
     * ```javascript
     * const pako = require('pako')
     *   , chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9])
     *   , chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]);
     *
     * const deflate = new pako.Deflate({ level: 3});
     *
     * deflate.push(chunk1, false);
     * deflate.push(chunk2, true);  // true -> last chunk
     *
     * if (deflate.err) { throw new Error(deflate.err); }
     *
     * console.log(deflate.result);
     * ```
     **/

    function Deflate$1(options) {
      this.options = common.assign({
        level: Z_DEFAULT_COMPRESSION,
        method: Z_DEFLATED$1,
        chunkSize: 16384,
        windowBits: 15,
        memLevel: 8,
        strategy: Z_DEFAULT_STRATEGY
      }, options || {});
      let opt = this.options;

      if (opt.raw && opt.windowBits > 0) {
        opt.windowBits = -opt.windowBits;
      } else if (opt.gzip && opt.windowBits > 0 && opt.windowBits < 16) {
        opt.windowBits += 16;
      }

      this.err = 0; // error code, if happens (0 = Z_OK)

      this.msg = ''; // error message

      this.ended = false; // used to avoid multiple onEnd() calls

      this.chunks = []; // chunks of compressed data

      this.strm = new zstream();
      this.strm.avail_out = 0;
      let status = deflate_1$2.deflateInit2(this.strm, opt.level, opt.method, opt.windowBits, opt.memLevel, opt.strategy);

      if (status !== Z_OK$2) {
        throw new Error(messages[status]);
      }

      if (opt.header) {
        deflate_1$2.deflateSetHeader(this.strm, opt.header);
      }

      if (opt.dictionary) {
        let dict; // Convert data if needed

        if (typeof opt.dictionary === 'string') {
          // If we need to compress text, change encoding to utf8.
          dict = strings.string2buf(opt.dictionary);
        } else if (toString$1$1.call(opt.dictionary) === '[object ArrayBuffer]') {
          dict = new Uint8Array(opt.dictionary);
        } else {
          dict = opt.dictionary;
        }

        status = deflate_1$2.deflateSetDictionary(this.strm, dict);

        if (status !== Z_OK$2) {
          throw new Error(messages[status]);
        }

        this._dict_set = true;
      }
    }
    /**
     * Deflate#push(data[, flush_mode]) -> Boolean
     * - data (Uint8Array|ArrayBuffer|String): input data. Strings will be
     *   converted to utf8 byte sequence.
     * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
     *   See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH.
     *
     * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with
     * new compressed chunks. Returns `true` on success. The last data block must
     * have `flush_mode` Z_FINISH (or `true`). That will flush internal pending
     * buffers and call [[Deflate#onEnd]].
     *
     * On fail call [[Deflate#onEnd]] with error code and return false.
     *
     * ##### Example
     *
     * ```javascript
     * push(chunk, false); // push one of data chunks
     * ...
     * push(chunk, true);  // push last chunk
     * ```
     **/


    Deflate$1.prototype.push = function (data, flush_mode) {
      const strm = this.strm;
      const chunkSize = this.options.chunkSize;

      let status, _flush_mode;

      if (this.ended) {
        return false;
      }

      if (flush_mode === ~~flush_mode) _flush_mode = flush_mode;else _flush_mode = flush_mode === true ? Z_FINISH$2 : Z_NO_FLUSH$1; // Convert data if needed

      if (typeof data === 'string') {
        // If we need to compress text, change encoding to utf8.
        strm.input = strings.string2buf(data);
      } else if (toString$1$1.call(data) === '[object ArrayBuffer]') {
        strm.input = new Uint8Array(data);
      } else {
        strm.input = data;
      }

      strm.next_in = 0;
      strm.avail_in = strm.input.length;

      for (;;) {
        if (strm.avail_out === 0) {
          strm.output = new Uint8Array(chunkSize);
          strm.next_out = 0;
          strm.avail_out = chunkSize;
        } // Make sure avail_out > 6 to avoid repeating markers


        if ((_flush_mode === Z_SYNC_FLUSH || _flush_mode === Z_FULL_FLUSH) && strm.avail_out <= 6) {
          this.onData(strm.output.subarray(0, strm.next_out));
          strm.avail_out = 0;
          continue;
        }

        status = deflate_1$2.deflate(strm, _flush_mode); // Ended => flush and finish

        if (status === Z_STREAM_END$2) {
          if (strm.next_out > 0) {
            this.onData(strm.output.subarray(0, strm.next_out));
          }

          status = deflate_1$2.deflateEnd(this.strm);
          this.onEnd(status);
          this.ended = true;
          return status === Z_OK$2;
        } // Flush if out buffer full


        if (strm.avail_out === 0) {
          this.onData(strm.output);
          continue;
        } // Flush if requested and has data


        if (_flush_mode > 0 && strm.next_out > 0) {
          this.onData(strm.output.subarray(0, strm.next_out));
          strm.avail_out = 0;
          continue;
        }

        if (strm.avail_in === 0) break;
      }

      return true;
    };
    /**
     * Deflate#onData(chunk) -> Void
     * - chunk (Uint8Array): output data.
     *
     * By default, stores data blocks in `chunks[]` property and glue
     * those in `onEnd`. Override this handler, if you need another behaviour.
     **/


    Deflate$1.prototype.onData = function (chunk) {
      this.chunks.push(chunk);
    };
    /**
     * Deflate#onEnd(status) -> Void
     * - status (Number): deflate status. 0 (Z_OK) on success,
     *   other if not.
     *
     * Called once after you tell deflate that the input stream is
     * complete (Z_FINISH). By default - join collected chunks,
     * free memory and fill `results` / `err` properties.
     **/


    Deflate$1.prototype.onEnd = function (status) {
      // On success - join
      if (status === Z_OK$2) {
        this.result = common.flattenChunks(this.chunks);
      }

      this.chunks = [];
      this.err = status;
      this.msg = this.strm.msg;
    };
    /**
     * deflate(data[, options]) -> Uint8Array
     * - data (Uint8Array|String): input data to compress.
     * - options (Object): zlib deflate options.
     *
     * Compress `data` with deflate algorithm and `options`.
     *
     * Supported options are:
     *
     * - level
     * - windowBits
     * - memLevel
     * - strategy
     * - dictionary
     *
     * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
     * for more information on these.
     *
     * Sugar (options):
     *
     * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
     *   negative windowBits implicitly.
     *
     * ##### Example:
     *
     * ```javascript
     * const pako = require('pako')
     * const data = new Uint8Array([1,2,3,4,5,6,7,8,9]);
     *
     * console.log(pako.deflate(data));
     * ```
     **/


    function deflate$1(input, options) {
      const deflator = new Deflate$1(options);
      deflator.push(input, true); // That will never happens, if you don't cheat with options :)

      if (deflator.err) {
        throw deflator.msg || messages[deflator.err];
      }

      return deflator.result;
    }
    /**
     * deflateRaw(data[, options]) -> Uint8Array
     * - data (Uint8Array|String): input data to compress.
     * - options (Object): zlib deflate options.
     *
     * The same as [[deflate]], but creates raw data, without wrapper
     * (header and adler32 crc).
     **/


    function deflateRaw$1(input, options) {
      options = options || {};
      options.raw = true;
      return deflate$1(input, options);
    }
    /**
     * gzip(data[, options]) -> Uint8Array
     * - data (Uint8Array|String): input data to compress.
     * - options (Object): zlib deflate options.
     *
     * The same as [[deflate]], but create gzip wrapper instead of
     * deflate one.
     **/


    function gzip$1(input, options) {
      options = options || {};
      options.gzip = true;
      return deflate$1(input, options);
    }

    var Deflate_1$1 = Deflate$1;
    var deflate_2 = deflate$1;
    var deflateRaw_1$1 = deflateRaw$1;
    var gzip_1$1 = gzip$1;
    var constants$1 = constants$2;
    var deflate_1$1 = {
      Deflate: Deflate_1$1,
      deflate: deflate_2,
      deflateRaw: deflateRaw_1$1,
      gzip: gzip_1$1,
      constants: constants$1
    }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.
    // See state defs from inflate.js

    const BAD$1 = 30;
    /* got a data error -- remain here until reset */

    const TYPE$1 = 12;
    /* i: waiting for type bits, including last-flag bit */

    /*
       Decode literal, length, and distance codes and write out the resulting
       literal and match bytes until either not enough input or output is
       available, an end-of-block is encountered, or a data error is encountered.
       When large enough input and output buffers are supplied to inflate(), for
       example, a 16K input buffer and a 64K output buffer, more than 95% of the
       inflate execution time is spent in this routine.
        Entry assumptions:
             state.mode === LEN
            strm.avail_in >= 6
            strm.avail_out >= 258
            start >= strm.avail_out
            state.bits < 8
        On return, state.mode is one of:
             LEN -- ran out of enough output space or enough available input
            TYPE -- reached end of block code, inflate() to interpret next block
            BAD -- error in block data
        Notes:
         - The maximum input bits used by a length/distance pair is 15 bits for the
          length code, 5 bits for the length extra, 15 bits for the distance code,
          and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
          Therefore if strm.avail_in >= 6, then there is enough input to avoid
          checking for available input while decoding.
         - The maximum bytes that a single length/distance pair can output is 258
          bytes, which is the maximum length that can be coded.  inflate_fast()
          requires strm.avail_out >= 258 for each loop to avoid checking for
          output space.
     */

    var inffast = function inflate_fast(strm, start) {
      let _in;
      /* local strm.input */


      let last;
      /* have enough input while in < last */

      let _out;
      /* local strm.output */


      let beg;
      /* inflate()'s initial strm.output */

      let end;
      /* while out < end, enough space available */
      //#ifdef INFLATE_STRICT

      let dmax;
      /* maximum distance from zlib header */
      //#endif

      let wsize;
      /* window size or zero if not using window */

      let whave;
      /* valid bytes in the window */

      let wnext;
      /* window write index */
      // Use `s_window` instead `window`, avoid conflict with instrumentation tools

      let s_window;
      /* allocated sliding window, if wsize != 0 */

      let hold;
      /* local strm.hold */

      let bits;
      /* local strm.bits */

      let lcode;
      /* local strm.lencode */

      let dcode;
      /* local strm.distcode */

      let lmask;
      /* mask for first level of length codes */

      let dmask;
      /* mask for first level of distance codes */

      let here;
      /* retrieved table entry */

      let op;
      /* code bits, operation, extra bits, or */

      /*  window position, window bytes to copy */

      let len;
      /* match length, unused bytes */

      let dist;
      /* match distance */

      let from;
      /* where to copy match from */

      let from_source;
      let input, output; // JS specific, because we have no pointers

      /* copy state to local variables */

      const state = strm.state; //here = state.here;

      _in = strm.next_in;
      input = strm.input;
      last = _in + (strm.avail_in - 5);
      _out = strm.next_out;
      output = strm.output;
      beg = _out - (start - strm.avail_out);
      end = _out + (strm.avail_out - 257); //#ifdef INFLATE_STRICT

      dmax = state.dmax; //#endif

      wsize = state.wsize;
      whave = state.whave;
      wnext = state.wnext;
      s_window = state.window;
      hold = state.hold;
      bits = state.bits;
      lcode = state.lencode;
      dcode = state.distcode;
      lmask = (1 << state.lenbits) - 1;
      dmask = (1 << state.distbits) - 1;
      /* decode literals and length/distances until end-of-block or not enough
         input data or output space */

      top: do {
        if (bits < 15) {
          hold += input[_in++] << bits;
          bits += 8;
          hold += input[_in++] << bits;
          bits += 8;
        }

        here = lcode[hold & lmask];

        dolen: for (;;) {
          // Goto emulation
          op = here >>> 24
          /*here.bits*/
          ;
          hold >>>= op;
          bits -= op;
          op = here >>> 16 & 0xff
          /*here.op*/
          ;

          if (op === 0) {
            /* literal */
            //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
            //        "inflate:         literal '%c'\n" :
            //        "inflate:         literal 0x%02x\n", here.val));
            output[_out++] = here & 0xffff
            /*here.val*/
            ;
          } else if (op & 16) {
            /* length base */
            len = here & 0xffff
            /*here.val*/
            ;
            op &= 15;
            /* number of extra bits */

            if (op) {
              if (bits < op) {
                hold += input[_in++] << bits;
                bits += 8;
              }

              len += hold & (1 << op) - 1;
              hold >>>= op;
              bits -= op;
            } //Tracevv((stderr, "inflate:         length %u\n", len));


            if (bits < 15) {
              hold += input[_in++] << bits;
              bits += 8;
              hold += input[_in++] << bits;
              bits += 8;
            }

            here = dcode[hold & dmask];

            dodist: for (;;) {
              // goto emulation
              op = here >>> 24
              /*here.bits*/
              ;
              hold >>>= op;
              bits -= op;
              op = here >>> 16 & 0xff
              /*here.op*/
              ;

              if (op & 16) {
                /* distance base */
                dist = here & 0xffff
                /*here.val*/
                ;
                op &= 15;
                /* number of extra bits */

                if (bits < op) {
                  hold += input[_in++] << bits;
                  bits += 8;

                  if (bits < op) {
                    hold += input[_in++] << bits;
                    bits += 8;
                  }
                }

                dist += hold & (1 << op) - 1; //#ifdef INFLATE_STRICT

                if (dist > dmax) {
                  strm.msg = 'invalid distance too far back';
                  state.mode = BAD$1;
                  break top;
                } //#endif


                hold >>>= op;
                bits -= op; //Tracevv((stderr, "inflate:         distance %u\n", dist));

                op = _out - beg;
                /* max distance in output */

                if (dist > op) {
                  /* see if copy from window */
                  op = dist - op;
                  /* distance back in window */

                  if (op > whave) {
                    if (state.sane) {
                      strm.msg = 'invalid distance too far back';
                      state.mode = BAD$1;
                      break top;
                    } // (!) This block is disabled in zlib defaults,
                    // don't enable it for binary compatibility
                    //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
                    //                if (len <= op - whave) {
                    //                  do {
                    //                    output[_out++] = 0;
                    //                  } while (--len);
                    //                  continue top;
                    //                }
                    //                len -= op - whave;
                    //                do {
                    //                  output[_out++] = 0;
                    //                } while (--op > whave);
                    //                if (op === 0) {
                    //                  from = _out - dist;
                    //                  do {
                    //                    output[_out++] = output[from++];
                    //                  } while (--len);
                    //                  continue top;
                    //                }
                    //#endif

                  }

                  from = 0; // window index

                  from_source = s_window;

                  if (wnext === 0) {
                    /* very common case */
                    from += wsize - op;

                    if (op < len) {
                      /* some from window */
                      len -= op;

                      do {
                        output[_out++] = s_window[from++];
                      } while (--op);

                      from = _out - dist;
                      /* rest from output */

                      from_source = output;
                    }
                  } else if (wnext < op) {
                    /* wrap around window */
                    from += wsize + wnext - op;
                    op -= wnext;

                    if (op < len) {
                      /* some from end of window */
                      len -= op;

                      do {
                        output[_out++] = s_window[from++];
                      } while (--op);

                      from = 0;

                      if (wnext < len) {
                        /* some from start of window */
                        op = wnext;
                        len -= op;

                        do {
                          output[_out++] = s_window[from++];
                        } while (--op);

                        from = _out - dist;
                        /* rest from output */

                        from_source = output;
                      }
                    }
                  } else {
                    /* contiguous in window */
                    from += wnext - op;

                    if (op < len) {
                      /* some from window */
                      len -= op;

                      do {
                        output[_out++] = s_window[from++];
                      } while (--op);

                      from = _out - dist;
                      /* rest from output */

                      from_source = output;
                    }
                  }

                  while (len > 2) {
                    output[_out++] = from_source[from++];
                    output[_out++] = from_source[from++];
                    output[_out++] = from_source[from++];
                    len -= 3;
                  }

                  if (len) {
                    output[_out++] = from_source[from++];

                    if (len > 1) {
                      output[_out++] = from_source[from++];
                    }
                  }
                } else {
                  from = _out - dist;
                  /* copy direct from output */

                  do {
                    /* minimum length is three */
                    output[_out++] = output[from++];
                    output[_out++] = output[from++];
                    output[_out++] = output[from++];
                    len -= 3;
                  } while (len > 2);

                  if (len) {
                    output[_out++] = output[from++];

                    if (len > 1) {
                      output[_out++] = output[from++];
                    }
                  }
                }
              } else if ((op & 64) === 0) {
                /* 2nd level distance code */
                here = dcode[(here & 0xffff
                /*here.val*/
                ) + (hold & (1 << op) - 1)];
                continue dodist;
              } else {
                strm.msg = 'invalid distance code';
                state.mode = BAD$1;
                break top;
              }

              break; // need to emulate goto via "continue"
            }
          } else if ((op & 64) === 0) {
            /* 2nd level length code */
            here = lcode[(here & 0xffff
            /*here.val*/
            ) + (hold & (1 << op) - 1)];
            continue dolen;
          } else if (op & 32) {
            /* end-of-block */
            //Tracevv((stderr, "inflate:         end of block\n"));
            state.mode = TYPE$1;
            break top;
          } else {
            strm.msg = 'invalid literal/length code';
            state.mode = BAD$1;
            break top;
          }

          break; // need to emulate goto via "continue"
        }
      } while (_in < last && _out < end);
      /* return unused bytes (on entry, bits < 8, so in won't go too far back) */


      len = bits >> 3;
      _in -= len;
      bits -= len << 3;
      hold &= (1 << bits) - 1;
      /* update state and return */

      strm.next_in = _in;
      strm.next_out = _out;
      strm.avail_in = _in < last ? 5 + (last - _in) : 5 - (_in - last);
      strm.avail_out = _out < end ? 257 + (end - _out) : 257 - (_out - end);
      state.hold = hold;
      state.bits = bits;
      return;
    }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.


    const MAXBITS = 15;
    const ENOUGH_LENS$1 = 852;
    const ENOUGH_DISTS$1 = 592; //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);

    const CODES$1 = 0;
    const LENS$1 = 1;
    const DISTS$1 = 2;
    const lbase = new Uint16Array([
    /* Length codes 257..285 base */
    3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0]);
    const lext = new Uint8Array([
    /* Length codes 257..285 extra */
    16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78]);
    const dbase = new Uint16Array([
    /* Distance codes 0..29 base */
    1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0]);
    const dext = new Uint8Array([
    /* Distance codes 0..29 extra */
    16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 64, 64]);

    const inflate_table = (type, lens, lens_index, codes, table, table_index, work, opts) => {
      const bits = opts.bits; //here = opts.here; /* table entry for duplication */

      let len = 0;
      /* a code's length in bits */

      let sym = 0;
      /* index of code symbols */

      let min = 0,
          max = 0;
      /* minimum and maximum code lengths */

      let root = 0;
      /* number of index bits for root table */

      let curr = 0;
      /* number of index bits for current table */

      let drop = 0;
      /* code bits to drop for sub-table */

      let left = 0;
      /* number of prefix codes available */

      let used = 0;
      /* code entries in table used */

      let huff = 0;
      /* Huffman code */

      let incr;
      /* for incrementing code, index */

      let fill;
      /* index for replicating entries */

      let low;
      /* low bits for current root entry */

      let mask;
      /* mask for low root bits */

      let next;
      /* next available space in table */

      let base = null;
      /* base value table to use */

      let base_index = 0; //  let shoextra;    /* extra bits table to use */

      let end;
      /* use base and extra for symbol > end */

      const count = new Uint16Array(MAXBITS + 1); //[MAXBITS+1];    /* number of codes of each length */

      const offs = new Uint16Array(MAXBITS + 1); //[MAXBITS+1];     /* offsets in table for each length */

      let extra = null;
      let extra_index = 0;
      let here_bits, here_op, here_val;
      /*
       Process a set of code lengths to create a canonical Huffman code.  The
       code lengths are lens[0..codes-1].  Each length corresponds to the
       symbols 0..codes-1.  The Huffman code is generated by first sorting the
       symbols by length from short to long, and retaining the symbol order
       for codes with equal lengths.  Then the code starts with all zero bits
       for the first code of the shortest length, and the codes are integer
       increments for the same length, and zeros are appended as the length
       increases.  For the deflate format, these bits are stored backwards
       from their more natural integer increment ordering, and so when the
       decoding tables are built in the large loop below, the integer codes
       are incremented backwards.
        This routine assumes, but does not check, that all of the entries in
       lens[] are in the range 0..MAXBITS.  The caller must assure this.
       1..MAXBITS is interpreted as that code length.  zero means that that
       symbol does not occur in this code.
        The codes are sorted by computing a count of codes for each length,
       creating from that a table of starting indices for each length in the
       sorted table, and then entering the symbols in order in the sorted
       table.  The sorted table is work[], with that space being provided by
       the caller.
        The length counts are used for other purposes as well, i.e. finding
       the minimum and maximum length codes, determining if there are any
       codes at all, checking for a valid set of lengths, and looking ahead
       at length counts to determine sub-table sizes when building the
       decoding tables.
       */

      /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */

      for (len = 0; len <= MAXBITS; len++) {
        count[len] = 0;
      }

      for (sym = 0; sym < codes; sym++) {
        count[lens[lens_index + sym]]++;
      }
      /* bound code lengths, force root to be within code lengths */


      root = bits;

      for (max = MAXBITS; max >= 1; max--) {
        if (count[max] !== 0) {
          break;
        }
      }

      if (root > max) {
        root = max;
      }

      if (max === 0) {
        /* no symbols to code at all */
        //table.op[opts.table_index] = 64;  //here.op = (var char)64;    /* invalid code marker */
        //table.bits[opts.table_index] = 1;   //here.bits = (var char)1;
        //table.val[opts.table_index++] = 0;   //here.val = (var short)0;
        table[table_index++] = 1 << 24 | 64 << 16 | 0; //table.op[opts.table_index] = 64;
        //table.bits[opts.table_index] = 1;
        //table.val[opts.table_index++] = 0;

        table[table_index++] = 1 << 24 | 64 << 16 | 0;
        opts.bits = 1;
        return 0;
        /* no symbols, but wait for decoding to report error */
      }

      for (min = 1; min < max; min++) {
        if (count[min] !== 0) {
          break;
        }
      }

      if (root < min) {
        root = min;
      }
      /* check for an over-subscribed or incomplete set of lengths */


      left = 1;

      for (len = 1; len <= MAXBITS; len++) {
        left <<= 1;
        left -= count[len];

        if (left < 0) {
          return -1;
        }
        /* over-subscribed */

      }

      if (left > 0 && (type === CODES$1 || max !== 1)) {
        return -1;
        /* incomplete set */
      }
      /* generate offsets into symbol table for each length for sorting */


      offs[1] = 0;

      for (len = 1; len < MAXBITS; len++) {
        offs[len + 1] = offs[len] + count[len];
      }
      /* sort symbols by length, by symbol order within each length */


      for (sym = 0; sym < codes; sym++) {
        if (lens[lens_index + sym] !== 0) {
          work[offs[lens[lens_index + sym]]++] = sym;
        }
      }
      /*
       Create and fill in decoding tables.  In this loop, the table being
       filled is at next and has curr index bits.  The code being used is huff
       with length len.  That code is converted to an index by dropping drop
       bits off of the bottom.  For codes where len is less than drop + curr,
       those top drop + curr - len bits are incremented through all values to
       fill the table with replicated entries.
        root is the number of index bits for the root table.  When len exceeds
       root, sub-tables are created pointed to by the root entry with an index
       of the low root bits of huff.  This is saved in low to check for when a
       new sub-table should be started.  drop is zero when the root table is
       being filled, and drop is root when sub-tables are being filled.
        When a new sub-table is needed, it is necessary to look ahead in the
       code lengths to determine what size sub-table is needed.  The length
       counts are used for this, and so count[] is decremented as codes are
       entered in the tables.
        used keeps track of how many table entries have been allocated from the
       provided *table space.  It is checked for LENS and DIST tables against
       the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
       the initial root table size constants.  See the comments in inftrees.h
       for more information.
        sym increments through all symbols, and the loop terminates when
       all codes of length max, i.e. all codes, have been processed.  This
       routine permits incomplete codes, so another loop after this one fills
       in the rest of the decoding tables with invalid code markers.
       */

      /* set up for code type */
      // poor man optimization - use if-else instead of switch,
      // to avoid deopts in old v8


      if (type === CODES$1) {
        base = extra = work;
        /* dummy value--not used */

        end = 19;
      } else if (type === LENS$1) {
        base = lbase;
        base_index -= 257;
        extra = lext;
        extra_index -= 257;
        end = 256;
      } else {
        /* DISTS */
        base = dbase;
        extra = dext;
        end = -1;
      }
      /* initialize opts for loop */


      huff = 0;
      /* starting code */

      sym = 0;
      /* starting code symbol */

      len = min;
      /* starting code length */

      next = table_index;
      /* current table to fill in */

      curr = root;
      /* current table index bits */

      drop = 0;
      /* current bits to drop from code for index */

      low = -1;
      /* trigger new sub-table when len > root */

      used = 1 << root;
      /* use root table entries */

      mask = used - 1;
      /* mask for comparing low */

      /* check available table space */

      if (type === LENS$1 && used > ENOUGH_LENS$1 || type === DISTS$1 && used > ENOUGH_DISTS$1) {
        return 1;
      }
      /* process all codes and make table entries */


      for (;;) {
        /* create table entry */
        here_bits = len - drop;

        if (work[sym] < end) {
          here_op = 0;
          here_val = work[sym];
        } else if (work[sym] > end) {
          here_op = extra[extra_index + work[sym]];
          here_val = base[base_index + work[sym]];
        } else {
          here_op = 32 + 64;
          /* end of block */

          here_val = 0;
        }
        /* replicate for those indices with low len bits equal to huff */


        incr = 1 << len - drop;
        fill = 1 << curr;
        min = fill;
        /* save offset to next table */

        do {
          fill -= incr;
          table[next + (huff >> drop) + fill] = here_bits << 24 | here_op << 16 | here_val | 0;
        } while (fill !== 0);
        /* backwards increment the len-bit code huff */


        incr = 1 << len - 1;

        while (huff & incr) {
          incr >>= 1;
        }

        if (incr !== 0) {
          huff &= incr - 1;
          huff += incr;
        } else {
          huff = 0;
        }
        /* go to next symbol, update count, len */


        sym++;

        if (--count[len] === 0) {
          if (len === max) {
            break;
          }

          len = lens[lens_index + work[sym]];
        }
        /* create new sub-table if needed */


        if (len > root && (huff & mask) !== low) {
          /* if first time, transition to sub-tables */
          if (drop === 0) {
            drop = root;
          }
          /* increment past last table */


          next += min;
          /* here min is 1 << curr */

          /* determine length of next table */

          curr = len - drop;
          left = 1 << curr;

          while (curr + drop < max) {
            left -= count[curr + drop];

            if (left <= 0) {
              break;
            }

            curr++;
            left <<= 1;
          }
          /* check for enough space */


          used += 1 << curr;

          if (type === LENS$1 && used > ENOUGH_LENS$1 || type === DISTS$1 && used > ENOUGH_DISTS$1) {
            return 1;
          }
          /* point entry in root table to sub-table */


          low = huff & mask;
          /*table.op[low] = curr;
          table.bits[low] = root;
          table.val[low] = next - opts.table_index;*/

          table[low] = root << 24 | curr << 16 | next - table_index | 0;
        }
      }
      /* fill in remaining table entry if code is incomplete (guaranteed to have
       at most one remaining entry, since if the code is incomplete, the
       maximum code length that was allowed to get this far is one bit) */


      if (huff !== 0) {
        //table.op[next + huff] = 64;            /* invalid code marker */
        //table.bits[next + huff] = len - drop;
        //table.val[next + huff] = 0;
        table[next + huff] = len - drop << 24 | 64 << 16 | 0;
      }
      /* set return parameters */
      //opts.table_index += used;


      opts.bits = root;
      return 0;
    };

    var inftrees = inflate_table; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    const CODES = 0;
    const LENS = 1;
    const DISTS = 2;
    /* Public constants ==========================================================*/

    /* ===========================================================================*/

    const {
      Z_FINISH: Z_FINISH$1,
      Z_BLOCK,
      Z_TREES,
      Z_OK: Z_OK$1,
      Z_STREAM_END: Z_STREAM_END$1,
      Z_NEED_DICT: Z_NEED_DICT$1,
      Z_STREAM_ERROR: Z_STREAM_ERROR$1,
      Z_DATA_ERROR: Z_DATA_ERROR$1,
      Z_MEM_ERROR: Z_MEM_ERROR$1,
      Z_BUF_ERROR,
      Z_DEFLATED
    } = constants$2;
    /* STATES ====================================================================*/

    /* ===========================================================================*/

    const HEAD = 1;
    /* i: waiting for magic header */

    const FLAGS = 2;
    /* i: waiting for method and flags (gzip) */

    const TIME = 3;
    /* i: waiting for modification time (gzip) */

    const OS = 4;
    /* i: waiting for extra flags and operating system (gzip) */

    const EXLEN = 5;
    /* i: waiting for extra length (gzip) */

    const EXTRA = 6;
    /* i: waiting for extra bytes (gzip) */

    const NAME$1 = 7;
    /* i: waiting for end of file name (gzip) */

    const COMMENT = 8;
    /* i: waiting for end of comment (gzip) */

    const HCRC = 9;
    /* i: waiting for header crc (gzip) */

    const DICTID = 10;
    /* i: waiting for dictionary check value */

    const DICT = 11;
    /* waiting for inflateSetDictionary() call */

    const TYPE = 12;
    /* i: waiting for type bits, including last-flag bit */

    const TYPEDO = 13;
    /* i: same, but skip check to exit inflate on new block */

    const STORED = 14;
    /* i: waiting for stored size (length and complement) */

    const COPY_ = 15;
    /* i/o: same as COPY below, but only first time in */

    const COPY = 16;
    /* i/o: waiting for input or output to copy stored block */

    const TABLE = 17;
    /* i: waiting for dynamic block table lengths */

    const LENLENS = 18;
    /* i: waiting for code length code lengths */

    const CODELENS = 19;
    /* i: waiting for length/lit and distance code lengths */

    const LEN_ = 20;
    /* i: same as LEN below, but only first time in */

    const LEN = 21;
    /* i: waiting for length/lit/eob code */

    const LENEXT = 22;
    /* i: waiting for length extra bits */

    const DIST = 23;
    /* i: waiting for distance code */

    const DISTEXT = 24;
    /* i: waiting for distance extra bits */

    const MATCH = 25;
    /* o: waiting for output space to copy string */

    const LIT = 26;
    /* o: waiting for output space to write literal */

    const CHECK = 27;
    /* i: waiting for 32-bit check value */

    const LENGTH = 28;
    /* i: waiting for 32-bit length (gzip) */

    const DONE = 29;
    /* finished check, done -- remain here until reset */

    const BAD = 30;
    /* got a data error -- remain here until reset */

    const MEM = 31;
    /* got an inflate() memory error -- remain here until reset */

    const SYNC = 32;
    /* looking for synchronization bytes to restart inflate() */

    /* ===========================================================================*/

    const ENOUGH_LENS = 852;
    const ENOUGH_DISTS = 592; //const ENOUGH =  (ENOUGH_LENS+ENOUGH_DISTS);

    const MAX_WBITS = 15;
    /* 32K LZ77 window */

    const DEF_WBITS = MAX_WBITS;

    const zswap32 = q => {
      return (q >>> 24 & 0xff) + (q >>> 8 & 0xff00) + ((q & 0xff00) << 8) + ((q & 0xff) << 24);
    };

    function InflateState() {
      this.mode = 0;
      /* current inflate mode */

      this.last = false;
      /* true if processing last block */

      this.wrap = 0;
      /* bit 0 true for zlib, bit 1 true for gzip */

      this.havedict = false;
      /* true if dictionary provided */

      this.flags = 0;
      /* gzip header method and flags (0 if zlib) */

      this.dmax = 0;
      /* zlib header max distance (INFLATE_STRICT) */

      this.check = 0;
      /* protected copy of check value */

      this.total = 0;
      /* protected copy of output count */
      // TODO: may be {}

      this.head = null;
      /* where to save gzip header information */

      /* sliding window */

      this.wbits = 0;
      /* log base 2 of requested window size */

      this.wsize = 0;
      /* window size or zero if not using window */

      this.whave = 0;
      /* valid bytes in the window */

      this.wnext = 0;
      /* window write index */

      this.window = null;
      /* allocated sliding window, if needed */

      /* bit accumulator */

      this.hold = 0;
      /* input bit accumulator */

      this.bits = 0;
      /* number of bits in "in" */

      /* for string and stored block copying */

      this.length = 0;
      /* literal or length of data to copy */

      this.offset = 0;
      /* distance back to copy string from */

      /* for table and code decoding */

      this.extra = 0;
      /* extra bits needed */

      /* fixed and dynamic code tables */

      this.lencode = null;
      /* starting table for length/literal codes */

      this.distcode = null;
      /* starting table for distance codes */

      this.lenbits = 0;
      /* index bits for lencode */

      this.distbits = 0;
      /* index bits for distcode */

      /* dynamic table building */

      this.ncode = 0;
      /* number of code length code lengths */

      this.nlen = 0;
      /* number of length code lengths */

      this.ndist = 0;
      /* number of distance code lengths */

      this.have = 0;
      /* number of code lengths in lens[] */

      this.next = null;
      /* next available space in codes[] */

      this.lens = new Uint16Array(320);
      /* temporary storage for code lengths */

      this.work = new Uint16Array(288);
      /* work area for code table building */

      /*
       because we don't have pointers in js, we use lencode and distcode directly
       as buffers so we don't need codes
      */
      //this.codes = new Int32Array(ENOUGH);       /* space for code tables */

      this.lendyn = null;
      /* dynamic table for length/literal codes (JS specific) */

      this.distdyn = null;
      /* dynamic table for distance codes (JS specific) */

      this.sane = 0;
      /* if false, allow invalid distance too far */

      this.back = 0;
      /* bits back of last unprocessed length/lit */

      this.was = 0;
      /* initial length of match */
    }

    const inflateResetKeep = strm => {
      if (!strm || !strm.state) {
        return Z_STREAM_ERROR$1;
      }

      const state = strm.state;
      strm.total_in = strm.total_out = state.total = 0;
      strm.msg = '';
      /*Z_NULL*/

      if (state.wrap) {
        /* to support ill-conceived Java test suite */
        strm.adler = state.wrap & 1;
      }

      state.mode = HEAD;
      state.last = 0;
      state.havedict = 0;
      state.dmax = 32768;
      state.head = null
      /*Z_NULL*/
      ;
      state.hold = 0;
      state.bits = 0; //state.lencode = state.distcode = state.next = state.codes;

      state.lencode = state.lendyn = new Int32Array(ENOUGH_LENS);
      state.distcode = state.distdyn = new Int32Array(ENOUGH_DISTS);
      state.sane = 1;
      state.back = -1; //Tracev((stderr, "inflate: reset\n"));

      return Z_OK$1;
    };

    const inflateReset = strm => {
      if (!strm || !strm.state) {
        return Z_STREAM_ERROR$1;
      }

      const state = strm.state;
      state.wsize = 0;
      state.whave = 0;
      state.wnext = 0;
      return inflateResetKeep(strm);
    };

    const inflateReset2 = (strm, windowBits) => {
      let wrap;
      /* get the state */

      if (!strm || !strm.state) {
        return Z_STREAM_ERROR$1;
      }

      const state = strm.state;
      /* extract wrap request from windowBits parameter */

      if (windowBits < 0) {
        wrap = 0;
        windowBits = -windowBits;
      } else {
        wrap = (windowBits >> 4) + 1;

        if (windowBits < 48) {
          windowBits &= 15;
        }
      }
      /* set number of window bits, free window if different */


      if (windowBits && (windowBits < 8 || windowBits > 15)) {
        return Z_STREAM_ERROR$1;
      }

      if (state.window !== null && state.wbits !== windowBits) {
        state.window = null;
      }
      /* update state and reset the rest of it */


      state.wrap = wrap;
      state.wbits = windowBits;
      return inflateReset(strm);
    };

    const inflateInit2 = (strm, windowBits) => {
      if (!strm) {
        return Z_STREAM_ERROR$1;
      } //strm.msg = Z_NULL;                 /* in case we return an error */


      const state = new InflateState(); //if (state === Z_NULL) return Z_MEM_ERROR;
      //Tracev((stderr, "inflate: allocated\n"));

      strm.state = state;
      state.window = null
      /*Z_NULL*/
      ;
      const ret = inflateReset2(strm, windowBits);

      if (ret !== Z_OK$1) {
        strm.state = null
        /*Z_NULL*/
        ;
      }

      return ret;
    };

    const inflateInit = strm => {
      return inflateInit2(strm, DEF_WBITS);
    };
    /*
     Return state with length and distance decoding tables and index sizes set to
     fixed code decoding.  Normally this returns fixed tables from inffixed.h.
     If BUILDFIXED is defined, then instead this routine builds the tables the
     first time it's called, and returns those tables the first time and
     thereafter.  This reduces the size of the code by about 2K bytes, in
     exchange for a little execution time.  However, BUILDFIXED should not be
     used for threaded applications, since the rewriting of the tables and virgin
     may not be thread-safe.
     */


    let virgin = true;
    let lenfix, distfix; // We have no pointers in JS, so keep tables separate

    const fixedtables = state => {
      /* build fixed huffman tables if first call (may not be thread safe) */
      if (virgin) {
        lenfix = new Int32Array(512);
        distfix = new Int32Array(32);
        /* literal/length table */

        let sym = 0;

        while (sym < 144) {
          state.lens[sym++] = 8;
        }

        while (sym < 256) {
          state.lens[sym++] = 9;
        }

        while (sym < 280) {
          state.lens[sym++] = 7;
        }

        while (sym < 288) {
          state.lens[sym++] = 8;
        }

        inftrees(LENS, state.lens, 0, 288, lenfix, 0, state.work, {
          bits: 9
        });
        /* distance table */

        sym = 0;

        while (sym < 32) {
          state.lens[sym++] = 5;
        }

        inftrees(DISTS, state.lens, 0, 32, distfix, 0, state.work, {
          bits: 5
        });
        /* do this just once */

        virgin = false;
      }

      state.lencode = lenfix;
      state.lenbits = 9;
      state.distcode = distfix;
      state.distbits = 5;
    };
    /*
     Update the window with the last wsize (normally 32K) bytes written before
     returning.  If window does not exist yet, create it.  This is only called
     when a window is already in use, or when output has been written during this
     inflate call, but the end of the deflate stream has not been reached yet.
     It is also called to create a window for dictionary data when a dictionary
     is loaded.
      Providing output buffers larger than 32K to inflate() should provide a speed
     advantage, since only the last 32K of output is copied to the sliding window
     upon return from inflate(), and since all distances after the first 32K of
     output will fall in the output data, making match copies simpler and faster.
     The advantage may be dependent on the size of the processor's data caches.
     */


    const updatewindow = (strm, src, end, copy) => {
      let dist;
      const state = strm.state;
      /* if it hasn't been done already, allocate space for the window */

      if (state.window === null) {
        state.wsize = 1 << state.wbits;
        state.wnext = 0;
        state.whave = 0;
        state.window = new Uint8Array(state.wsize);
      }
      /* copy state->wsize or less output bytes into the circular window */


      if (copy >= state.wsize) {
        state.window.set(src.subarray(end - state.wsize, end), 0);
        state.wnext = 0;
        state.whave = state.wsize;
      } else {
        dist = state.wsize - state.wnext;

        if (dist > copy) {
          dist = copy;
        } //zmemcpy(state->window + state->wnext, end - copy, dist);


        state.window.set(src.subarray(end - copy, end - copy + dist), state.wnext);
        copy -= dist;

        if (copy) {
          //zmemcpy(state->window, end - copy, copy);
          state.window.set(src.subarray(end - copy, end), 0);
          state.wnext = copy;
          state.whave = state.wsize;
        } else {
          state.wnext += dist;

          if (state.wnext === state.wsize) {
            state.wnext = 0;
          }

          if (state.whave < state.wsize) {
            state.whave += dist;
          }
        }
      }

      return 0;
    };

    const inflate$2 = (strm, flush) => {
      let state;
      let input, output; // input/output buffers

      let next;
      /* next input INDEX */

      let put;
      /* next output INDEX */

      let have, left;
      /* available input and output */

      let hold;
      /* bit buffer */

      let bits;
      /* bits in bit buffer */

      let _in, _out;
      /* save starting available input and output */


      let copy;
      /* number of stored or match bytes to copy */

      let from;
      /* where to copy match bytes from */

      let from_source;
      let here = 0;
      /* current decoding table entry */

      let here_bits, here_op, here_val; // paked "here" denormalized (JS specific)
      //let last;                   /* parent table entry */

      let last_bits, last_op, last_val; // paked "last" denormalized (JS specific)

      let len;
      /* length to copy for repeats, bits to drop */

      let ret;
      /* return code */

      const hbuf = new Uint8Array(4);
      /* buffer for gzip header crc calculation */

      let opts;
      let n; // temporary variable for NEED_BITS

      const order =
      /* permutation of code lengths */
      new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);

      if (!strm || !strm.state || !strm.output || !strm.input && strm.avail_in !== 0) {
        return Z_STREAM_ERROR$1;
      }

      state = strm.state;

      if (state.mode === TYPE) {
        state.mode = TYPEDO;
      }
      /* skip check */
      //--- LOAD() ---


      put = strm.next_out;
      output = strm.output;
      left = strm.avail_out;
      next = strm.next_in;
      input = strm.input;
      have = strm.avail_in;
      hold = state.hold;
      bits = state.bits; //---

      _in = have;
      _out = left;
      ret = Z_OK$1;

      inf_leave: // goto emulation
      for (;;) {
        switch (state.mode) {
          case HEAD:
            if (state.wrap === 0) {
              state.mode = TYPEDO;
              break;
            } //=== NEEDBITS(16);


            while (bits < 16) {
              if (have === 0) {
                break inf_leave;
              }

              have--;
              hold += input[next++] << bits;
              bits += 8;
            } //===//


            if (state.wrap & 2 && hold === 0x8b1f) {
              /* gzip header */
              state.check = 0
              /*crc32(0L, Z_NULL, 0)*/
              ; //=== CRC2(state.check, hold);

              hbuf[0] = hold & 0xff;
              hbuf[1] = hold >>> 8 & 0xff;
              state.check = crc32_1(state.check, hbuf, 2, 0); //===//
              //=== INITBITS();

              hold = 0;
              bits = 0; //===//

              state.mode = FLAGS;
              break;
            }

            state.flags = 0;
            /* expect zlib header */

            if (state.head) {
              state.head.done = false;
            }

            if (!(state.wrap & 1) ||
            /* check if zlib header allowed */
            (((hold & 0xff
            /*BITS(8)*/
            ) << 8) + (hold >> 8)) % 31) {
              strm.msg = 'incorrect header check';
              state.mode = BAD;
              break;
            }

            if ((hold & 0x0f
            /*BITS(4)*/
            ) !== Z_DEFLATED) {
              strm.msg = 'unknown compression method';
              state.mode = BAD;
              break;
            } //--- DROPBITS(4) ---//


            hold >>>= 4;
            bits -= 4; //---//

            len = (hold & 0x0f
            /*BITS(4)*/
            ) + 8;

            if (state.wbits === 0) {
              state.wbits = len;
            } else if (len > state.wbits) {
              strm.msg = 'invalid window size';
              state.mode = BAD;
              break;
            } // !!! pako patch. Force use `options.windowBits` if passed.
            // Required to always use max window size by default.


            state.dmax = 1 << state.wbits; //state.dmax = 1 << len;
            //Tracev((stderr, "inflate:   zlib header ok\n"));

            strm.adler = state.check = 1
            /*adler32(0L, Z_NULL, 0)*/
            ;
            state.mode = hold & 0x200 ? DICTID : TYPE; //=== INITBITS();

            hold = 0;
            bits = 0; //===//

            break;

          case FLAGS:
            //=== NEEDBITS(16); */
            while (bits < 16) {
              if (have === 0) {
                break inf_leave;
              }

              have--;
              hold += input[next++] << bits;
              bits += 8;
            } //===//


            state.flags = hold;

            if ((state.flags & 0xff) !== Z_DEFLATED) {
              strm.msg = 'unknown compression method';
              state.mode = BAD;
              break;
            }

            if (state.flags & 0xe000) {
              strm.msg = 'unknown header flags set';
              state.mode = BAD;
              break;
            }

            if (state.head) {
              state.head.text = hold >> 8 & 1;
            }

            if (state.flags & 0x0200) {
              //=== CRC2(state.check, hold);
              hbuf[0] = hold & 0xff;
              hbuf[1] = hold >>> 8 & 0xff;
              state.check = crc32_1(state.check, hbuf, 2, 0); //===//
            } //=== INITBITS();


            hold = 0;
            bits = 0; //===//

            state.mode = TIME;

          /* falls through */

          case TIME:
            //=== NEEDBITS(32); */
            while (bits < 32) {
              if (have === 0) {
                break inf_leave;
              }

              have--;
              hold += input[next++] << bits;
              bits += 8;
            } //===//


            if (state.head) {
              state.head.time = hold;
            }

            if (state.flags & 0x0200) {
              //=== CRC4(state.check, hold)
              hbuf[0] = hold & 0xff;
              hbuf[1] = hold >>> 8 & 0xff;
              hbuf[2] = hold >>> 16 & 0xff;
              hbuf[3] = hold >>> 24 & 0xff;
              state.check = crc32_1(state.check, hbuf, 4, 0); //===
            } //=== INITBITS();


            hold = 0;
            bits = 0; //===//

            state.mode = OS;

          /* falls through */

          case OS:
            //=== NEEDBITS(16); */
            while (bits < 16) {
              if (have === 0) {
                break inf_leave;
              }

              have--;
              hold += input[next++] << bits;
              bits += 8;
            } //===//


            if (state.head) {
              state.head.xflags = hold & 0xff;
              state.head.os = hold >> 8;
            }

            if (state.flags & 0x0200) {
              //=== CRC2(state.check, hold);
              hbuf[0] = hold & 0xff;
              hbuf[1] = hold >>> 8 & 0xff;
              state.check = crc32_1(state.check, hbuf, 2, 0); //===//
            } //=== INITBITS();


            hold = 0;
            bits = 0; //===//

            state.mode = EXLEN;

          /* falls through */

          case EXLEN:
            if (state.flags & 0x0400) {
              //=== NEEDBITS(16); */
              while (bits < 16) {
                if (have === 0) {
                  break inf_leave;
                }

                have--;
                hold += input[next++] << bits;
                bits += 8;
              } //===//


              state.length = hold;

              if (state.head) {
                state.head.extra_len = hold;
              }

              if (state.flags & 0x0200) {
                //=== CRC2(state.check, hold);
                hbuf[0] = hold & 0xff;
                hbuf[1] = hold >>> 8 & 0xff;
                state.check = crc32_1(state.check, hbuf, 2, 0); //===//
              } //=== INITBITS();


              hold = 0;
              bits = 0; //===//
            } else if (state.head) {
              state.head.extra = null
              /*Z_NULL*/
              ;
            }

            state.mode = EXTRA;

          /* falls through */

          case EXTRA:
            if (state.flags & 0x0400) {
              copy = state.length;

              if (copy > have) {
                copy = have;
              }

              if (copy) {
                if (state.head) {
                  len = state.head.extra_len - state.length;

                  if (!state.head.extra) {
                    // Use untyped array for more convenient processing later
                    state.head.extra = new Uint8Array(state.head.extra_len);
                  }

                  state.head.extra.set(input.subarray(next, // extra field is limited to 65536 bytes
                  // - no need for additional size check
                  next + copy),
                  /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/
                  len); //zmemcpy(state.head.extra + len, next,
                  //        len + copy > state.head.extra_max ?
                  //        state.head.extra_max - len : copy);
                }

                if (state.flags & 0x0200) {
                  state.check = crc32_1(state.check, input, copy, next);
                }

                have -= copy;
                next += copy;
                state.length -= copy;
              }

              if (state.length) {
                break inf_leave;
              }
            }

            state.length = 0;
            state.mode = NAME$1;

          /* falls through */

          case NAME$1:
            if (state.flags & 0x0800) {
              if (have === 0) {
                break inf_leave;
              }

              copy = 0;

              do {
                // TODO: 2 or 1 bytes?
                len = input[next + copy++];
                /* use constant limit because in js we should not preallocate memory */

                if (state.head && len && state.length < 65536
                /*state.head.name_max*/
                ) {
                  state.head.name += String.fromCharCode(len);
                }
              } while (len && copy < have);

              if (state.flags & 0x0200) {
                state.check = crc32_1(state.check, input, copy, next);
              }

              have -= copy;
              next += copy;

              if (len) {
                break inf_leave;
              }
            } else if (state.head) {
              state.head.name = null;
            }

            state.length = 0;
            state.mode = COMMENT;

          /* falls through */

          case COMMENT:
            if (state.flags & 0x1000) {
              if (have === 0) {
                break inf_leave;
              }

              copy = 0;

              do {
                len = input[next + copy++];
                /* use constant limit because in js we should not preallocate memory */

                if (state.head && len && state.length < 65536
                /*state.head.comm_max*/
                ) {
                  state.head.comment += String.fromCharCode(len);
                }
              } while (len && copy < have);

              if (state.flags & 0x0200) {
                state.check = crc32_1(state.check, input, copy, next);
              }

              have -= copy;
              next += copy;

              if (len) {
                break inf_leave;
              }
            } else if (state.head) {
              state.head.comment = null;
            }

            state.mode = HCRC;

          /* falls through */

          case HCRC:
            if (state.flags & 0x0200) {
              //=== NEEDBITS(16); */
              while (bits < 16) {
                if (have === 0) {
                  break inf_leave;
                }

                have--;
                hold += input[next++] << bits;
                bits += 8;
              } //===//


              if (hold !== (state.check & 0xffff)) {
                strm.msg = 'header crc mismatch';
                state.mode = BAD;
                break;
              } //=== INITBITS();


              hold = 0;
              bits = 0; //===//
            }

            if (state.head) {
              state.head.hcrc = state.flags >> 9 & 1;
              state.head.done = true;
            }

            strm.adler = state.check = 0;
            state.mode = TYPE;
            break;

          case DICTID:
            //=== NEEDBITS(32); */
            while (bits < 32) {
              if (have === 0) {
                break inf_leave;
              }

              have--;
              hold += input[next++] << bits;
              bits += 8;
            } //===//


            strm.adler = state.check = zswap32(hold); //=== INITBITS();

            hold = 0;
            bits = 0; //===//

            state.mode = DICT;

          /* falls through */

          case DICT:
            if (state.havedict === 0) {
              //--- RESTORE() ---
              strm.next_out = put;
              strm.avail_out = left;
              strm.next_in = next;
              strm.avail_in = have;
              state.hold = hold;
              state.bits = bits; //---

              return Z_NEED_DICT$1;
            }

            strm.adler = state.check = 1
            /*adler32(0L, Z_NULL, 0)*/
            ;
            state.mode = TYPE;

          /* falls through */

          case TYPE:
            if (flush === Z_BLOCK || flush === Z_TREES) {
              break inf_leave;
            }

          /* falls through */

          case TYPEDO:
            if (state.last) {
              //--- BYTEBITS() ---//
              hold >>>= bits & 7;
              bits -= bits & 7; //---//

              state.mode = CHECK;
              break;
            } //=== NEEDBITS(3); */


            while (bits < 3) {
              if (have === 0) {
                break inf_leave;
              }

              have--;
              hold += input[next++] << bits;
              bits += 8;
            } //===//


            state.last = hold & 0x01
            /*BITS(1)*/
            ; //--- DROPBITS(1) ---//

            hold >>>= 1;
            bits -= 1; //---//

            switch (hold & 0x03
            /*BITS(2)*/
            ) {
              case 0:
                /* stored block */
                //Tracev((stderr, "inflate:     stored block%s\n",
                //        state.last ? " (last)" : ""));
                state.mode = STORED;
                break;

              case 1:
                /* fixed block */
                fixedtables(state); //Tracev((stderr, "inflate:     fixed codes block%s\n",
                //        state.last ? " (last)" : ""));

                state.mode = LEN_;
                /* decode codes */

                if (flush === Z_TREES) {
                  //--- DROPBITS(2) ---//
                  hold >>>= 2;
                  bits -= 2; //---//

                  break inf_leave;
                }

                break;

              case 2:
                /* dynamic block */
                //Tracev((stderr, "inflate:     dynamic codes block%s\n",
                //        state.last ? " (last)" : ""));
                state.mode = TABLE;
                break;

              case 3:
                strm.msg = 'invalid block type';
                state.mode = BAD;
            } //--- DROPBITS(2) ---//


            hold >>>= 2;
            bits -= 2; //---//

            break;

          case STORED:
            //--- BYTEBITS() ---// /* go to byte boundary */
            hold >>>= bits & 7;
            bits -= bits & 7; //---//
            //=== NEEDBITS(32); */

            while (bits < 32) {
              if (have === 0) {
                break inf_leave;
              }

              have--;
              hold += input[next++] << bits;
              bits += 8;
            } //===//


            if ((hold & 0xffff) !== (hold >>> 16 ^ 0xffff)) {
              strm.msg = 'invalid stored block lengths';
              state.mode = BAD;
              break;
            }

            state.length = hold & 0xffff; //Tracev((stderr, "inflate:       stored length %u\n",
            //        state.length));
            //=== INITBITS();

            hold = 0;
            bits = 0; //===//

            state.mode = COPY_;

            if (flush === Z_TREES) {
              break inf_leave;
            }

          /* falls through */

          case COPY_:
            state.mode = COPY;

          /* falls through */

          case COPY:
            copy = state.length;

            if (copy) {
              if (copy > have) {
                copy = have;
              }

              if (copy > left) {
                copy = left;
              }

              if (copy === 0) {
                break inf_leave;
              } //--- zmemcpy(put, next, copy); ---


              output.set(input.subarray(next, next + copy), put); //---//

              have -= copy;
              next += copy;
              left -= copy;
              put += copy;
              state.length -= copy;
              break;
            } //Tracev((stderr, "inflate:       stored end\n"));


            state.mode = TYPE;
            break;

          case TABLE:
            //=== NEEDBITS(14); */
            while (bits < 14) {
              if (have === 0) {
                break inf_leave;
              }

              have--;
              hold += input[next++] << bits;
              bits += 8;
            } //===//


            state.nlen = (hold & 0x1f
            /*BITS(5)*/
            ) + 257; //--- DROPBITS(5) ---//

            hold >>>= 5;
            bits -= 5; //---//

            state.ndist = (hold & 0x1f
            /*BITS(5)*/
            ) + 1; //--- DROPBITS(5) ---//

            hold >>>= 5;
            bits -= 5; //---//

            state.ncode = (hold & 0x0f
            /*BITS(4)*/
            ) + 4; //--- DROPBITS(4) ---//

            hold >>>= 4;
            bits -= 4; //---//
            //#ifndef PKZIP_BUG_WORKAROUND

            if (state.nlen > 286 || state.ndist > 30) {
              strm.msg = 'too many length or distance symbols';
              state.mode = BAD;
              break;
            } //#endif
            //Tracev((stderr, "inflate:       table sizes ok\n"));


            state.have = 0;
            state.mode = LENLENS;

          /* falls through */

          case LENLENS:
            while (state.have < state.ncode) {
              //=== NEEDBITS(3);
              while (bits < 3) {
                if (have === 0) {
                  break inf_leave;
                }

                have--;
                hold += input[next++] << bits;
                bits += 8;
              } //===//


              state.lens[order[state.have++]] = hold & 0x07; //BITS(3);
              //--- DROPBITS(3) ---//

              hold >>>= 3;
              bits -= 3; //---//
            }

            while (state.have < 19) {
              state.lens[order[state.have++]] = 0;
            } // We have separate tables & no pointers. 2 commented lines below not needed.
            //state.next = state.codes;
            //state.lencode = state.next;
            // Switch to use dynamic table


            state.lencode = state.lendyn;
            state.lenbits = 7;
            opts = {
              bits: state.lenbits
            };
            ret = inftrees(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts);
            state.lenbits = opts.bits;

            if (ret) {
              strm.msg = 'invalid code lengths set';
              state.mode = BAD;
              break;
            } //Tracev((stderr, "inflate:       code lengths ok\n"));


            state.have = 0;
            state.mode = CODELENS;

          /* falls through */

          case CODELENS:
            while (state.have < state.nlen + state.ndist) {
              for (;;) {
                here = state.lencode[hold & (1 << state.lenbits) - 1];
                /*BITS(state.lenbits)*/

                here_bits = here >>> 24;
                here_op = here >>> 16 & 0xff;
                here_val = here & 0xffff;

                if (here_bits <= bits) {
                  break;
                } //--- PULLBYTE() ---//


                if (have === 0) {
                  break inf_leave;
                }

                have--;
                hold += input[next++] << bits;
                bits += 8; //---//
              }

              if (here_val < 16) {
                //--- DROPBITS(here.bits) ---//
                hold >>>= here_bits;
                bits -= here_bits; //---//

                state.lens[state.have++] = here_val;
              } else {
                if (here_val === 16) {
                  //=== NEEDBITS(here.bits + 2);
                  n = here_bits + 2;

                  while (bits < n) {
                    if (have === 0) {
                      break inf_leave;
                    }

                    have--;
                    hold += input[next++] << bits;
                    bits += 8;
                  } //===//
                  //--- DROPBITS(here.bits) ---//


                  hold >>>= here_bits;
                  bits -= here_bits; //---//

                  if (state.have === 0) {
                    strm.msg = 'invalid bit length repeat';
                    state.mode = BAD;
                    break;
                  }

                  len = state.lens[state.have - 1];
                  copy = 3 + (hold & 0x03); //BITS(2);
                  //--- DROPBITS(2) ---//

                  hold >>>= 2;
                  bits -= 2; //---//
                } else if (here_val === 17) {
                  //=== NEEDBITS(here.bits + 3);
                  n = here_bits + 3;

                  while (bits < n) {
                    if (have === 0) {
                      break inf_leave;
                    }

                    have--;
                    hold += input[next++] << bits;
                    bits += 8;
                  } //===//
                  //--- DROPBITS(here.bits) ---//


                  hold >>>= here_bits;
                  bits -= here_bits; //---//

                  len = 0;
                  copy = 3 + (hold & 0x07); //BITS(3);
                  //--- DROPBITS(3) ---//

                  hold >>>= 3;
                  bits -= 3; //---//
                } else {
                  //=== NEEDBITS(here.bits + 7);
                  n = here_bits + 7;

                  while (bits < n) {
                    if (have === 0) {
                      break inf_leave;
                    }

                    have--;
                    hold += input[next++] << bits;
                    bits += 8;
                  } //===//
                  //--- DROPBITS(here.bits) ---//


                  hold >>>= here_bits;
                  bits -= here_bits; //---//

                  len = 0;
                  copy = 11 + (hold & 0x7f); //BITS(7);
                  //--- DROPBITS(7) ---//

                  hold >>>= 7;
                  bits -= 7; //---//
                }

                if (state.have + copy > state.nlen + state.ndist) {
                  strm.msg = 'invalid bit length repeat';
                  state.mode = BAD;
                  break;
                }

                while (copy--) {
                  state.lens[state.have++] = len;
                }
              }
            }
            /* handle error breaks in while */


            if (state.mode === BAD) {
              break;
            }
            /* check for end-of-block code (better have one) */


            if (state.lens[256] === 0) {
              strm.msg = 'invalid code -- missing end-of-block';
              state.mode = BAD;
              break;
            }
            /* build code tables -- note: do not change the lenbits or distbits
               values here (9 and 6) without reading the comments in inftrees.h
               concerning the ENOUGH constants, which depend on those values */


            state.lenbits = 9;
            opts = {
              bits: state.lenbits
            };
            ret = inftrees(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); // We have separate tables & no pointers. 2 commented lines below not needed.
            // state.next_index = opts.table_index;

            state.lenbits = opts.bits; // state.lencode = state.next;

            if (ret) {
              strm.msg = 'invalid literal/lengths set';
              state.mode = BAD;
              break;
            }

            state.distbits = 6; //state.distcode.copy(state.codes);
            // Switch to use dynamic table

            state.distcode = state.distdyn;
            opts = {
              bits: state.distbits
            };
            ret = inftrees(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); // We have separate tables & no pointers. 2 commented lines below not needed.
            // state.next_index = opts.table_index;

            state.distbits = opts.bits; // state.distcode = state.next;

            if (ret) {
              strm.msg = 'invalid distances set';
              state.mode = BAD;
              break;
            } //Tracev((stderr, 'inflate:       codes ok\n'));


            state.mode = LEN_;

            if (flush === Z_TREES) {
              break inf_leave;
            }

          /* falls through */

          case LEN_:
            state.mode = LEN;

          /* falls through */

          case LEN:
            if (have >= 6 && left >= 258) {
              //--- RESTORE() ---
              strm.next_out = put;
              strm.avail_out = left;
              strm.next_in = next;
              strm.avail_in = have;
              state.hold = hold;
              state.bits = bits; //---

              inffast(strm, _out); //--- LOAD() ---

              put = strm.next_out;
              output = strm.output;
              left = strm.avail_out;
              next = strm.next_in;
              input = strm.input;
              have = strm.avail_in;
              hold = state.hold;
              bits = state.bits; //---

              if (state.mode === TYPE) {
                state.back = -1;
              }

              break;
            }

            state.back = 0;

            for (;;) {
              here = state.lencode[hold & (1 << state.lenbits) - 1];
              /*BITS(state.lenbits)*/

              here_bits = here >>> 24;
              here_op = here >>> 16 & 0xff;
              here_val = here & 0xffff;

              if (here_bits <= bits) {
                break;
              } //--- PULLBYTE() ---//


              if (have === 0) {
                break inf_leave;
              }

              have--;
              hold += input[next++] << bits;
              bits += 8; //---//
            }

            if (here_op && (here_op & 0xf0) === 0) {
              last_bits = here_bits;
              last_op = here_op;
              last_val = here_val;

              for (;;) {
                here = state.lencode[last_val + ((hold & (1 << last_bits + last_op) - 1
                /*BITS(last.bits + last.op)*/
                ) >> last_bits)];
                here_bits = here >>> 24;
                here_op = here >>> 16 & 0xff;
                here_val = here & 0xffff;

                if (last_bits + here_bits <= bits) {
                  break;
                } //--- PULLBYTE() ---//


                if (have === 0) {
                  break inf_leave;
                }

                have--;
                hold += input[next++] << bits;
                bits += 8; //---//
              } //--- DROPBITS(last.bits) ---//


              hold >>>= last_bits;
              bits -= last_bits; //---//

              state.back += last_bits;
            } //--- DROPBITS(here.bits) ---//


            hold >>>= here_bits;
            bits -= here_bits; //---//

            state.back += here_bits;
            state.length = here_val;

            if (here_op === 0) {
              //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
              //        "inflate:         literal '%c'\n" :
              //        "inflate:         literal 0x%02x\n", here.val));
              state.mode = LIT;
              break;
            }

            if (here_op & 32) {
              //Tracevv((stderr, "inflate:         end of block\n"));
              state.back = -1;
              state.mode = TYPE;
              break;
            }

            if (here_op & 64) {
              strm.msg = 'invalid literal/length code';
              state.mode = BAD;
              break;
            }

            state.extra = here_op & 15;
            state.mode = LENEXT;

          /* falls through */

          case LENEXT:
            if (state.extra) {
              //=== NEEDBITS(state.extra);
              n = state.extra;

              while (bits < n) {
                if (have === 0) {
                  break inf_leave;
                }

                have--;
                hold += input[next++] << bits;
                bits += 8;
              } //===//


              state.length += hold & (1 << state.extra) - 1
              /*BITS(state.extra)*/
              ; //--- DROPBITS(state.extra) ---//

              hold >>>= state.extra;
              bits -= state.extra; //---//

              state.back += state.extra;
            } //Tracevv((stderr, "inflate:         length %u\n", state.length));


            state.was = state.length;
            state.mode = DIST;

          /* falls through */

          case DIST:
            for (;;) {
              here = state.distcode[hold & (1 << state.distbits) - 1];
              /*BITS(state.distbits)*/

              here_bits = here >>> 24;
              here_op = here >>> 16 & 0xff;
              here_val = here & 0xffff;

              if (here_bits <= bits) {
                break;
              } //--- PULLBYTE() ---//


              if (have === 0) {
                break inf_leave;
              }

              have--;
              hold += input[next++] << bits;
              bits += 8; //---//
            }

            if ((here_op & 0xf0) === 0) {
              last_bits = here_bits;
              last_op = here_op;
              last_val = here_val;

              for (;;) {
                here = state.distcode[last_val + ((hold & (1 << last_bits + last_op) - 1
                /*BITS(last.bits + last.op)*/
                ) >> last_bits)];
                here_bits = here >>> 24;
                here_op = here >>> 16 & 0xff;
                here_val = here & 0xffff;

                if (last_bits + here_bits <= bits) {
                  break;
                } //--- PULLBYTE() ---//


                if (have === 0) {
                  break inf_leave;
                }

                have--;
                hold += input[next++] << bits;
                bits += 8; //---//
              } //--- DROPBITS(last.bits) ---//


              hold >>>= last_bits;
              bits -= last_bits; //---//

              state.back += last_bits;
            } //--- DROPBITS(here.bits) ---//


            hold >>>= here_bits;
            bits -= here_bits; //---//

            state.back += here_bits;

            if (here_op & 64) {
              strm.msg = 'invalid distance code';
              state.mode = BAD;
              break;
            }

            state.offset = here_val;
            state.extra = here_op & 15;
            state.mode = DISTEXT;

          /* falls through */

          case DISTEXT:
            if (state.extra) {
              //=== NEEDBITS(state.extra);
              n = state.extra;

              while (bits < n) {
                if (have === 0) {
                  break inf_leave;
                }

                have--;
                hold += input[next++] << bits;
                bits += 8;
              } //===//


              state.offset += hold & (1 << state.extra) - 1
              /*BITS(state.extra)*/
              ; //--- DROPBITS(state.extra) ---//

              hold >>>= state.extra;
              bits -= state.extra; //---//

              state.back += state.extra;
            } //#ifdef INFLATE_STRICT


            if (state.offset > state.dmax) {
              strm.msg = 'invalid distance too far back';
              state.mode = BAD;
              break;
            } //#endif
            //Tracevv((stderr, "inflate:         distance %u\n", state.offset));


            state.mode = MATCH;

          /* falls through */

          case MATCH:
            if (left === 0) {
              break inf_leave;
            }

            copy = _out - left;

            if (state.offset > copy) {
              /* copy from window */
              copy = state.offset - copy;

              if (copy > state.whave) {
                if (state.sane) {
                  strm.msg = 'invalid distance too far back';
                  state.mode = BAD;
                  break;
                } // (!) This block is disabled in zlib defaults,
                // don't enable it for binary compatibility
                //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
                //          Trace((stderr, "inflate.c too far\n"));
                //          copy -= state.whave;
                //          if (copy > state.length) { copy = state.length; }
                //          if (copy > left) { copy = left; }
                //          left -= copy;
                //          state.length -= copy;
                //          do {
                //            output[put++] = 0;
                //          } while (--copy);
                //          if (state.length === 0) { state.mode = LEN; }
                //          break;
                //#endif

              }

              if (copy > state.wnext) {
                copy -= state.wnext;
                from = state.wsize - copy;
              } else {
                from = state.wnext - copy;
              }

              if (copy > state.length) {
                copy = state.length;
              }

              from_source = state.window;
            } else {
              /* copy from output */
              from_source = output;
              from = put - state.offset;
              copy = state.length;
            }

            if (copy > left) {
              copy = left;
            }

            left -= copy;
            state.length -= copy;

            do {
              output[put++] = from_source[from++];
            } while (--copy);

            if (state.length === 0) {
              state.mode = LEN;
            }

            break;

          case LIT:
            if (left === 0) {
              break inf_leave;
            }

            output[put++] = state.length;
            left--;
            state.mode = LEN;
            break;

          case CHECK:
            if (state.wrap) {
              //=== NEEDBITS(32);
              while (bits < 32) {
                if (have === 0) {
                  break inf_leave;
                }

                have--; // Use '|' instead of '+' to make sure that result is signed

                hold |= input[next++] << bits;
                bits += 8;
              } //===//


              _out -= left;
              strm.total_out += _out;
              state.total += _out;

              if (_out) {
                strm.adler = state.check =
                /*UPDATE(state.check, put - _out, _out);*/
                state.flags ? crc32_1(state.check, output, _out, put - _out) : adler32_1(state.check, output, _out, put - _out);
              }

              _out = left; // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too

              if ((state.flags ? hold : zswap32(hold)) !== state.check) {
                strm.msg = 'incorrect data check';
                state.mode = BAD;
                break;
              } //=== INITBITS();


              hold = 0;
              bits = 0; //===//
              //Tracev((stderr, "inflate:   check matches trailer\n"));
            }

            state.mode = LENGTH;

          /* falls through */

          case LENGTH:
            if (state.wrap && state.flags) {
              //=== NEEDBITS(32);
              while (bits < 32) {
                if (have === 0) {
                  break inf_leave;
                }

                have--;
                hold += input[next++] << bits;
                bits += 8;
              } //===//


              if (hold !== (state.total & 0xffffffff)) {
                strm.msg = 'incorrect length check';
                state.mode = BAD;
                break;
              } //=== INITBITS();


              hold = 0;
              bits = 0; //===//
              //Tracev((stderr, "inflate:   length matches trailer\n"));
            }

            state.mode = DONE;

          /* falls through */

          case DONE:
            ret = Z_STREAM_END$1;
            break inf_leave;

          case BAD:
            ret = Z_DATA_ERROR$1;
            break inf_leave;

          case MEM:
            return Z_MEM_ERROR$1;

          case SYNC:
          /* falls through */

          default:
            return Z_STREAM_ERROR$1;
        }
      } // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave"

      /*
         Return from inflate(), updating the total counts and the check value.
         If there was no progress during the inflate() call, return a buffer
         error.  Call updatewindow() to create and/or update the window state.
         Note: a memory error from inflate() is non-recoverable.
       */
      //--- RESTORE() ---


      strm.next_out = put;
      strm.avail_out = left;
      strm.next_in = next;
      strm.avail_in = have;
      state.hold = hold;
      state.bits = bits; //---

      if (state.wsize || _out !== strm.avail_out && state.mode < BAD && (state.mode < CHECK || flush !== Z_FINISH$1)) {
        if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) ;
      }

      _in -= strm.avail_in;
      _out -= strm.avail_out;
      strm.total_in += _in;
      strm.total_out += _out;
      state.total += _out;

      if (state.wrap && _out) {
        strm.adler = state.check =
        /*UPDATE(state.check, strm.next_out - _out, _out);*/
        state.flags ? crc32_1(state.check, output, _out, strm.next_out - _out) : adler32_1(state.check, output, _out, strm.next_out - _out);
      }

      strm.data_type = state.bits + (state.last ? 64 : 0) + (state.mode === TYPE ? 128 : 0) + (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0);

      if ((_in === 0 && _out === 0 || flush === Z_FINISH$1) && ret === Z_OK$1) {
        ret = Z_BUF_ERROR;
      }

      return ret;
    };

    const inflateEnd = strm => {
      if (!strm || !strm.state
      /*|| strm->zfree == (free_func)0*/
      ) {
        return Z_STREAM_ERROR$1;
      }

      let state = strm.state;

      if (state.window) {
        state.window = null;
      }

      strm.state = null;
      return Z_OK$1;
    };

    const inflateGetHeader = (strm, head) => {
      /* check state */
      if (!strm || !strm.state) {
        return Z_STREAM_ERROR$1;
      }

      const state = strm.state;

      if ((state.wrap & 2) === 0) {
        return Z_STREAM_ERROR$1;
      }
      /* save header structure */


      state.head = head;
      head.done = false;
      return Z_OK$1;
    };

    const inflateSetDictionary = (strm, dictionary) => {
      const dictLength = dictionary.length;
      let state;
      let dictid;
      let ret;
      /* check state */

      if (!strm
      /* == Z_NULL */
      || !strm.state
      /* == Z_NULL */
      ) {
        return Z_STREAM_ERROR$1;
      }

      state = strm.state;

      if (state.wrap !== 0 && state.mode !== DICT) {
        return Z_STREAM_ERROR$1;
      }
      /* check for correct dictionary identifier */


      if (state.mode === DICT) {
        dictid = 1;
        /* adler32(0, null, 0)*/

        /* dictid = adler32(dictid, dictionary, dictLength); */

        dictid = adler32_1(dictid, dictionary, dictLength, 0);

        if (dictid !== state.check) {
          return Z_DATA_ERROR$1;
        }
      }
      /* copy dictionary to window using updatewindow(), which will amend the
       existing dictionary if appropriate */


      ret = updatewindow(strm, dictionary, dictLength, dictLength);

      if (ret) {
        state.mode = MEM;
        return Z_MEM_ERROR$1;
      }

      state.havedict = 1; // Tracev((stderr, "inflate:   dictionary set\n"));

      return Z_OK$1;
    };

    var inflateReset_1 = inflateReset;
    var inflateReset2_1 = inflateReset2;
    var inflateResetKeep_1 = inflateResetKeep;
    var inflateInit_1 = inflateInit;
    var inflateInit2_1 = inflateInit2;
    var inflate_2$1 = inflate$2;
    var inflateEnd_1 = inflateEnd;
    var inflateGetHeader_1 = inflateGetHeader;
    var inflateSetDictionary_1 = inflateSetDictionary;
    var inflateInfo = 'pako inflate (from Nodeca project)';
    /* Not implemented
    module.exports.inflateCopy = inflateCopy;
    module.exports.inflateGetDictionary = inflateGetDictionary;
    module.exports.inflateMark = inflateMark;
    module.exports.inflatePrime = inflatePrime;
    module.exports.inflateSync = inflateSync;
    module.exports.inflateSyncPoint = inflateSyncPoint;
    module.exports.inflateUndermine = inflateUndermine;
    */

    var inflate_1$2 = {
      inflateReset: inflateReset_1,
      inflateReset2: inflateReset2_1,
      inflateResetKeep: inflateResetKeep_1,
      inflateInit: inflateInit_1,
      inflateInit2: inflateInit2_1,
      inflate: inflate_2$1,
      inflateEnd: inflateEnd_1,
      inflateGetHeader: inflateGetHeader_1,
      inflateSetDictionary: inflateSetDictionary_1,
      inflateInfo: inflateInfo
    }; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    function GZheader() {
      /* true if compressed data believed to be text */
      this.text = 0;
      /* modification time */

      this.time = 0;
      /* extra flags (not used when writing a gzip file) */

      this.xflags = 0;
      /* operating system */

      this.os = 0;
      /* pointer to extra field or Z_NULL if none */

      this.extra = null;
      /* extra field length (valid if extra != Z_NULL) */

      this.extra_len = 0; // Actually, we don't need it in JS,
      // but leave for few code modifications
      //
      // Setup limits is not necessary because in js we should not preallocate memory
      // for inflate use constant limit in 65536 bytes
      //

      /* space at extra (only when reading header) */
      // this.extra_max  = 0;

      /* pointer to zero-terminated file name or Z_NULL */

      this.name = '';
      /* space at name (only when reading header) */
      // this.name_max   = 0;

      /* pointer to zero-terminated comment or Z_NULL */

      this.comment = '';
      /* space at comment (only when reading header) */
      // this.comm_max   = 0;

      /* true if there was or will be a header crc */

      this.hcrc = 0;
      /* true when done reading gzip header (not used when writing a gzip file) */

      this.done = false;
    }

    var gzheader = GZheader;
    const toString$2 = Object.prototype.toString;
    /* Public constants ==========================================================*/

    /* ===========================================================================*/

    const {
      Z_NO_FLUSH,
      Z_FINISH,
      Z_OK,
      Z_STREAM_END,
      Z_NEED_DICT,
      Z_STREAM_ERROR,
      Z_DATA_ERROR,
      Z_MEM_ERROR
    } = constants$2;
    /* ===========================================================================*/

    /**
     * class Inflate
     *
     * Generic JS-style wrapper for zlib calls. If you don't need
     * streaming behaviour - use more simple functions: [[inflate]]
     * and [[inflateRaw]].
     **/

    /* internal
     * inflate.chunks -> Array
     *
     * Chunks of output data, if [[Inflate#onData]] not overridden.
     **/

    /**
     * Inflate.result -> Uint8Array|String
     *
     * Uncompressed result, generated by default [[Inflate#onData]]
     * and [[Inflate#onEnd]] handlers. Filled after you push last chunk
     * (call [[Inflate#push]] with `Z_FINISH` / `true` param).
     **/

    /**
     * Inflate.err -> Number
     *
     * Error code after inflate finished. 0 (Z_OK) on success.
     * Should be checked if broken data possible.
     **/

    /**
     * Inflate.msg -> String
     *
     * Error message, if [[Inflate.err]] != 0
     **/

    /**
     * new Inflate(options)
     * - options (Object): zlib inflate options.
     *
     * Creates new inflator instance with specified params. Throws exception
     * on bad params. Supported options:
     *
     * - `windowBits`
     * - `dictionary`
     *
     * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
     * for more information on these.
     *
     * Additional options, for internal needs:
     *
     * - `chunkSize` - size of generated data chunks (16K by default)
     * - `raw` (Boolean) - do raw inflate
     * - `to` (String) - if equal to 'string', then result will be converted
     *   from utf8 to utf16 (javascript) string. When string output requested,
     *   chunk length can differ from `chunkSize`, depending on content.
     *
     * By default, when no options set, autodetect deflate/gzip data format via
     * wrapper header.
     *
     * ##### Example:
     *
     * ```javascript
     * const pako = require('pako')
     * const chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9])
     * const chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]);
     *
     * const inflate = new pako.Inflate({ level: 3});
     *
     * inflate.push(chunk1, false);
     * inflate.push(chunk2, true);  // true -> last chunk
     *
     * if (inflate.err) { throw new Error(inflate.err); }
     *
     * console.log(inflate.result);
     * ```
     **/

    function Inflate$1(options) {
      this.options = common.assign({
        chunkSize: 1024 * 64,
        windowBits: 15,
        to: ''
      }, options || {});
      const opt = this.options; // Force window size for `raw` data, if not set directly,
      // because we have no header for autodetect.

      if (opt.raw && opt.windowBits >= 0 && opt.windowBits < 16) {
        opt.windowBits = -opt.windowBits;

        if (opt.windowBits === 0) {
          opt.windowBits = -15;
        }
      } // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate


      if (opt.windowBits >= 0 && opt.windowBits < 16 && !(options && options.windowBits)) {
        opt.windowBits += 32;
      } // Gzip header has no info about windows size, we can do autodetect only
      // for deflate. So, if window size not set, force it to max when gzip possible


      if (opt.windowBits > 15 && opt.windowBits < 48) {
        // bit 3 (16) -> gzipped data
        // bit 4 (32) -> autodetect gzip/deflate
        if ((opt.windowBits & 15) === 0) {
          opt.windowBits |= 15;
        }
      }

      this.err = 0; // error code, if happens (0 = Z_OK)

      this.msg = ''; // error message

      this.ended = false; // used to avoid multiple onEnd() calls

      this.chunks = []; // chunks of compressed data

      this.strm = new zstream();
      this.strm.avail_out = 0;
      let status = inflate_1$2.inflateInit2(this.strm, opt.windowBits);

      if (status !== Z_OK) {
        throw new Error(messages[status]);
      }

      this.header = new gzheader();
      inflate_1$2.inflateGetHeader(this.strm, this.header); // Setup dictionary

      if (opt.dictionary) {
        // Convert data if needed
        if (typeof opt.dictionary === 'string') {
          opt.dictionary = strings.string2buf(opt.dictionary);
        } else if (toString$2.call(opt.dictionary) === '[object ArrayBuffer]') {
          opt.dictionary = new Uint8Array(opt.dictionary);
        }

        if (opt.raw) {
          //In raw mode we need to set the dictionary early
          status = inflate_1$2.inflateSetDictionary(this.strm, opt.dictionary);

          if (status !== Z_OK) {
            throw new Error(messages[status]);
          }
        }
      }
    }
    /**
     * Inflate#push(data[, flush_mode]) -> Boolean
     * - data (Uint8Array|ArrayBuffer): input data
     * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE
     *   flush modes. See constants. Skipped or `false` means Z_NO_FLUSH,
     *   `true` means Z_FINISH.
     *
     * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with
     * new output chunks. Returns `true` on success. If end of stream detected,
     * [[Inflate#onEnd]] will be called.
     *
     * `flush_mode` is not needed for normal operation, because end of stream
     * detected automatically. You may try to use it for advanced things, but
     * this functionality was not tested.
     *
     * On fail call [[Inflate#onEnd]] with error code and return false.
     *
     * ##### Example
     *
     * ```javascript
     * push(chunk, false); // push one of data chunks
     * ...
     * push(chunk, true);  // push last chunk
     * ```
     **/


    Inflate$1.prototype.push = function (data, flush_mode) {
      const strm = this.strm;
      const chunkSize = this.options.chunkSize;
      const dictionary = this.options.dictionary;

      let status, _flush_mode, last_avail_out;

      if (this.ended) return false;
      if (flush_mode === ~~flush_mode) _flush_mode = flush_mode;else _flush_mode = flush_mode === true ? Z_FINISH : Z_NO_FLUSH; // Convert data if needed

      if (toString$2.call(data) === '[object ArrayBuffer]') {
        strm.input = new Uint8Array(data);
      } else {
        strm.input = data;
      }

      strm.next_in = 0;
      strm.avail_in = strm.input.length;

      for (;;) {
        if (strm.avail_out === 0) {
          strm.output = new Uint8Array(chunkSize);
          strm.next_out = 0;
          strm.avail_out = chunkSize;
        }

        status = inflate_1$2.inflate(strm, _flush_mode);

        if (status === Z_NEED_DICT && dictionary) {
          status = inflate_1$2.inflateSetDictionary(strm, dictionary);

          if (status === Z_OK) {
            status = inflate_1$2.inflate(strm, _flush_mode);
          } else if (status === Z_DATA_ERROR) {
            // Replace code with more verbose
            status = Z_NEED_DICT;
          }
        } // Skip snyc markers if more data follows and not raw mode


        while (strm.avail_in > 0 && status === Z_STREAM_END && strm.state.wrap > 0 && data[strm.next_in] !== 0) {
          inflate_1$2.inflateReset(strm);
          status = inflate_1$2.inflate(strm, _flush_mode);
        }

        switch (status) {
          case Z_STREAM_ERROR:
          case Z_DATA_ERROR:
          case Z_NEED_DICT:
          case Z_MEM_ERROR:
            this.onEnd(status);
            this.ended = true;
            return false;
        } // Remember real `avail_out` value, because we may patch out buffer content
        // to align utf8 strings boundaries.


        last_avail_out = strm.avail_out;

        if (strm.next_out) {
          if (strm.avail_out === 0 || status === Z_STREAM_END) {
            if (this.options.to === 'string') {
              let next_out_utf8 = strings.utf8border(strm.output, strm.next_out);
              let tail = strm.next_out - next_out_utf8;
              let utf8str = strings.buf2string(strm.output, next_out_utf8); // move tail & realign counters

              strm.next_out = tail;
              strm.avail_out = chunkSize - tail;
              if (tail) strm.output.set(strm.output.subarray(next_out_utf8, next_out_utf8 + tail), 0);
              this.onData(utf8str);
            } else {
              this.onData(strm.output.length === strm.next_out ? strm.output : strm.output.subarray(0, strm.next_out));
            }
          }
        } // Must repeat iteration if out buffer is full


        if (status === Z_OK && last_avail_out === 0) continue; // Finalize if end of stream reached.

        if (status === Z_STREAM_END) {
          status = inflate_1$2.inflateEnd(this.strm);
          this.onEnd(status);
          this.ended = true;
          return true;
        }

        if (strm.avail_in === 0) break;
      }

      return true;
    };
    /**
     * Inflate#onData(chunk) -> Void
     * - chunk (Uint8Array|String): output data. When string output requested,
     *   each chunk will be string.
     *
     * By default, stores data blocks in `chunks[]` property and glue
     * those in `onEnd`. Override this handler, if you need another behaviour.
     **/


    Inflate$1.prototype.onData = function (chunk) {
      this.chunks.push(chunk);
    };
    /**
     * Inflate#onEnd(status) -> Void
     * - status (Number): inflate status. 0 (Z_OK) on success,
     *   other if not.
     *
     * Called either after you tell inflate that the input stream is
     * complete (Z_FINISH). By default - join collected chunks,
     * free memory and fill `results` / `err` properties.
     **/


    Inflate$1.prototype.onEnd = function (status) {
      // On success - join
      if (status === Z_OK) {
        if (this.options.to === 'string') {
          this.result = this.chunks.join('');
        } else {
          this.result = common.flattenChunks(this.chunks);
        }
      }

      this.chunks = [];
      this.err = status;
      this.msg = this.strm.msg;
    };
    /**
     * inflate(data[, options]) -> Uint8Array|String
     * - data (Uint8Array): input data to decompress.
     * - options (Object): zlib inflate options.
     *
     * Decompress `data` with inflate/ungzip and `options`. Autodetect
     * format via wrapper header by default. That's why we don't provide
     * separate `ungzip` method.
     *
     * Supported options are:
     *
     * - windowBits
     *
     * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
     * for more information.
     *
     * Sugar (options):
     *
     * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
     *   negative windowBits implicitly.
     * - `to` (String) - if equal to 'string', then result will be converted
     *   from utf8 to utf16 (javascript) string. When string output requested,
     *   chunk length can differ from `chunkSize`, depending on content.
     *
     *
     * ##### Example:
     *
     * ```javascript
     * const pako = require('pako');
     * const input = pako.deflate(new Uint8Array([1,2,3,4,5,6,7,8,9]));
     * let output;
     *
     * try {
     *   output = pako.inflate(input);
     * } catch (err) {
     *   console.log(err);
     * }
     * ```
     **/


    function inflate$1(input, options) {
      const inflator = new Inflate$1(options);
      inflator.push(input); // That will never happens, if you don't cheat with options :)

      if (inflator.err) throw inflator.msg || messages[inflator.err];
      return inflator.result;
    }
    /**
     * inflateRaw(data[, options]) -> Uint8Array|String
     * - data (Uint8Array): input data to decompress.
     * - options (Object): zlib inflate options.
     *
     * The same as [[inflate]], but creates raw data, without wrapper
     * (header and adler32 crc).
     **/


    function inflateRaw$1(input, options) {
      options = options || {};
      options.raw = true;
      return inflate$1(input, options);
    }
    /**
     * ungzip(data[, options]) -> Uint8Array|String
     * - data (Uint8Array): input data to decompress.
     * - options (Object): zlib inflate options.
     *
     * Just shortcut to [[inflate]], because it autodetects format
     * by header.content. Done for convenience.
     **/


    var Inflate_1$1 = Inflate$1;
    var inflate_2 = inflate$1;
    var inflateRaw_1$1 = inflateRaw$1;
    var ungzip$1 = inflate$1;
    var constants = constants$2;
    var inflate_1$1 = {
      Inflate: Inflate_1$1,
      inflate: inflate_2,
      inflateRaw: inflateRaw_1$1,
      ungzip: ungzip$1,
      constants: constants
    };
    const {
      Deflate,
      deflate,
      deflateRaw,
      gzip
    } = deflate_1$1;
    const {
      Inflate,
      inflate: inflate$3,
      inflateRaw,
      ungzip: ungzip$2
    } = inflate_1$1;
    var Deflate_1 = Deflate;
    var deflate_1 = deflate;
    var deflateRaw_1 = deflateRaw;
    var gzip_1 = gzip;
    var Inflate_1 = Inflate;
    var inflate_1 = inflate$3;
    var inflateRaw_1 = inflateRaw;
    var ungzip_1 = ungzip$2;
    var constants_1 = constants$2;
    var pako = {
      Deflate: Deflate_1,
      deflate: deflate_1,
      deflateRaw: deflateRaw_1,
      gzip: gzip_1,
      Inflate: Inflate_1,
      inflate: inflate_1,
      inflateRaw: inflateRaw_1,
      ungzip: ungzip_1,
      constants: constants_1
    }; // Added by JTR
    // exports.Deflate = Deflate_1;
    // exports.Inflate = Inflate_1;
    // exports.constants = constants_1;
    // exports['default'] = pako;
    // exports.deflate = deflate_1;
    // exports.deflateRaw = deflateRaw_1;
    // exports.gzip = gzip_1;
    // exports.inflate = inflate_1;
    // exports.inflateRaw = inflateRaw_1;
    // exports.ungzip = ungzip_1;
    //   Object.defineProperty(exports, '__esModule', { value: true });
    //
    // })));

    pako.deflateRaw;
    pako.deflate;
    pako.inflateRaw;
    const inflate = pako.inflate;
    pako.gzip;
    const FEXTRA = 4; // gzip spec F.EXTRA flag

    function isgzipped(data) {
      const b = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
      return b[0] === 31 && b[1] === 139;
    }
    /**
     * Pako does not properly ungzip block compressed files if > 1 block is present.  Test for bgzip and use wrapper.
     */


    function ungzip(data) {
      const ba = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
      const b = ba[3] & FEXTRA;

      if (b !== 0 && ba[12] === 66 && ba[13] === 67) {
        return unbgzf(ba.buffer);
      } else {
        return pako.ungzip(ba);
      }
    } // Uncompress data,  assumed to be series of bgzipped blocks


    function unbgzf(data, lim) {
      const oBlockList = [];
      let ptr = 0;
      let totalSize = 0;
      lim = lim || data.byteLength - 18;

      while (ptr < lim) {
        try {
          const ba = ArrayBuffer.isView(data) ? data : new Uint8Array(data, ptr, 18);
          const xlen = ba[11] << 8 | ba[10];
          const flg = ba[3];
          const fextra = flg & FEXTRA;
          const si1 = ba[12];
          const si2 = ba[13];
          const slen = ba[15] << 8 | ba[14];
          const bsize = (ba[17] << 8 | ba[16]) + 1;
          const start = 12 + xlen + ptr; // Start of CDATA

          const bytesLeft = data.byteLength - start;
          const cDataSize = bsize - xlen - 19;
          if (bytesLeft < cDataSize || cDataSize <= 0) break;
          const a = new Uint8Array(data, start, cDataSize);
          const unc = pako.inflateRaw(a); // const inflate = new Zlib.RawInflate(a);
          // const unc = inflate.decompress();

          ptr += cDataSize - 1 + 26; //inflate.ip + 26

          totalSize += unc.byteLength;
          oBlockList.push(unc);
        } catch (e) {
          console.error(e);
          break;
        }
      } // Concatenate decompressed blocks


      if (oBlockList.length === 1) {
        return oBlockList[0];
      } else {
        const out = new Uint8Array(totalSize);
        let cursor = 0;

        for (let i = 0; i < oBlockList.length; ++i) {
          var b = new Uint8Array(oBlockList[i]);
          arrayCopy(b, 0, out, cursor, b.length);
          cursor += b.length;
        }

        return out;
      }
    }

    function bgzBlockSize(data) {
      const ba = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
      const bsize = (ba[17] << 8 | ba[16]) + 1;
      return bsize;
    } // From Thomas Down's zlib implementation


    const testArray = new Uint8Array(1);
    const hasSubarray = typeof testArray.subarray === 'function';
    /* (typeof testArray.slice === 'function'); */
    // Chrome slice performance is so dire that we're currently not using it...

    function arrayCopy(src, srcOffset, dest, destOffset, count) {
      if (count === 0) {
        return;
      }

      if (!src) {
        throw "Undef src";
      } else if (!dest) {
        throw "Undef dest";
      }

      if (srcOffset === 0 && count === src.length) {
        arrayCopy_fast(src, dest, destOffset);
      } else if (hasSubarray) {
        arrayCopy_fast(src.subarray(srcOffset, srcOffset + count), dest, destOffset);
      } else if (src.BYTES_PER_ELEMENT === 1 && count > 100) {
        arrayCopy_fast(new Uint8Array(src.buffer, src.byteOffset + srcOffset, count), dest, destOffset);
      } else {
        arrayCopy_slow(src, srcOffset, dest, destOffset, count);
      }
    }

    function arrayCopy_slow(src, srcOffset, dest, destOffset, count) {
      for (let i = 0; i < count; ++i) {
        dest[destOffset + i] = src[srcOffset + i];
      }
    }

    function arrayCopy_fast(src, dest, destOffset) {
      dest.set(src, destOffset);
    }
    /**
     * Compress string and encode in a url safe form
     * @param s
     */


    function compressString(str) {
      const bytes = new Uint8Array(str.length);

      for (var i = 0; i < str.length; i++) {
        bytes[i] = str.charCodeAt(i);
      }

      const compressedBytes = new pako.deflateRaw(bytes); // UInt8Arry

      const compressedString = String.fromCharCode.apply(null, compressedBytes); // Convert to string

      let enc = btoa(compressedString);
      return enc.replace(/\+/g, '.').replace(/\//g, '_').replace(/=/g, '-'); // URL safe
    }
    /**
     * Uncompress the url-safe encoded compressed string, presumably created by compressString above
     *
     * @param enc
     * @returns {string}
     */


    function uncompressString(enc) {
      enc = enc.replace(/\./g, '+').replace(/_/g, '/').replace(/-/g, '=');
      const compressedString = atob(enc);
      const compressedBytes = [];

      for (let i = 0; i < compressedString.length; i++) {
        compressedBytes.push(compressedString.charCodeAt(i));
      } //const bytes = new Zlib.RawInflate(compressedBytes).decompress();


      const bytes = pako.inflateRaw(compressedBytes);
      let str = '';

      for (let b of bytes) {
        str += String.fromCharCode(b);
      }

      return str;
    }
    /**
     * @param dataURI
     * @returns {Array<number>|Uint8Array}
     */


    function decodeDataURI$1(dataURI, gzip) {
      const split = dataURI.split(',');
      const info = split[0].split(':')[1];
      let dataString = split[1];

      if (info.indexOf('base64') >= 0) {
        dataString = atob(dataString);
        const bytes = new Uint8Array(dataString.length);

        for (let i = 0; i < dataString.length; i++) {
          bytes[i] = dataString.charCodeAt(i);
        }

        let plain;

        if (gzip || info.indexOf('gzip') > 0) {
          plain = pako.ungzip(bytes);
        } else {
          plain = bytes;
        }

        return plain;
      } else {
        return decodeURIComponent(dataString); // URL encoded string -- not currently used or tested
      }
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const IGVMath = {
      lerp: (v0, v1, t) => {
        return (1 - t) * v0 + t * v1;
      },
      mean: function (array) {
        var t = 0,
            n = 0,
            i;

        for (i = 0; i < array.length; i++) {
          if (!isNaN(array[i])) {
            t += array[i];
            n++;
          }
        }

        return n > 0 ? t / n : 0;
      },
      meanAndStdev: function (array) {
        var v,
            t = 0,
            t2 = 0,
            n = 0,
            i;

        for (i = 0; i < array.length; i++) {
          v = array[i];

          if (!isNaN(v)) {
            t += v;
            t2 += v * v;
            n++;
          }
        }

        return n > 0 ? {
          mean: t / n,
          stdev: Math.sqrt(t2 - t * t / n)
        } : {
          mean: 0,
          stdev: 0
        };
      },
      median: function (numbers) {
        // median of [3, 5, 4, 4, 1, 1, 2, 3] = 3
        var median = 0,
            numsLen = numbers.length;
        numbers.sort();

        if (numsLen % 2 === 0 // is even
        ) {
          // average of two middle numbers
          median = (numbers[numsLen / 2 - 1] + numbers[numsLen / 2]) / 2;
        } else {
          // is odd
          // middle number only
          median = numbers[(numsLen - 1) / 2];
        }

        return median;
      },
      // Fast percentile function for "p" near edges.  This needs profiled for p in middle (e.g. median)
      percentile: function (array, p) {
        if (array.length === 0) return undefined;
        var k = Math.floor(array.length * ((100 - p) / 100));

        if (k === 0) {
          array.sort(function (a, b) {
            return b - a;
          });
          return array[k];
        } else {
          return selectElement(array, k);
        }
      },
      clamp: function (value, min, max) {
        return Math.min(Math.max(value, min), max);
      },
      log2: function (x) {
        return Math.log(x) / Math.LN2;
      }
    };

    function selectElement(array, k) {
      // Credit Steve Hanov http://stevehanov.ca/blog/index.php?id=122
      var heap = new BinaryHeap(),
          i;

      for (i = 0; i < array.length; i++) {
        var item = array[i]; // If we have not yet found k items, or the current item is larger than
        // the smallest item on the heap, add current item

        if (heap.content.length < k || item > heap.content[0]) {
          // If the heap is full, remove the smallest element on the heap.
          if (heap.content.length === k) {
            heap.pop();
          }

          heap.push(item);
        }
      }

      return heap.content[0];
    }

    function BinaryHeap() {
      this.content = [];
    }

    BinaryHeap.prototype = {
      push: function (element) {
        // Add the new element to the end of the array.
        this.content.push(element); // Allow it to bubble up.

        this.bubbleUp(this.content.length - 1);
      },
      pop: function () {
        // Store the first element so we can return it later.
        var result = this.content[0]; // Get the element at the end of the array.

        var end = this.content.pop(); // If there are any elements left, put the end element at the
        // start, and let it sink down.

        if (this.content.length > 0) {
          this.content[0] = end;
          this.sinkDown(0);
        }

        return result;
      },
      remove: function (node) {
        var length = this.content.length; // To remove a value, we must search through the array to find
        // it.

        for (var i = 0; i < length; i++) {
          if (this.content[i] !== node) continue; // When it is found, the process seen in 'pop' is repeated
          // to fill up the hole.

          var end = this.content.pop(); // If the element we popped was the one we needed to remove,
          // we're done.

          if (i === length - 1) break; // Otherwise, we replace the removed element with the popped
          // one, and allow it to float up or sink down as appropriate.

          this.content[i] = end;
          this.bubbleUp(i);
          this.sinkDown(i);
          break;
        }
      },
      size: function () {
        return this.content.length;
      },
      bubbleUp: function (n) {
        // Fetch the element that has to be moved.
        var element = this.content[n],
            score = element; // When at 0, an element can not go up any further.

        while (n > 0) {
          // Compute the parent element's index, and fetch it.
          var parentN = Math.floor((n + 1) / 2) - 1,
              parent = this.content[parentN]; // If the parent has a lesser score, things are in order and we
          // are done.

          if (score >= parent) break; // Otherwise, swap the parent with the current element and
          // continue.

          this.content[parentN] = element;
          this.content[n] = parent;
          n = parentN;
        }
      },
      sinkDown: function (n) {
        // Look up the target element and its score.
        var length = this.content.length,
            element = this.content[n],
            elemScore = element;

        while (true) {
          // Compute the indices of the child elements.
          var child2N = (n + 1) * 2,
              child1N = child2N - 1; // This is used to store the new position of the element,
          // if any.

          var swap = null; // If the first child exists (is inside the array)...

          if (child1N < length) {
            // Look it up and compute its score.
            var child1 = this.content[child1N],
                child1Score = child1; // If the score is less than our element's, we need to swap.

            if (child1Score < elemScore) swap = child1N;
          } // Do the same checks for the other child.


          if (child2N < length) {
            var child2 = this.content[child2N],
                child2Score = child2;
            if (child2Score < (swap == null ? elemScore : child1Score)) swap = child2N;
          } // No need to swap further, we are done.


          if (swap == null) break; // Otherwise, swap and continue.

          this.content[n] = this.content[swap];
          this.content[swap] = element;
          n = swap;
        }
      }
    };

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    function _random(min, max) {
      return Math.random() * (max - min) + min;
    }

    const IGVColor = {
      rgbListFromHSV: () => {
        let s = 1;
        let accumulation = [];

        for (let v = 1; v >= 0.5; v -= .1) {
          for (let h = 0; h < 1; h += 1 / 28) {
            const r = "rgb(" + IGVColor.hsvToRgb(h, s, v).join(",") + ")";
            accumulation.push(r);
          }
        } // add black


        accumulation.pop();
        accumulation.push(IGVColor.rgbColor(16, 16, 16));
        return accumulation;
      },
      rgbToHex: function (rgb) {
        rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
        return rgb && rgb.length === 4 ? "#" + ("0" + parseInt(rgb[1], 10).toString(16)).slice(-2) + ("0" + parseInt(rgb[2], 10).toString(16)).slice(-2) + ("0" + parseInt(rgb[3], 10).toString(16)).slice(-2) : '';
      },
      hexToRgb: function (hex) {
        var cooked = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);

        if (null === cooked) {
          return undefined;
        }

        return "rgb(" + parseInt(cooked[1], 16) + "," + parseInt(cooked[2], 16) + "," + parseInt(cooked[3], 16) + ")";
      },

      /**
       * Converts an HSV color value to RGB. Conversion formula
       * adapted from http://en.wikipedia.org/wiki/HSV_color_space.
       * Assumes h, s, and v are contained in the set [0, 1] and
       * returns r, g, and b in the set [0, 255].
       *
       * Credit: https://gist.githubusercontent.com/mjackson/5311256
       *
       * @param   h       The hue
       * @param   s       The saturation
       * @param   v       The value
       * @return  Array   The RGB representation
       */
      hsvToRgb: function (h, s, v) {
        var r, g, b;
        var i = Math.floor(h * 6);
        var f = h * 6 - i;
        var p = v * (1 - s);
        var q = v * (1 - f * s);
        var t = v * (1 - (1 - f) * s);

        switch (i % 6) {
          case 0:
            r = v, g = t, b = p;
            break;

          case 1:
            r = q, g = v, b = p;
            break;

          case 2:
            r = p, g = v, b = t;
            break;

          case 3:
            r = p, g = q, b = v;
            break;

          case 4:
            r = t, g = p, b = v;
            break;

          case 5:
            r = v, g = p, b = q;
            break;
        }

        return [Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)];
      },

      /**
       * Converts an HSL color value to RGB. Conversion formula
       * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
       * Assumes h, s, and l are contained in the set [0, 1] and
       * returns r, g, and b in the set [0, 255].
       *
       * Credit: https://gist.githubusercontent.com/mjackson/5311256
       *
       * @param   h       The hue
       * @param   s       The saturation
       * @param   l       The lightness
       * @return  Array   The RGB representation
       */
      hslToRgb: function (h, s, l) {
        var r, g, b;

        if (s === 0) {
          r = g = b = l; // achromatic
        } else {
          var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
          var p = 2 * l - q;
          r = IGVColor.hue2rgb(p, q, h + 1 / 3);
          g = IGVColor.hue2rgb(p, q, h);
          b = IGVColor.hue2rgb(p, q, h - 1 / 3);
        }

        return [r * 255, g * 255, b * 255];
      },
      hue2rgb: (p, q, t) => {
        if (t < 0) t += 1;
        if (t > 1) t -= 1;
        if (t < 1 / 6) return p + (q - p) * 6 * t;
        if (t < 1 / 2) return q;
        if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
        return p;
      },
      rgbaColor: function (r, g, b, a) {
        r = IGVMath.clamp(r, 0, 255);
        g = IGVMath.clamp(g, 0, 255);
        b = IGVMath.clamp(b, 0, 255);
        a = IGVMath.clamp(a, 0.0, 1.0);
        return "rgba(" + r + "," + g + "," + b + "," + a + ")";
      },
      rgbColor: function (r, g, b) {
        r = IGVMath.clamp(r, 0, 255);
        g = IGVMath.clamp(g, 0, 255);
        b = IGVMath.clamp(b, 0, 255);
        return "rgb(" + r + "," + g + "," + b + ")";
      },
      greyScale: function (value) {
        var grey = IGVMath.clamp(value, 0, 255);
        return "rgb(" + grey + "," + grey + "," + grey + ")";
      },
      randomGrey: function (min, max) {
        min = IGVMath.clamp(min, 0, 255);
        max = IGVMath.clamp(max, 0, 255);
        var g = Math.round(_random(min, max)).toString(10);
        return "rgb(" + g + "," + g + "," + g + ")";
      },
      randomRGB: function (min, max) {
        min = IGVMath.clamp(min, 0, 255);
        max = IGVMath.clamp(max, 0, 255);
        var r = Math.round(_random(min, max)).toString(10);
        var g = Math.round(_random(min, max)).toString(10);
        var b = Math.round(_random(min, max)).toString(10);
        return "rgb(" + r + "," + g + "," + b + ")";
      },
      randomRGBConstantAlpha: function (min, max, alpha) {
        min = IGVMath.clamp(min, 0, 255);
        max = IGVMath.clamp(max, 0, 255);
        var r = Math.round(_random(min, max)).toString(10);
        var g = Math.round(_random(min, max)).toString(10);
        var b = Math.round(_random(min, max)).toString(10);
        return "rgba(" + r + "," + g + "," + b + "," + alpha + ")";
      },
      addAlpha: function (color, alpha) {
        if (color === "0" || color === ".") {
          color = "rgb(0,0,0)";
        } else {
          const c = this.colorNameToHex(color);

          if (c) {
            color = c;
          }
        }

        var isHex = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(color);

        if (color.startsWith("rgba")) {
          const idx = color.lastIndexOf(",");
          return color.substring(0, idx + 1) + alpha.toString() + ")";
        }

        if (isHex) {
          color = IGVColor.hexToRgb(color);
        }

        if (color.startsWith("rgb")) {
          return color.replace("rgb", "rgba").replace(")", ", " + alpha + ")");
        } else {
          console.log(color + " is not an rgb style string");
          return color;
        }
      },
      rgbComponents: function (color) {
        if (color === "0" || color === ".") {
          return [0, 0, 0];
        }

        const isHex = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(color);

        if (isHex) {
          color = IGVColor.hexToRgb(color);
        } else {
          if (!color.startsWith("rgb")) {
            const hex = this.colorNameToHex(color);
            color = this.hexToRgb(hex);
          }
        }

        if (color.startsWith("rgb(")) {
          return color.substring(4, color.length - 1).split(",").map(s => Number.parseInt(s.trim()));
        } else if (color.startsWith("rgba(")) {
          return color.substring(5, color.length - 1).split(",").map((s, i) => {
            s = s.trim();
            return i === 3 ? Number.parseFloat(s) : Number.parseInt(s);
          });
        } else {
          throw Error("Unrecognized color string: color");
        }
      },

      /**
       *
       * @param dest  RGB components as an array
       * @param src  RGB components as an array
       * @param alpha   alpha transparancy in the range 0-1
       * @returns {}
       */
      getCompositeColor: function (dest, src, alpha) {
        var r = Math.floor(alpha * src[0] + (1 - alpha) * dest[0]),
            g = Math.floor(alpha * src[1] + (1 - alpha) * dest[1]),
            b = Math.floor(alpha * src[2] + (1 - alpha) * dest[2]);
        return "rgb(" + r + "," + g + "," + b + ")";
      },
      createColorString: function (str) {
        // Excel will quote color strings, strip all quotes
        str = stripQuotes$1(str);

        if (str.includes(",")) {
          return str.startsWith("rgb") ? str : "rgb(" + str + ")";
        } else {
          return str;
        }
      },
      darkenLighten: function (color, amt) {
        let src;
        let hexColor = this.colorNameToHex(color);

        if (hexColor) {
          src = IGVColor.hexToRgb(hexColor);
        } else {
          src = color.startsWith('rgb(') ? color : IGVColor.hexToRgb(color);
        }

        const components = src.replace(")", "").substring(4).split(",");
        const r = Math.max(0, Math.min(255, Number.parseInt(components[0].trim()) + amt));
        const g = Math.max(0, Math.min(255, Number.parseInt(components[1].trim()) + amt));
        const b = Math.max(0, Math.min(255, Number.parseInt(components[2].trim()) + amt));
        return 'rgb(' + r.toString() + ',' + g.toString() + ',' + b.toString() + ')';
      },

      /**
       * Convert html/css color name to hex value.  Adapted from https://gist.github.com/mxfh/4719348
       * @param colorName
       * @returns {*}
       */
      colorNameToHex: function (colorName) {
        // color list from http://stackoverflow.com/q/1573053/731179  with added gray/gray
        const definedColorNames = {
          "aliceblue": "#f0f8ff",
          "antiquewhite": "#faebd7",
          "aqua": "#00ffff",
          "aquamarine": "#7fffd4",
          "azure": "#f0ffff",
          "beige": "#f5f5dc",
          "bisque": "#ffe4c4",
          "black": "#000000",
          "blanchedalmond": "#ffebcd",
          "blue": "#0000ff",
          "blueviolet": "#8a2be2",
          "brown": "#a52a2a",
          "burlywood": "#deb887",
          "cadetblue": "#5f9ea0",
          "chartreuse": "#7fff00",
          "chocolate": "#d2691e",
          "coral": "#ff7f50",
          "cornflowerblue": "#6495ed",
          "cornsilk": "#fff8dc",
          "crimson": "#dc143c",
          "cyan": "#00ffff",
          "darkblue": "#00008b",
          "darkcyan": "#008b8b",
          "darkgoldenrod": "#b8860b",
          "darkgray": "#a9a9a9",
          "darkgreen": "#006400",
          "darkkhaki": "#bdb76b",
          "darkmagenta": "#8b008b",
          "darkolivegreen": "#556b2f",
          "darkorange": "#ff8c00",
          "darkorchid": "#9932cc",
          "darkred": "#8b0000",
          "darksalmon": "#e9967a",
          "darkseagreen": "#8fbc8f",
          "darkslateblue": "#483d8b",
          "darkslategray": "#2f4f4f",
          "darkturquoise": "#00ced1",
          "darkviolet": "#9400d3",
          "deeppink": "#ff1493",
          "deepskyblue": "#00bfff",
          "dimgray": "#696969",
          "dodgerblue": "#1e90ff",
          "firebrick": "#b22222",
          "floralwhite": "#fffaf0",
          "forestgreen": "#228b22",
          "fuchsia": "#ff00ff",
          "gainsboro": "#dcdcdc",
          "ghostwhite": "#f8f8ff",
          "gold": "#ffd700",
          "goldenrod": "#daa520",
          "gray": "#808080",
          "green": "#008000",
          "greenyellow": "#adff2f",
          "honeydew": "#f0fff0",
          "hotpink": "#ff69b4",
          "indianred ": "#cd5c5c",
          "indigo ": "#4b0082",
          "ivory": "#fffff0",
          "khaki": "#f0e68c",
          "lavender": "#e6e6fa",
          "lavenderblush": "#fff0f5",
          "lawngreen": "#7cfc00",
          "lemonchiffon": "#fffacd",
          "lightblue": "#add8e6",
          "lightcoral": "#f08080",
          "lightcyan": "#e0ffff",
          "lightgoldenrodyellow": "#fafad2",
          "lightgrey": "#d3d3d3",
          "lightgreen": "#90ee90",
          "lightpink": "#ffb6c1",
          "lightsalmon": "#ffa07a",
          "lightseagreen": "#20b2aa",
          "lightskyblue": "#87cefa",
          "lightslategray": "#778899",
          "lightsteelblue": "#b0c4de",
          "lightyellow": "#ffffe0",
          "lime": "#00ff00",
          "limegreen": "#32cd32",
          "linen": "#faf0e6",
          "magenta": "#ff00ff",
          "maroon": "#800000",
          "mediumaquamarine": "#66cdaa",
          "mediumblue": "#0000cd",
          "mediumorchid": "#ba55d3",
          "mediumpurple": "#9370d8",
          "mediumseagreen": "#3cb371",
          "mediumslateblue": "#7b68ee",
          "mediumspringgreen": "#00fa9a",
          "mediumturquoise": "#48d1cc",
          "mediumvioletred": "#c71585",
          "midnightblue": "#191970",
          "mintcream": "#f5fffa",
          "mistyrose": "#ffe4e1",
          "moccasin": "#ffe4b5",
          "navajowhite": "#ffdead",
          "navy": "#000080",
          "oldlace": "#fdf5e6",
          "olive": "#808000",
          "olivedrab": "#6b8e23",
          "orange": "#ffa500",
          "orangered": "#ff4500",
          "orchid": "#da70d6",
          "palegoldenrod": "#eee8aa",
          "palegreen": "#98fb98",
          "paleturquoise": "#afeeee",
          "palevioletred": "#d87093",
          "papayawhip": "#ffefd5",
          "peachpuff": "#ffdab9",
          "peru": "#cd853f",
          "pink": "#ffc0cb",
          "plum": "#dda0dd",
          "powderblue": "#b0e0e6",
          "purple": "#800080",
          "red": "#ff0000",
          "rosybrown": "#bc8f8f",
          "royalblue": "#4169e1",
          "saddlebrown": "#8b4513",
          "salmon": "#fa8072",
          "sandybrown": "#f4a460",
          "seagreen": "#2e8b57",
          "seashell": "#fff5ee",
          "sienna": "#a0522d",
          "silver": "#c0c0c0",
          "skyblue": "#87ceeb",
          "slateblue": "#6a5acd",
          "slategray": "#708090",
          "snow": "#fffafa",
          "springgreen": "#00ff7f",
          "steelblue": "#4682b4",
          "tan": "#d2b48c",
          "teal": "#008080",
          "thistle": "#d8bfd8",
          "tomato": "#ff6347",
          "turquoise": "#40e0d0",
          "violet": "#ee82ee",
          "wheat": "#f5deb3",
          "white": "#ffffff",
          "whitesmoke": "#f5f5f5",
          "yellow": "#ffff00",
          "yellowgreen": "#9acd32",
          "darkgrey": "#a9a9a9",
          "darkslategrey": "#2f4f4f",
          "dimgrey": "#696969",
          "grey": "#808080",
          "lightgray": "#d3d3d3",
          "lightslategrey": "#778899",
          "slategrey": "#708090"
        };
        return definedColorNames[colorName];
      }
    };

    /**
     * Make the target element movable by clicking and dragging on the handle.  This is not a general purprose function,
     * it makes several options specific to igv dialogs, the primary one being that the
     * target is absolutely positioned in pixel coordinates

     */
    let dragData$1; // Its assumed we are only dragging one element at a time.

    let bbox = undefined;

    function makeDraggable$1(target, handle, constraint) {
      if (constraint) {
        bbox = Object.assign({}, constraint);
      }

      handle.addEventListener('mousedown', dragStart$1.bind(target));
    }

    function dragStart$1(event) {
      event.stopPropagation();
      event.preventDefault();
      offset$1(this);
      const dragFunction = drag$1.bind(this);
      const dragEndFunction = dragEnd$1.bind(this);
      const computedStyle = getComputedStyle(this);
      const top = parseInt(computedStyle.top.replace("px", ""));
      const left = parseInt(computedStyle.left.replace("px", ""));
      dragData$1 = {
        dragFunction: dragFunction,
        dragEndFunction: dragEndFunction,
        screenX: event.screenX,
        screenY: event.screenY,
        top: top,
        left: left
      };
      document.addEventListener('mousemove', dragFunction);
      document.addEventListener('mouseup', dragEndFunction);
      document.addEventListener('mouseleave', dragEndFunction);
      document.addEventListener('mouseexit', dragEndFunction);
    }

    function drag$1(event) {
      if (!dragData$1) {
        console.log("No drag data!");
        return;
      }

      event.stopPropagation();
      event.preventDefault();
      const dx = event.screenX - dragData$1.screenX;
      const dy = event.screenY - dragData$1.screenY; // const left = bbox ? Math.max(bbox.minX, dragData.left + dx) : dragData.left + dx

      const left = dragData$1.left + dx;
      const top = bbox ? Math.max(bbox.minY, dragData$1.top + dy) : dragData$1.top + dy;
      this.style.left = `${left}px`;
      this.style.top = `${top}px`;
    }

    function dragEnd$1(event) {
      if (!dragData$1) {
        console.log("No drag data!");
        return;
      }

      event.stopPropagation();
      event.preventDefault();
      const dragFunction = dragData$1.dragFunction;
      const dragEndFunction = dragData$1.dragEndFunction;
      document.removeEventListener('mousemove', dragFunction);
      document.removeEventListener('mouseup', dragEndFunction);
      document.removeEventListener('mouseleave', dragEndFunction);
      document.removeEventListener('mouseexit', dragEndFunction);
      dragData$1 = undefined;
    }

    const appleCrayonPalette$1 = {
      licorice: "#000000",
      lead: "#1e1e1e",
      tungsten: "#3a3a3a",
      iron: "#545453",
      steel: "#6e6e6e",
      tin: "#878687",
      nickel: "#888787",
      aluminum: "#a09fa0",
      magnesium: "#b8b8b8",
      silver: "#d0d0d0",
      mercury: "#e8e8e8",
      snow: "#ffffff",
      //
      cayenne: "#891100",
      mocha: "#894800",
      aspargus: "#888501",
      fern: "#458401",
      clover: "#028401",
      moss: "#018448",
      teal: "#008688",
      ocean: "#004a88",
      midnight: "#001888",
      eggplant: "#491a88",
      plum: "#891e88",
      maroon: "#891648",
      //
      maraschino: "#ff2101",
      tangerine: "#ff8802",
      lemon: "#fffa03",
      lime: "#83f902",
      spring: "#05f802",
      seam_foam: "#03f987",
      turquoise: "#00fdff",
      aqua: "#008cff",
      blueberry: "#002eff",
      grape: "#8931ff",
      magenta: "#ff39ff",
      strawberry: "#ff2987",
      //
      salmon: "#ff726e",
      cantaloupe: "#ffce6e",
      banana: "#fffb6d",
      honeydew: "#cefa6e",
      flora: "#68f96e",
      spindrift: "#68fbd0",
      ice: "#68fdff",
      sky: "#6acfff",
      orchid: "#6e76ff",
      lavender: "#d278ff",
      bubblegum: "#ff7aff",
      carnation: "#ff7fd3"
    };

    // Support for oauth token based authorization
    // This class supports explicit setting of an oauth token either globally or for specific hosts.
    //
    // The variable oauth.google.access_token, which becomes igv.oauth.google.access_token on ES5 conversion is
    // supported for backward compatibility
    const DEFAULT_HOST = "googleapis";
    const oauth = {
      oauthTokens: {},
      setToken: function (token, host) {
        host = host || DEFAULT_HOST;
        this.oauthTokens[host] = token;

        if (host === DEFAULT_HOST) {
          this.google.access_token = token; // legacy support
        }
      },
      getToken: function (host) {
        host = host || DEFAULT_HOST;
        let token;

        for (let key of Object.keys(this.oauthTokens)) {
          const regex = wildcardToRegExp(key);

          if (regex.test(host)) {
            token = this.oauthTokens[key];
            break;
          }
        }

        return token;
      },
      removeToken: function (host) {
        host = host || DEFAULT_HOST;

        for (let key of Object.keys(this.oauthTokens)) {
          const regex = wildcardToRegExp(key);

          if (regex.test(host)) {
            this.oauthTokens[key] = undefined;
          }
        }

        if (host === DEFAULT_HOST) {
          this.google.access_token = undefined; // legacy support
        }
      },
      // Special object for google -- legacy support
      google: {
        setToken: function (token) {
          oauth.setToken(token);
        }
      }
    };
    /**
     * Creates a RegExp from the given string, converting asterisks to .* expressions,
     * and escaping all other characters.
     *
     * credit https://gist.github.com/donmccurdy/6d073ce2c6f3951312dfa45da14a420f
     */

    function wildcardToRegExp(s) {
      return new RegExp('^' + s.split(/\*+/).map(regExpEscape).join('.*') + '$');
    }
    /**
     * RegExp-escapes all characters in the given string.
     *
     * credit https://gist.github.com/donmccurdy/6d073ce2c6f3951312dfa45da14a420f
     */


    function regExpEscape(s) {
      return s.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
    }

    // The MIT License (MIT)
    /**
     * @constructor
     * @param {Object} options A set op options to pass to the throttle function
     *        @param {number} requestsPerSecond The amount of requests per second
     *                                          the library will limit to
     */

    class Throttle {
      constructor(options) {
        this.requestsPerSecond = options.requestsPerSecond || 10;
        this.lastStartTime = 0;
        this.queued = [];
      }
      /**
       * Adds a promise
       * @param {Function} async function to be executed
       * @param {Object} options A set of options.
       * @return {Promise} A promise
       */


      add(asyncFunction, options) {
        var self = this;
        return new Promise(function (resolve, reject) {
          self.queued.push({
            resolve: resolve,
            reject: reject,
            asyncFunction: asyncFunction
          });
          self.dequeue();
        });
      }
      /**
       * Adds all the promises passed as parameters
       * @param {Function} promises An array of functions that return a promise
       * @param {Object} options A set of options.
       * @param {number} options.signal An AbortSignal object that can be used to abort the returned promise
       * @param {number} options.weight A "weight" of each operation resolving by array of promises
       * @return {Promise} A promise that succeeds when all the promises passed as options do
       */


      addAll(promises, options) {
        var addedPromises = promises.map(function (promise) {
          return this.add(promise, options);
        }.bind(this));
        return Promise.all(addedPromises);
      }

      /**
       * Dequeues a promise
       * @return {void}
       */
      dequeue() {
        if (this.queued.length > 0) {
          var now = new Date(),
              inc = 1000 / this.requestsPerSecond + 1,
              elapsed = now - this.lastStartTime;

          if (elapsed >= inc) {
            this._execute();
          } else {
            // we have reached the limit, schedule a dequeue operation
            setTimeout(function () {
              this.dequeue();
            }.bind(this), inc - elapsed);
          }
        }
      }
      /**
       * Executes the promise
       * @private
       * @return {void}
       */


      async _execute() {
        this.lastStartTime = new Date();
        var candidate = this.queued.shift();
        const f = candidate.asyncFunction;

        try {
          const r = await f();
          candidate.resolve(r);
        } catch (e) {
          candidate.reject(e);
        }
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    let RANGE_WARNING_GIVEN = false;
    const googleThrottle = new Throttle({
      requestsPerSecond: 8
    });
    const igvxhr = {
      apiKey: undefined,
      setApiKey: function (key) {
        this.apiKey = key;
      },
      load: load,
      loadArrayBuffer: async function (url, options) {
        options = options || {};

        if (!options.responseType) {
          options.responseType = "arraybuffer";
        }

        if (isFile(url)) {
          return loadFileSlice(url, options);
        } else {
          return load(url, options);
        }
      },
      loadJson: async function (url, options) {
        options = options || {};
        const method = options.method || (options.sendData ? "POST" : "GET");

        if (method === "POST") {
          options.contentType = "application/json";
        }

        const result = await this.loadString(url, options);

        if (result) {
          return JSON.parse(result);
        } else {
          return result;
        }
      },
      loadString: async function (path, options) {
        options = options || {};

        if (path instanceof File) {
          return loadStringFromFile(path, options);
        } else {
          return loadStringFromUrl(path, options);
        }
      }
    };

    async function load(url, options) {
      options = options || {};
      const urlType = typeof url; // Resolve functions, promises, and functions that return promises

      url = await (typeof url === 'function' ? url() : url);

      if (isFile(url)) {
        return loadFileSlice(url, options);
      } else if (typeof url.startsWith === 'function') {
        // Test for string
        if (url.startsWith("data:")) {
          const buffer = decodeDataURI$1(url).buffer;

          if (options.range) {
            const rangeEnd = options.range.size ? options.range.start + options.range.size : buffer.byteLength;
            return buffer.slice(options.range.start, rangeEnd);
          } else {
            return buffer;
          }
        } else {
          if (url.startsWith("https://drive.google.com")) {
            url = driveDownloadURL(url);
          }

          if (isGoogleDriveURL(url) || url.startsWith("https://www.dropbox.com")) {
            return googleThrottle.add(async function () {
              return loadURL(url, options);
            });
          } else {
            return loadURL(url, options);
          }
        }
      } else {
        throw Error(`url must be either a 'File', 'string', 'function', or 'Promise'.  Actual type: ${urlType}`);
      }
    }

    async function loadURL(url, options) {
      //console.log(`${Date.now()}   ${url}`)
      url = mapUrl(url);
      options = options || {};
      let oauthToken = options.oauthToken || getOauthToken(url);

      if (oauthToken) {
        oauthToken = await (typeof oauthToken === 'function' ? oauthToken() : oauthToken);
      }

      return new Promise(function (resolve, reject) {
        // Various Google tansformations
        if (isGoogleURL(url) && !isGoogleStorageSigned(url)) {
          if (isGoogleStorageURL(url)) {
            url = translateGoogleCloudURL(url);
          }

          url = addApiKey(url);

          if (isGoogleDriveURL(url)) {
            addTeamDrive(url);
          } // If we have an access token try it, but don't force a signIn or request for scopes yet


          if (!oauthToken) {
            oauthToken = getCurrentGoogleAccessToken();
          }
        }

        const headers = options.headers || {};

        if (oauthToken) {
          addOauthHeaders(headers, oauthToken);
        }

        const range = options.range;
        const isChrome = navigator.userAgent.indexOf('Chrome') > -1;
        navigator.vendor.indexOf("Apple") === 0 && /\sSafari\//.test(navigator.userAgent);

        if (range && isChrome && !isAmazonV4Signed(url) && !isGoogleStorageSigned(url)) {
          // Hack to prevent caching for byte-ranges. Attempt to fix net:err-cache errors in Chrome
          url += url.includes("?") ? "&" : "?";
          url += "someRandomSeed=" + Math.random().toString(36);
        }

        const xhr = new XMLHttpRequest();
        const sendData = options.sendData || options.body;
        const method = options.method || (sendData ? "POST" : "GET");
        const responseType = options.responseType;
        const contentType = options.contentType;
        const mimeType = options.mimeType;
        xhr.open(method, url);

        if (options.timeout) {
          xhr.timeout = options.timeout;
        }

        if (range) {
          var rangeEnd = range.size ? range.start + range.size - 1 : "";
          xhr.setRequestHeader("Range", "bytes=" + range.start + "-" + rangeEnd); //      xhr.setRequestHeader("Cache-Control", "no-cache");    <= This can cause CORS issues, disabled for now
        }

        if (contentType) {
          xhr.setRequestHeader("Content-Type", contentType);
        }

        if (mimeType) {
          xhr.overrideMimeType(mimeType);
        }

        if (responseType) {
          xhr.responseType = responseType;
        }

        if (headers) {
          for (let key of Object.keys(headers)) {
            const value = headers[key];
            xhr.setRequestHeader(key, value);
          }
        } // NOTE: using withCredentials with servers that return "*" for access-allowed-origin will fail


        if (options.withCredentials === true) {
          xhr.withCredentials = true;
        }

        xhr.onload = async function (event) {
          // when the url points to a local file, the status is 0 but that is not an error
          if (xhr.status === 0 || xhr.status >= 200 && xhr.status <= 300) {
            if (range && xhr.status !== 206 && range.start !== 0) {
              // For small files a range starting at 0 can return the whole file => 200
              // Provide just the slice we asked for, throw out the rest quietly
              // If file is large warn user
              if (xhr.response.length > 100000 && !RANGE_WARNING_GIVEN) {
                alert(`Warning: Range header ignored for URL: ${url}.  This can have performance impacts.`);
              }

              resolve(xhr.response.slice(range.start, range.start + range.size));
            } else {
              resolve(xhr.response);
            }
          } else if (typeof gapi !== "undefined" && (xhr.status === 404 || xhr.status === 401 || xhr.status === 403) && isGoogleURL(url) && !options.retries) {
            tryGoogleAuth();
          } else {
            if (xhr.status === 403) {
              handleError("Access forbidden: " + url);
            } else if (xhr.status === 416) {
              //  Tried to read off the end of the file.   This shouldn't happen, but if it does return an
              handleError("Unsatisfiable range");
            } else {
              handleError(xhr.status);
            }
          }
        };

        xhr.onerror = function (event) {
          if (isGoogleURL(url) && !options.retries) {
            tryGoogleAuth();
          }

          handleError("Error accessing resource: " + url + " Status: " + xhr.status);
        };

        xhr.ontimeout = function (event) {
          handleError("Timed out");
        };

        xhr.onabort = function (event) {
          console.log("Aborted");
          reject(event);
        };

        try {
          xhr.send(sendData);
        } catch (e) {
          reject(e);
        }

        function handleError(error) {
          if (reject) {
            reject(error);
          } else {
            throw error;
          }
        }

        async function tryGoogleAuth() {
          try {
            const accessToken = await fetchGoogleAccessToken(url);
            options.retries = 1;
            options.oauthToken = accessToken;
            const response = await load(url, options);
            resolve(response);
          } catch (e) {
            if (e.error) {
              const msg = e.error.startsWith("popup_blocked") ? "Google login popup blocked by browser." : e.error;
              alert(msg);
            } else {
              handleError(e);
            }
          }
        }
      });
    }

    async function loadFileSlice(localfile, options) {
      let blob = options && options.range ? localfile.slice(options.range.start, options.range.start + options.range.size) : localfile;
      const arrayBuffer = await blob.arrayBuffer();

      if ("arraybuffer" === options.responseType) {
        return arrayBuffer;
      } else {
        return arrayBufferToString(arrayBuffer);
      }
    }

    async function loadStringFromFile(localfile, options) {
      const blob = options.range ? localfile.slice(options.range.start, options.range.start + options.range.size) : localfile;
      const arrayBuffer = await blob.arrayBuffer();
      return arrayBufferToString(arrayBuffer);
    }

    async function loadStringFromUrl(url, options) {
      options = options || {};
      options.responseType = "arraybuffer";
      const data = await igvxhr.load(url, options);
      return arrayBufferToString(data);
    }

    function isAmazonV4Signed(url) {
      return url.indexOf("X-Amz-Signature") > -1;
    }

    function isGoogleStorageSigned(url) {
      return url.indexOf("X-Goog-Signature") > -1;
    }

    function getOauthToken(url) {
      // Google is the default provider, don't try to parse host for google URLs
      const host = isGoogleURL(url) ? undefined : parseUri(url).host;
      let token = oauth.getToken(host);

      if (token) {
        return token;
      } else if (host === undefined) {
        const googleToken = getCurrentGoogleAccessToken();

        if (googleToken && googleToken.expires_at > Date.now()) {
          return googleToken.access_token;
        }
      }
    }
    /**
     * Return a Google oAuth token, triggering a sign in if required.   This method should not be called until we know
     * a token is required, that is until we've tried the url and received a 401, 403, or 404.
     *
     * @param url
     * @returns the oauth token
     */


    async function fetchGoogleAccessToken(url) {
      if (isInitialized()) {
        const scope = getScopeForURL(url);
        const googleToken = await getAccessToken(scope);
        return googleToken ? googleToken.access_token : undefined;
      } else {
        throw Error(`Authorization is required, but Google oAuth has not been initalized. Contact your site administrator for assistance.`);
      }
    }
    /**
     * Return the current google access token, if one exists.  Do not triger signOn or request additional scopes.
     * @returns {undefined|access_token}
     */


    function getCurrentGoogleAccessToken() {
      if (isInitialized()) {
        const googleToken = getCurrentAccessToken();
        return googleToken ? googleToken.access_token : undefined;
      } else {
        return undefined;
      }
    }

    function addOauthHeaders(headers, acToken) {
      if (acToken) {
        headers["Cache-Control"] = "no-cache";
        headers["Authorization"] = "Bearer " + acToken;
      }

      return headers;
    }

    function addApiKey(url) {
      let apiKey = igvxhr.apiKey;

      if (!apiKey && typeof gapi !== "undefined") {
        apiKey = gapi.apiKey;
      }

      if (apiKey !== undefined && !url.includes("key=")) {
        const paramSeparator = url.includes("?") ? "&" : "?";
        url = url + paramSeparator + "key=" + apiKey;
      }

      return url;
    }

    function addTeamDrive(url) {
      if (url.includes("supportsTeamDrive")) {
        return url;
      } else {
        const paramSeparator = url.includes("?") ? "&" : "?";
        url = url + paramSeparator + "supportsTeamDrive=true";
      }
    }
    /**
     * Perform some well-known url mappings.
     * @param url
     */


    function mapUrl(url) {
      if (url.includes("//www.dropbox.com")) {
        return url.replace("//www.dropbox.com", "//dl.dropboxusercontent.com");
      } else if (url.includes("//drive.google.com")) {
        return driveDownloadURL(url);
      } else if (url.includes("//www.broadinstitute.org/igvdata")) {
        return url.replace("//www.broadinstitute.org/igvdata", "//data.broadinstitute.org/igvdata");
      } else if (url.includes("//igvdata.broadinstitute.org")) {
        return url.replace("//igvdata.broadinstitute.org", "https://dn7ywbm9isq8j.cloudfront.net");
      } else if (url.startsWith("ftp://ftp.ncbi.nlm.nih.gov/geo")) {
        return url.replace("ftp://", "https://");
      } else {
        return url;
      }
    }

    function arrayBufferToString(arraybuffer) {
      let plain;

      if (isgzipped(arraybuffer)) {
        plain = ungzip(arraybuffer);
      } else {
        plain = new Uint8Array(arraybuffer);
      }

      if ('TextDecoder' in getGlobalObject()) {
        return new TextDecoder().decode(plain);
      } else {
        return decodeUTF8(plain);
      }
    }
    /**
     * Use when TextDecoder is not available (primarily IE).
     *
     * From: https://gist.github.com/Yaffle/5458286
     *
     * @param octets
     * @returns {string}
     */


    function decodeUTF8(octets) {
      var string = "";
      var i = 0;

      while (i < octets.length) {
        var octet = octets[i];
        var bytesNeeded = 0;
        var codePoint = 0;

        if (octet <= 0x7F) {
          bytesNeeded = 0;
          codePoint = octet & 0xFF;
        } else if (octet <= 0xDF) {
          bytesNeeded = 1;
          codePoint = octet & 0x1F;
        } else if (octet <= 0xEF) {
          bytesNeeded = 2;
          codePoint = octet & 0x0F;
        } else if (octet <= 0xF4) {
          bytesNeeded = 3;
          codePoint = octet & 0x07;
        }

        if (octets.length - i - bytesNeeded > 0) {
          var k = 0;

          while (k < bytesNeeded) {
            octet = octets[i + k + 1];
            codePoint = codePoint << 6 | octet & 0x3F;
            k += 1;
          }
        } else {
          codePoint = 0xFFFD;
          bytesNeeded = octets.length - i;
        }

        string += String.fromCodePoint(codePoint);
        i += bytesNeeded + 1;
      }

      return string;
    }

    function getGlobalObject() {
      if (typeof self !== 'undefined') {
        return self;
      }

      if (typeof global !== 'undefined') {
        return global;
      } else {
        return window;
      }
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    /** An implementation of an interval tree, following the explanation.
     * from CLR.
     *
     * Public interface:
     *   Constructor  IntervalTree
     *   Insertion    insert
     *   Search       findOverlapping
     */
    var BLACK = 1;
    var RED = 2;
    var NIL = {};
    NIL.color = BLACK;
    NIL.parent = NIL;
    NIL.left = NIL;
    NIL.right = NIL;

    class IntervalTree {
      constructor() {
        this.root = NIL;
      }

      insert(start, end, value) {
        var interval = new Interval(start, end, value);
        var x = new Node(interval);
        this.treeInsert(x);
        x.color = RED;

        while (x !== this.root && x.parent.color === RED) {
          if (x.parent === x.parent.parent.left) {
            let y = x.parent.parent.right;

            if (y.color === RED) {
              x.parent.color = BLACK;
              y.color = BLACK;
              x.parent.parent.color = RED;
              x = x.parent.parent;
            } else {
              if (x === x.parent.right) {
                x = x.parent;
                leftRotate.call(this, x);
              }

              x.parent.color = BLACK;
              x.parent.parent.color = RED;
              rightRotate.call(this, x.parent.parent);
            }
          } else {
            let y = x.parent.parent.left;

            if (y.color === RED) {
              x.parent.color = BLACK;
              y.color = BLACK;
              x.parent.parent.color = RED;
              x = x.parent.parent;
            } else {
              if (x === x.parent.left) {
                x = x.parent;
                rightRotate.call(this, x);
              }

              x.parent.color = BLACK;
              x.parent.parent.color = RED;
              leftRotate.call(this, x.parent.parent);
            }
          }
        }

        this.root.color = BLACK;
      }
      /**
       *
       * @param start - query interval
       * @param end - query interval
       * @returns Array of all intervals overlapping the query region
       */


      findOverlapping(start, end) {
        var searchInterval = new Interval(start, end, 0);
        if (this.root === NIL) return [];
        var intervals = searchAll.call(this, searchInterval, this.root, []);

        if (intervals.length > 1) {
          intervals.sort(function (i1, i2) {
            return i1.low - i2.low;
          });
        }

        return intervals;
      }
      /**
       * Dump info on intervals to console.  For debugging.
       */


      logIntervals() {
        logNode(this.root, 0);

        function logNode(node, indent) {
          var space = "";

          for (var i = 0; i < indent; i++) space += " ";

          console.log(space + node.interval.low + " " + node.interval.high); // + " " + (node.interval.value ? node.interval.value : " null"));

          indent += 5;
          if (node.left !== NIL) logNode(node.left, indent);
          if (node.right !== NIL) logNode(node.right, indent);
        }
      }

      mapIntervals(func) {
        applyInterval(this.root);

        function applyInterval(node) {
          func(node.interval);
          if (node.left !== NIL) applyInterval(node.left);
          if (node.right !== NIL) applyInterval(node.right);
        }
      }
      /**
       * Note:  Does not maintain RB constraints,  this is done post insert
       *
       * @param x  a Node
       */


      treeInsert(x) {
        var node = this.root;
        var y = NIL;

        while (node !== NIL) {
          y = node;

          if (x.interval.low <= node.interval.low) {
            node = node.left;
          } else {
            node = node.right;
          }
        }

        x.parent = y;

        if (y === NIL) {
          this.root = x;
          x.left = x.right = NIL;
        } else {
          if (x.interval.low <= y.interval.low) {
            y.left = x;
          } else {
            y.right = x;
          }
        }

        applyUpdate.call(this, x);
      }

    }

    function searchAll(interval, node, results) {
      if (node.interval.overlaps(interval)) {
        results.push(node.interval);
      }

      if (node.left !== NIL && node.left.max >= interval.low) {
        searchAll.call(this, interval, node.left, results);
      }

      if (node.right !== NIL && node.right.min <= interval.high) {
        searchAll.call(this, interval, node.right, results);
      }

      return results;
    }

    function leftRotate(x) {
      var y = x.right;
      x.right = y.left;

      if (y.left !== NIL) {
        y.left.parent = x;
      }

      y.parent = x.parent;

      if (x.parent === NIL) {
        this.root = y;
      } else {
        if (x.parent.left === x) {
          x.parent.left = y;
        } else {
          x.parent.right = y;
        }
      }

      y.left = x;
      x.parent = y;
      applyUpdate.call(this, x); // no need to apply update on y, since it'll y is an ancestor
      // of x, and will be touched by applyUpdate().
    }

    function rightRotate(x) {
      var y = x.left;
      x.left = y.right;

      if (y.right !== NIL) {
        y.right.parent = x;
      }

      y.parent = x.parent;

      if (x.parent === NIL) {
        this.root = y;
      } else {
        if (x.parent.right === x) {
          x.parent.right = y;
        } else {
          x.parent.left = y;
        }
      }

      y.right = x;
      x.parent = y;
      applyUpdate.call(this, x); // no need to apply update on y, since it'll y is an ancestor
      // of x, and will be touched by applyUpdate().
    } // Applies the statistic update on the node and its ancestors.


    function applyUpdate(node) {
      while (node !== NIL) {
        var nodeMax = node.left.max > node.right.max ? node.left.max : node.right.max;
        var intervalHigh = node.interval.high;
        node.max = nodeMax > intervalHigh ? nodeMax : intervalHigh;
        var nodeMin = node.left.min < node.right.min ? node.left.min : node.right.min;
        var intervalLow = node.interval.low;
        node.min = nodeMin < intervalLow ? nodeMin : intervalLow;
        node = node.parent;
      }
    }

    class Interval {
      constructor(low, high, value) {
        this.low = low;
        this.high = high;
        this.value = value;
      }

      equals(other) {
        if (!other) {
          return false;
        }

        if (this === other) {
          return true;
        }

        return this.low === other.low && this.high === other.high;
      }

      compareTo(other) {
        if (this.low < other.low) return -1;
        if (this.low > other.low) return 1;
        if (this.high < other.high) return -1;
        if (this.high > other.high) return 1;
        return 0;
      }
      /**
       * Returns true if this interval overlaps the other.
       */


      overlaps(other) {
        return this.low <= other.high && other.low <= this.high;
      }

    }

    function Node(interval) {
      this.parent = NIL;
      this.left = NIL;
      this.right = NIL;
      this.interval = interval;
      this.color = RED;
    } //

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    /**
     * Object for caching lists of features.  Supports effecient queries for sub-range  (chr, start, end)
     *
     * @param featureList
     * @param The genomic range spanned by featureList (optional)
     * @constructor
     */

    class FeatureCache$1 {
      constructor(featureList, genome, range) {
        featureList = featureList || [];
        this.treeMap = this.buildTreeMap(featureList, genome);
        this.range = range;
        this.count = featureList.length;
      }

      containsRange(genomicRange) {
        // No range means cache contains all features
        return this.range === undefined || this.range.contains(genomicRange.chr, genomicRange.start, genomicRange.end);
      }

      queryFeatures(chr, start, end) {
        const tree = this.treeMap[chr];
        if (!tree) return [];
        const intervals = tree.findOverlapping(start, end);

        if (intervals.length === 0) {
          return [];
        } else {
          // Trim the list of features in the intervals to those
          // overlapping the requested range.
          // Assumption: features are sorted by start position
          const featureList = [];
          const all = this.allFeatures[chr];

          if (all) {
            for (let interval of intervals) {
              const indexRange = interval.value;

              for (let i = indexRange.start; i < indexRange.end; i++) {
                let feature = all[i];
                if (feature.start > end) break;else if (feature.end >= start) {
                  featureList.push(feature);
                }
              }
            }

            featureList.sort(function (a, b) {
              return a.start - b.start;
            });
          }

          return featureList;
        }
      }

      /**
       * Returns all features, unsorted.
       *
       * @returns {Array}
       */
      getAllFeatures() {
        return this.allFeatures;
      }

      buildTreeMap(featureList, genome) {
        const treeMap = {};
        const chromosomes = [];
        this.allFeatures = {};

        if (featureList) {
          for (let feature of featureList) {
            let chr = feature.chr; // Translate to "official" name

            if (genome) {
              chr = genome.getChromosomeName(chr);
            }

            let geneList = this.allFeatures[chr];

            if (!geneList) {
              chromosomes.push(chr);
              geneList = [];
              this.allFeatures[chr] = geneList;
            }

            geneList.push(feature);
          } // Now build interval tree for each chromosome


          for (let chr of chromosomes) {
            const chrFeatures = this.allFeatures[chr];
            chrFeatures.sort(function (f1, f2) {
              return f1.start === f2.start ? 0 : f1.start > f2.start ? 1 : -1;
            });
            treeMap[chr] = buildIntervalTree$1(chrFeatures);
          }
        }

        return treeMap;
      }

    }
    /**
     * Build an interval tree from the feature list for fast interval based queries.   We lump features in groups
     * of 10, or total size / 100,   to reduce size of the tree.
     *
     * @param featureList
     */


    function buildIntervalTree$1(featureList) {
      const tree = new IntervalTree();
      const len = featureList.length;
      const chunkSize = Math.max(10, Math.round(len / 10));

      for (let i = 0; i < len; i += chunkSize) {
        const e = Math.min(len, i + chunkSize);
        const subArray = new IndexRange(i, e); //featureList.slice(i, e);

        const iStart = featureList[i].start; //

        let iEnd = iStart;

        for (let j = i; j < e; j++) {
          iEnd = Math.max(iEnd, featureList[j].end);
        }

        tree.insert(iStart, iEnd, subArray);
      }

      return tree;
    }

    class IndexRange {
      constructor(start, end) {
        this.start = start;
        this.end = end;
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const FeatureUtils = {
      packFeatures: function (features, maxRows, sorted) {
        var start;
        var end;
        if (!features) return;
        maxRows = maxRows || 10000;

        if (!sorted) {
          features.sort(function (a, b) {
            return a.start - b.start;
          });
        }

        if (features.length === 0) {
          return [];
        } else {
          var bucketList = [],
              allocatedCount = 0,
              lastAllocatedCount = 0,
              nextStart,
              row,
              index,
              bucket,
              feature,
              gap = 2,
              bucketStart;
          start = features[0].start;
          end = features[features.length - 1].start;
          bucketStart = Math.max(start, features[0].start);
          nextStart = bucketStart;
          features.forEach(function (alignment) {
            var buckListIndex = Math.max(0, alignment.start - bucketStart);

            if (bucketList[buckListIndex] === undefined) {
              bucketList[buckListIndex] = [];
            }

            bucketList[buckListIndex].push(alignment);
          });
          row = 0;

          while (allocatedCount < features.length && row <= maxRows) {
            while (nextStart <= end) {
              bucket = undefined;

              while (!bucket && nextStart <= end) {
                index = nextStart - bucketStart;

                if (bucketList[index] === undefined) {
                  ++nextStart; // No buckets at this index
                } else {
                  bucket = bucketList[index];
                }
              } // while (bucket)


              if (!bucket) {
                break;
              }

              feature = bucket.pop();

              if (0 === bucket.length) {
                bucketList[index] = undefined;
              }

              feature.row = row;
              nextStart = feature.end + gap;
              ++allocatedCount;
            } // while (nextStart)


            row++;
            nextStart = bucketStart;
            if (allocatedCount === lastAllocatedCount) break; // Protect from infinite loops

            lastAllocatedCount = allocatedCount;
          } // while (allocatedCount)

        }
      },

      /**
       * Find features overlapping the given interval.  It is assumed that all features share the same chromosome.
       *
       * TODO -- significant overlap with FeatureCache, refactor to combine
       *
       * @param featureList
       * @param start
       * @param end
       */
      findOverlapping: function (featureList, start, end) {
        if (!featureList || featureList.length === 0) {
          return [];
        } else {
          const tree = buildIntervalTree(featureList);
          const intervals = tree.findOverlapping(start, end);

          if (intervals.length === 0) {
            return [];
          } else {
            // Trim the list of features in the intervals to those
            // overlapping the requested range.
            // Assumption: features are sorted by start position
            featureList = [];
            intervals.forEach(function (interval) {
              const intervalFeatures = interval.value;
              const len = intervalFeatures.length;

              for (let i = 0; i < len; i++) {
                const feature = intervalFeatures[i];
                if (feature.start > end) break;else if (feature.end > start) {
                  featureList.push(feature);
                }
              }
            });
            featureList.sort(function (a, b) {
              return a.start - b.start;
            });
            return featureList;
          }
        }
      }
    };
    /**
     * Build an interval tree from the feature list for fast interval based queries.   We lump features in groups
     * of 10, or total size / 100,   to reduce size of the tree.
     *
     * @param featureList
     */

    function buildIntervalTree(featureList) {
      const tree = new IntervalTree();
      const len = featureList.length;
      const chunkSize = Math.max(10, Math.round(len / 100));
      featureList.sort(function (f1, f2) {
        return f1.start === f2.start ? 0 : f1.start > f2.start ? 1 : -1;
      });

      for (let i = 0; i < len; i += chunkSize) {
        const e = Math.min(len, i + chunkSize);
        const subArray = featureList.slice(i, e);
        const iStart = subArray[0].start;
        let iEnd = iStart;
        subArray.forEach(function (feature) {
          iEnd = Math.max(iEnd, feature.end);
        });
        tree.insert(iStart, iEnd, subArray);
      }

      return tree;
    }

    function createCheckbox$1(name, initialState) {
      const container = div$1({
        class: 'igv-menu-popup-check-container'
      });
      const div = div$1();
      container.appendChild(div);
      const svg = createIcon$1('check', true === initialState ? '#444' : 'transparent');
      div.appendChild(svg);
      const label = div$1();
      label.innerText = name;
      container.appendChild(label);
      return container;
    }

    /**
     * Configure item list for track "gear" menu.
     * @param trackView
     */

    const MenuUtils = {
      trackMenuItemList: function (trackView) {
        const vizWindowTypes = new Set(['alignment', 'annotation', 'variant', 'eqtl', 'snp']);
        const hasVizWindow = trackView.track.config && trackView.track.config.visibilityWindow !== undefined;
        let menuItems = [];

        if (trackView.track.config.type !== 'sequence') {
          menuItems.push(trackRenameMenuItem(trackView));
          menuItems.push(trackHeightMenuItem(trackView));
        }

        if (this.showColorPicker(trackView.track)) {
          menuItems.push('<hr/>');
          menuItems.push(colorPickerMenuItem({
            trackView,
            label: "Set track color",
            option: "color"
          }));
          menuItems.push(unsetColorMenuItem({
            trackView,
            label: "Unset track color"
          }));
          menuItems.push(colorPickerMenuItem({
            trackView,
            label: "Set alt color",
            option: "altColor"
          }));
        }

        if (trackView.track.menuItemList) {
          menuItems = menuItems.concat(trackView.track.menuItemList());
        }

        if (hasVizWindow || vizWindowTypes.has(trackView.track.type)) {
          menuItems.push('<hr/>');
          menuItems.push(visibilityWindowMenuItem(trackView));
        }

        if (trackView.track.removable !== false) {
          menuItems.push('<hr/>');
          menuItems.push(trackRemovalMenuItem(trackView));
        }

        return menuItems;
      },
      numericDataMenuItems: function (trackView) {
        const menuItems = [];
        menuItems.push('<hr/>'); // Data range

        const object = $$1('<div>');
        object.text('Set data range');

        const click = () => {
          trackView.browser.dataRangeDialog.configure(trackView);
          trackView.browser.dataRangeDialog.present($$1(trackView.browser.columnContainer));
        };

        menuItems.push({
          object,
          click
        });

        if (trackView.track.logScale !== undefined) {
          menuItems.push({
            object: $$1(createCheckbox$1("Log scale", trackView.track.logScale)),
            click: () => {
              trackView.track.logScale = !trackView.track.logScale;
              trackView.repaintViews();
            }
          });
        }

        menuItems.push({
          object: $$1(createCheckbox$1("Autoscale", trackView.track.autoscale)),
          click: () => {
            trackView.track.autoscale = !trackView.track.autoscale;
            trackView.updateViews();
          }
        });
        return menuItems;
      },
      trackMenuItemListHelper: function (itemList, menuPopup) {
        var list = [];

        if (itemList.length > 0) {
          list = itemList.map(function (item, i) {
            var $e; // name and object fields checked for backward compatibility

            if (item.name) {
              $e = $$1('<div>');
              $e.text(item.name);
            } else if (item.object) {
              $e = item.object;
            } else if (typeof item.label === 'string') {
              $e = $$1('<div>');
              $e.html(item.label);
            } else if (typeof item === 'string') {
              if (item.startsWith("<")) {
                $e = $$1(item);
              } else {
                $e = $$1("<div>" + item + "</div>");
              }
            }

            if (0 === i) {
              $e.addClass('igv-track-menu-border-top');
            }

            if (item.click) {
              $e.on('click', handleClick);
              $e.on('touchend', function (e) {
                handleClick(e);
              });
              $e.on('mouseup', function (e) {
                e.preventDefault();
                e.stopPropagation();
              }); // eslint-disable-next-line no-inner-declarations

              function handleClick(e) {
                item.click(e);
                menuPopup.hide();
                e.preventDefault();
                e.stopPropagation();
              }
            }

            return {
              object: $e,
              init: item.init || undefined
            };
          });
        }

        return list;
      },

      showColorPicker(track) {
        return undefined === track.type || "bedtype" === track.type || "alignment" === track.type || "annotation" === track.type || "variant" === track.type || "wig" === track.type || 'interact' === track.type;
      },

      createMenuItem(label, action) {
        const object = $$1('<div>');
        object.text(label);
        return {
          object,
          click: action
        };
      }

    };

    function visibilityWindowMenuItem(trackView) {
      const click = e => {
        const callback = () => {
          let value = trackView.browser.inputDialog.input.value;
          value = '' === value || undefined === value ? -1 : value.trim();
          trackView.track.visibilityWindow = Number.parseInt(value);
          trackView.track.config.visibilityWindow = Number.parseInt(value);
          trackView.updateViews();
        };

        const config = {
          label: 'Visibility Window',
          value: trackView.track.visibilityWindow,
          callback
        };
        trackView.browser.inputDialog.present(config, e);
      };

      const object = $$1('<div>');
      object.text('Set visibility window');
      return {
        object,
        click
      };
    }

    function trackRemovalMenuItem(trackView) {
      const object = $$1('<div>');
      object.text('Remove track');
      return {
        object,
        click: () => trackView.browser.removeTrack(trackView.track)
      };
    }

    function colorPickerMenuItem(_ref) {
      let {
        trackView,
        label,
        option
      } = _ref;
      const $e = $$1('<div>');
      $e.text(label);
      return {
        object: $e,
        click: () => trackView.presentColorPicker(option)
      };
    }

    function unsetColorMenuItem(_ref2) {
      let {
        trackView,
        label
      } = _ref2;
      const $e = $$1('<div>');
      $e.text(label);
      return {
        object: $e,
        click: () => {
          trackView.track.color = undefined;
          trackView.repaintViews();
        }
      };
    }

    function trackRenameMenuItem(trackView) {
      const click = e => {
        const callback = function () {
          let value = trackView.browser.inputDialog.input.value;
          value = '' === value || undefined === value ? 'untitled' : value.trim();
          trackView.track.name = value;
        };

        const config = {
          label: 'Track Name',
          value: getTrackLabelText(trackView.track) || 'unnamed',
          callback
        };
        trackView.browser.inputDialog.present(config, e);
      };

      const object = $$1('<div>');
      object.text('Set track name');
      return {
        object,
        click
      };
    }

    function trackHeightMenuItem(trackView) {
      const click = e => {
        const callback = () => {
          const number = Number(trackView.browser.inputDialog.input.value, 10);

          if (undefined !== number) {
            // If explicitly setting the height adust min or max, if neccessary.
            if (trackView.track.minHeight !== undefined && trackView.track.minHeight > number) {
              trackView.track.minHeight = number;
            }

            if (trackView.track.maxHeight !== undefined && trackView.track.maxHeight < number) {
              trackView.track.minHeight = number;
            }

            trackView.setTrackHeight(number, true);
            trackView.checkContentHeight();
            trackView.repaintViews(); // Explicitly setting track height turns off autoHeight

            trackView.track.autoHeight = false;
          }
        };

        const config = {
          label: 'Track Height',
          value: trackView.track.height,
          callback
        };
        trackView.browser.inputDialog.present(config, e);
      };

      const object = $$1('<div>');
      object.text('Set track height');
      return {
        object,
        click
      };
    }

    function getTrackLabelText(track) {
      var vp, txt;
      vp = track.trackView.viewports[0];
      txt = vp.$trackLabel.text();
      return txt;
    }

    function div(options) {
      return create("div", options);
    }

    function create(tag, options) {
      const elem = document.createElement(tag);

      if (options) {
        if (options.class) {
          elem.classList.add(options.class);
        }

        if (options.id) {
          elem.id = options.id;
        }

        if (options.style) {
          applyStyle(elem, options.style);
        }
      }

      return elem;
    }

    function hide(elem) {
      const cssStyle = getComputedStyle(elem);

      if (cssStyle.display !== "none") {
        elem._initialDisplay = cssStyle.display;
      }

      elem.style.display = "none";
    }

    function show(elem) {
      const currentDisplay = getComputedStyle(elem).display;

      if (currentDisplay === "none") {
        const d = elem._initialDisplay || "block";
        elem.style.display = d;
      }
    }

    function offset(elem) {
      // Return zeros for disconnected and hidden (display: none) elements (gh-2310)
      // Support: IE <=11 only
      // Running getBoundingClientRect on a
      // disconnected node in IE throws an error
      if (!elem.getClientRects().length) {
        return {
          top: 0,
          left: 0
        };
      } // Get document-relative position by adding viewport scroll to viewport-relative gBCR


      const rect = elem.getBoundingClientRect();
      const win = elem.ownerDocument.defaultView;
      return {
        top: rect.top + win.pageYOffset,
        left: rect.left + win.pageXOffset
      };
    }

    function pageCoordinates(e) {
      if (e.type.startsWith("touch")) {
        const touch = e.touches[0];
        return {
          x: touch.pageX,
          y: touch.pageY
        };
      } else {
        return {
          x: e.pageX,
          y: e.pageY
        };
      }
    }

    function applyStyle(elem, style) {
      for (let key of Object.keys(style)) {
        elem.style[key] = style[key];
      }
    }

    let getMouseXY = (domElement, {
      clientX,
      clientY
    }) => {
      // DOMRect object with eight properties: left, top, right, bottom, x, y, width, height
      const {
        left,
        top,
        width,
        height
      } = domElement.getBoundingClientRect();
      const x = clientX - left;
      const y = clientY - top;
      return {
        x,
        y,
        xNormalized: x / width,
        yNormalized: y / height,
        width,
        height
      };
    };
    /**
     * Translate the mouse coordinates for the event to the coordinates for the given target element
     * @param event
     * @param domElement
     * @returns {{x: number, y: number}}
     */


    function translateMouseCoordinates(event, domElement) {
      const {
        clientX,
        clientY
      } = event;
      return getMouseXY(domElement, {
        clientX,
        clientY
      });
    }

    function createCheckbox(name, initialState) {
      const container = div({
        class: 'igv-ui-trackgear-popover-check-container'
      });
      const svg = iconMarkup('check', true === initialState ? '#444' : 'transparent');
      svg.style.borderColor = 'gray';
      svg.style.borderWidth = '1px';
      svg.style.borderStyle = 'solid';
      container.appendChild(svg);
      let label = div(); //{ class: 'igv-some-label-class' });

      label.textContent = name;
      container.appendChild(label);
      return container;
    }

    function createIcon(name, color) {
      return iconMarkup(name, color);
    }

    function iconMarkup(name, color) {
      color = color || "currentColor";
      let icon = icons[name];

      if (!icon) {
        console.error(`No icon named: ${name}`);
        icon = icons["question"];
      }

      const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
      svg.setAttributeNS(null, 'viewBox', '0 0 ' + icon[0] + ' ' + icon[1]);
      const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
      path.setAttributeNS(null, 'fill', color);
      path.setAttributeNS(null, 'd', icon[4]);
      svg.appendChild(path);
      return svg;
    }

    const icons = {
      "check": [512, 512, [], "f00c", "M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"],
      "cog": [512, 512, [], "f013", "M444.788 291.1l42.616 24.599c4.867 2.809 7.126 8.618 5.459 13.985-11.07 35.642-29.97 67.842-54.689 94.586a12.016 12.016 0 0 1-14.832 2.254l-42.584-24.595a191.577 191.577 0 0 1-60.759 35.13v49.182a12.01 12.01 0 0 1-9.377 11.718c-34.956 7.85-72.499 8.256-109.219.007-5.49-1.233-9.403-6.096-9.403-11.723v-49.184a191.555 191.555 0 0 1-60.759-35.13l-42.584 24.595a12.016 12.016 0 0 1-14.832-2.254c-24.718-26.744-43.619-58.944-54.689-94.586-1.667-5.366.592-11.175 5.459-13.985L67.212 291.1a193.48 193.48 0 0 1 0-70.199l-42.616-24.599c-4.867-2.809-7.126-8.618-5.459-13.985 11.07-35.642 29.97-67.842 54.689-94.586a12.016 12.016 0 0 1 14.832-2.254l42.584 24.595a191.577 191.577 0 0 1 60.759-35.13V25.759a12.01 12.01 0 0 1 9.377-11.718c34.956-7.85 72.499-8.256 109.219-.007 5.49 1.233 9.403 6.096 9.403 11.723v49.184a191.555 191.555 0 0 1 60.759 35.13l42.584-24.595a12.016 12.016 0 0 1 14.832 2.254c24.718 26.744 43.619 58.944 54.689 94.586 1.667 5.366-.592 11.175-5.459 13.985L444.788 220.9a193.485 193.485 0 0 1 0 70.2zM336 256c0-44.112-35.888-80-80-80s-80 35.888-80 80 35.888 80 80 80 80-35.888 80-80z"],
      "exclamation": [192, 512, [], "f12a", "M176 432c0 44.112-35.888 80-80 80s-80-35.888-80-80 35.888-80 80-80 80 35.888 80 80zM25.26 25.199l13.6 272C39.499 309.972 50.041 320 62.83 320h66.34c12.789 0 23.331-10.028 23.97-22.801l13.6-272C167.425 11.49 156.496 0 142.77 0H49.23C35.504 0 24.575 11.49 25.26 25.199z"],
      "exclamation-circle": [512, 512, [], "f06a", "M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"],
      "exclamation-triangle": [576, 512, [], "f071", "M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"],
      "minus": [448, 512, [], "f068", "M424 318.2c13.3 0 24-10.7 24-24v-76.4c0-13.3-10.7-24-24-24H24c-13.3 0-24 10.7-24 24v76.4c0 13.3 10.7 24 24 24h400z"],
      "minus-circle": [512, 512, [], "f056", "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zM124 296c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h264c6.6 0 12 5.4 12 12v56c0 6.6-5.4 12-12 12H124z"],
      "minus-square": [448, 512, [], "f146", "M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zM92 296c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h264c6.6 0 12 5.4 12 12v56c0 6.6-5.4 12-12 12H92z"],
      "plus": [448, 512, [], "f067", "M448 294.2v-76.4c0-13.3-10.7-24-24-24H286.2V56c0-13.3-10.7-24-24-24h-76.4c-13.3 0-24 10.7-24 24v137.8H24c-13.3 0-24 10.7-24 24v76.4c0 13.3 10.7 24 24 24h137.8V456c0 13.3 10.7 24 24 24h76.4c13.3 0 24-10.7 24-24V318.2H424c13.3 0 24-10.7 24-24z"],
      "plus-circle": [512, 512, [], "f055", "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm144 276c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92h-92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"],
      "plus-square": [448, 512, [], "f0fe", "M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-32 252c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92H92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"],
      "question": [384, 512, [], "f128", "M202.021 0C122.202 0 70.503 32.703 29.914 91.026c-7.363 10.58-5.093 25.086 5.178 32.874l43.138 32.709c10.373 7.865 25.132 6.026 33.253-4.148 25.049-31.381 43.63-49.449 82.757-49.449 30.764 0 68.816 19.799 68.816 49.631 0 22.552-18.617 34.134-48.993 51.164-35.423 19.86-82.299 44.576-82.299 106.405V320c0 13.255 10.745 24 24 24h72.471c13.255 0 24-10.745 24-24v-5.773c0-42.86 125.268-44.645 125.268-160.627C377.504 66.256 286.902 0 202.021 0zM192 373.459c-38.196 0-69.271 31.075-69.271 69.271 0 38.195 31.075 69.27 69.271 69.27s69.271-31.075 69.271-69.271-31.075-69.27-69.271-69.27z"],
      "save": [448, 512, [], "f0c7", "M433.941 129.941l-83.882-83.882A48 48 0 0 0 316.118 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V163.882a48 48 0 0 0-14.059-33.941zM224 416c-35.346 0-64-28.654-64-64 0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64zm96-304.52V212c0 6.627-5.373 12-12 12H76c-6.627 0-12-5.373-12-12V108c0-6.627 5.373-12 12-12h228.52c3.183 0 6.235 1.264 8.485 3.515l3.48 3.48A11.996 11.996 0 0 1 320 111.48z"],
      "search": [512, 512, [], "f002", "M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"],
      "share": [512, 512, [], "f064", "M503.691 189.836L327.687 37.851C312.281 24.546 288 35.347 288 56.015v80.053C127.371 137.907 0 170.1 0 322.326c0 61.441 39.581 122.309 83.333 154.132 13.653 9.931 33.111-2.533 28.077-18.631C66.066 312.814 132.917 274.316 288 272.085V360c0 20.7 24.3 31.453 39.687 18.164l176.004-152c11.071-9.562 11.086-26.753 0-36.328z"],
      "spinner": [512, 512, [], "f110", "M304 48c0 26.51-21.49 48-48 48s-48-21.49-48-48 21.49-48 48-48 48 21.49 48 48zm-48 368c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zm208-208c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zM96 256c0-26.51-21.49-48-48-48S0 229.49 0 256s21.49 48 48 48 48-21.49 48-48zm12.922 99.078c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.491-48-48-48zm294.156 0c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.49-48-48-48zM108.922 60.922c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.491-48-48-48z"],
      "square": [448, 512, [], "f0c8", "M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48z"],
      "square-full": [512, 512, [], "f45c", "M512 512H0V0h512v512z"],
      "times": [384, 512, [], "f00d", "M323.1 441l53.9-53.9c9.4-9.4 9.4-24.5 0-33.9L279.8 256l97.2-97.2c9.4-9.4 9.4-24.5 0-33.9L323.1 71c-9.4-9.4-24.5-9.4-33.9 0L192 168.2 94.8 71c-9.4-9.4-24.5-9.4-33.9 0L7 124.9c-9.4 9.4-9.4 24.5 0 33.9l97.2 97.2L7 353.2c-9.4 9.4-9.4 24.5 0 33.9L60.9 441c9.4 9.4 24.5 9.4 33.9 0l97.2-97.2 97.2 97.2c9.3 9.3 24.5 9.3 33.9 0z"],
      "times-circle": [512, 512, [], "f057", "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"],
      "wrench": [512, 512, [], "f0ad", "M481.156 200c9.3 0 15.12 10.155 10.325 18.124C466.295 259.992 420.419 288 368 288c-79.222 0-143.501-63.974-143.997-143.079C223.505 65.469 288.548-.001 368.002 0c52.362.001 98.196 27.949 123.4 69.743C496.24 77.766 490.523 88 481.154 88H376l-40 56 40 56h105.156zm-171.649 93.003L109.255 493.255c-24.994 24.993-65.515 24.994-90.51 0-24.993-24.994-24.993-65.516 0-90.51L218.991 202.5c16.16 41.197 49.303 74.335 90.516 90.503zM104 432c0-13.255-10.745-24-24-24s-24 10.745-24 24 10.745 24 24 24 24-10.745 24-24z"]
    };

    function attachDialogCloseHandlerWithParent(parent, closeHandler) {
      var container = document.createElement("div");
      parent.appendChild(container);
      container.appendChild(createIcon("times"));
      container.addEventListener('click', function (e) {
        e.preventDefault();
        e.stopPropagation();
        closeHandler();
      });
    }
    /**
     * Make the target element movable by clicking and dragging on the handle.  This is not a general purprose function,
     * it makes several options specific to igv dialogs, the primary one being that the
     * target is absolutely positioned in pixel coordinates

     */


    let dragData; // Its assumed we are only dragging one element at a time.

    function makeDraggable(target, handle) {
      handle.addEventListener('mousedown', dragStart.bind(target));
    }

    function dragStart(event) {
      event.stopPropagation();
      event.preventDefault();
      offset(this);
      const dragFunction = drag.bind(this);
      const dragEndFunction = dragEnd.bind(this);
      const computedStyle = getComputedStyle(this);
      const top = parseInt(computedStyle.top.replace("px", ""));
      const left = parseInt(computedStyle.left.replace("px", ""));
      dragData = {
        dragFunction: dragFunction,
        dragEndFunction: dragEndFunction,
        screenX: event.screenX,
        screenY: event.screenY,
        top: top,
        left: left
      };
      document.addEventListener('mousemove', dragFunction);
      document.addEventListener('mouseup', dragEndFunction);
      document.addEventListener('mouseleave', dragEndFunction);
      document.addEventListener('mouseexit', dragEndFunction);
    }

    function drag(event) {
      if (!dragData) {
        console.log("No drag data!");
        return;
      }

      event.stopPropagation();
      event.preventDefault();
      const dx = event.screenX - dragData.screenX;
      const dy = event.screenY - dragData.screenY;
      this.style.left = `${dragData.left + dx}px`;
      this.style.top = `${dragData.top + dy}px`;
    }

    function dragEnd(event) {
      if (!dragData) {
        console.log("No drag data!");
        return;
      }

      event.stopPropagation();
      event.preventDefault();
      const dragFunction = dragData.dragFunction;
      const dragEndFunction = dragData.dragEndFunction;
      document.removeEventListener('mousemove', dragFunction);
      document.removeEventListener('mouseup', dragEndFunction);
      document.removeEventListener('mouseleave', dragEndFunction);
      document.removeEventListener('mouseexit', dragEndFunction);
      dragData = undefined;
    }

    const httpMessages = {
      "401": "Access unauthorized",
      "403": "Access forbidden",
      "404": "Not found"
    };

    class AlertDialog {
      /**
       * Initialize a new alert dialog
       * @param parent
       * @param alertProps - Optional - properties such as scroll to error
       */
      constructor(parent, alertProps) {
        this.alertProps = Object.assign({
          /** When an alert is presented - focus occur */
          shouldFocus: true,

          /** When focus occur - scroll into that element in the view */
          preventScroll: false
        }, alertProps); // container

        this.container = div({
          class: "igv-ui-alert-dialog-container"
        });
        parent.appendChild(this.container);
        this.container.setAttribute('tabIndex', '-1'); // header

        const header = div();
        this.container.appendChild(header);
        this.errorHeadline = div();
        header.appendChild(this.errorHeadline);
        this.errorHeadline.textContent = ''; // body container

        let bodyContainer = div({
          class: 'igv-ui-alert-dialog-body'
        });
        this.container.appendChild(bodyContainer); // body copy

        this.body = div({
          class: 'igv-ui-alert-dialog-body-copy'
        });
        bodyContainer.appendChild(this.body); // ok container

        let ok_container = div();
        this.container.appendChild(ok_container); // ok

        this.ok = div();
        ok_container.appendChild(this.ok);
        this.ok.textContent = 'OK';

        const okHandler = () => {
          if (typeof this.callback === 'function') {
            this.callback("OK");
            this.callback = undefined;
          }

          this.body.innerHTML = '';
          hide(this.container);
        };

        this.ok.addEventListener('click', event => {
          event.stopPropagation();
          okHandler();
        });
        this.container.addEventListener('keypress', event => {
          event.stopPropagation();

          if ('Enter' === event.key) {
            okHandler();
          }
        });
        makeDraggable(this.container, header);
        hide(this.container);
      }

      present(alert, callback) {
        this.errorHeadline.textContent = alert.message ? 'ERROR' : '';
        let string = alert.message || alert;

        if (httpMessages.hasOwnProperty(string)) {
          string = httpMessages[string];
        }

        this.body.innerHTML = string;
        this.callback = callback;
        show(this.container);

        if (this.alertProps.shouldFocus) {
          this.container.focus({
            preventScroll: this.alertProps.preventScroll
          });
        }
      }

    }

    let alertDialog;
    const Alert = {
      init(root, config = {}) {
        alertDialog = new AlertDialog(root, config);
      },

      presentAlert(alert, callback) {
        if (!alertDialog) {
          this.init(document.body);
        }

        alertDialog.present(alert, callback);
      }

    };

    class InputDialog {
      constructor(parent) {
        this.parent = parent; // dialog container

        this.container = div({
          class: 'igv-ui-generic-dialog-container'
        });
        parent.appendChild(this.container); // const { x, y, width, height } = this.container.getBoundingClientRect();
        // console.log(`InputDialog - x ${ x } y ${ y } width ${ width } height ${ height }`)
        // dialog header

        const header = div({
          class: 'igv-ui-generic-dialog-header'
        });
        this.container.appendChild(header); // dialog label

        this.label = div({
          class: 'igv-ui-generic-dialog-one-liner'
        });
        this.container.appendChild(this.label);
        this.label.text = 'Unlabeled'; // input container

        this.input_container = div({
          class: 'igv-ui-generic-dialog-input'
        });
        this.container.appendChild(this.input_container); //

        this.input = document.createElement("input");
        this.input_container.appendChild(this.input); // ok | cancel

        const buttons = div({
          class: 'igv-ui-generic-dialog-ok-cancel'
        });
        this.container.appendChild(buttons); // ok

        this.ok = div();
        buttons.appendChild(this.ok);
        this.ok.textContent = 'OK'; // cancel

        this.cancel = div();
        buttons.appendChild(this.cancel);
        this.cancel.textContent = 'Cancel';
        hide(this.container);
        this.input.addEventListener('keyup', e => {
          if (13 === e.keyCode) {
            if (typeof this.callback === 'function') {
              this.callback(this.input.value);
              this.callback = undefined;
            }

            this.input.value = undefined;
            hide(this.container);
          }
        });
        this.ok.addEventListener('click', () => {
          if (typeof this.callback === 'function') {
            this.callback(this.input.value);
            this.callback = undefined;
          }

          this.input.value = undefined;
          hide(this.container);
        });

        const cancel = () => {
          this.input.value = '';
          hide(this.container);
        };

        this.cancel.addEventListener('click', cancel);
        attachDialogCloseHandlerWithParent(header, cancel);
        makeDraggable(this.container, header);
      }

      present(options, e) {
        this.label.textContent = options.label;
        this.input.value = options.value;
        this.callback = options.callback || options.click;
        show(this.container);
        const {
          x,
          y
        } = pageCoordinates(e);
        this.clampLocation(x, y);
      }

      clampLocation(pageX, pageY) {
        const {
          width: w,
          height: h
        } = this.container.getBoundingClientRect();
        const {
          x: px,
          y: py,
          width: pw,
          height: ph
        } = this.parent.getBoundingClientRect();
        const y = Math.min(Math.max(pageY, py), py + ph - h);
        const x = Math.min(Math.max(pageX, px), px + pw - w);
        this.container.style.left = `${x}px`;
        this.container.style.top = `${y}px`;
      }

    }

    const appleCrayonPalette = {
      licorice: "#000000",
      lead: "#1e1e1e",
      tungsten: "#3a3a3a",
      iron: "#545453",
      steel: "#6e6e6e",
      tin: "#878687",
      nickel: "#888787",
      aluminum: "#a09fa0",
      magnesium: "#b8b8b8",
      silver: "#d0d0d0",
      mercury: "#e8e8e8",
      snow: "#ffffff",
      //
      cayenne: "#891100",
      mocha: "#894800",
      aspargus: "#888501",
      fern: "#458401",
      clover: "#028401",
      moss: "#018448",
      teal: "#008688",
      ocean: "#004a88",
      midnight: "#001888",
      eggplant: "#491a88",
      plum: "#891e88",
      maroon: "#891648",
      //
      maraschino: "#ff2101",
      tangerine: "#ff8802",
      lemon: "#fffa03",
      lime: "#83f902",
      spring: "#05f802",
      seam_foam: "#03f987",
      turquoise: "#00fdff",
      aqua: "#008cff",
      blueberry: "#002eff",
      grape: "#8931ff",
      magenta: "#ff39ff",
      strawberry: "#ff2987",
      //
      salmon: "#ff726e",
      cantaloupe: "#ffce6e",
      banana: "#fffb6d",
      honeydew: "#cefa6e",
      flora: "#68f96e",
      spindrift: "#68fbd0",
      ice: "#68fdff",
      sky: "#6acfff",
      orchid: "#6e76ff",
      lavender: "#d278ff",
      bubblegum: "#ff7aff",
      carnation: "#ff7fd3"
    }; //import { DOMUtils, UIUtils, makeDraggable } from '../node_modules/igv-utils/src/index.js'

    class GenericContainer {
      constructor({
        parent,
        top,
        left,
        width,
        height,
        border,
        closeHandler
      }) {
        let container = div({
          class: 'igv-ui-generic-container'
        });
        parent.appendChild(container);
        hide(container);
        this.container = container;

        if (top !== undefined) {
          this.container.style.top = `${top}px`;
        }

        if (left !== undefined) {
          this.container.style.left = `${left}px`;
        }

        if (width !== undefined) {
          this.container.style.width = `${width}px`;
        }

        if (height !== undefined) {
          this.container.style.height = `${height}px`;
        }

        if (border) {
          this.container.style.border = border;
        } //
        // let bbox = parent.getBoundingClientRect();
        // this.origin = {x: bbox.x, y: bbox.y};
        // this.container.offset({left: this.origin.x, top: this.origin.y});
        // header


        const header = div();
        this.container.appendChild(header); // close button

        attachDialogCloseHandlerWithParent(header, e => {
          hide(this.container);

          if (typeof closeHandler === "function") {
            closeHandler(e);
          }
        });
        makeDraggable(this.container, header);
      }

      show() {
        show(this.container);
      }

      hide() {
        hide(this.container);
      }

      dispose() {
        if (this.container.parent) {
          this.container.parent.removeChild(this.container);
        }
      }

    }

    class ColorPicker extends GenericContainer {
      constructor({
        parent,
        top,
        left,
        width,
        height,
        defaultColors,
        colorHandler
      }) {
        super({
          parent,
          top,
          left,
          width,
          height,
          border: '1px solid gray'
        });
        createColorSwatchSelector(this.container, colorHandler, defaultColors);
      }

    }

    const createColorSwatchSelector = (container, colorHandler, defaultColors) => {
      const hexColorStrings = Object.values(appleCrayonPalette);

      for (let hexColorString of hexColorStrings) {
        const swatch = div({
          class: 'igv-ui-color-swatch'
        });
        container.appendChild(swatch);
        decorateSwatch(swatch, hexColorString, colorHandler);
      }

      if (defaultColors) {
        for (let hexColorString of defaultColors) {
          const swatch = div({
            class: 'igv-ui-color-swatch'
          });
          container.appendChild(swatch);
          decorateSwatch(swatch, hexColorString, colorHandler);
        }
      }
    };

    const decorateSwatch = (swatch, hexColorString, colorHandler) => {
      swatch.style.backgroundColor = hexColorString;
      swatch.addEventListener('mouseenter', e => swatch.style.borderColor = hexColorString);
      swatch.addEventListener('mouseleave', e => swatch.style.borderColor = 'white');
      swatch.addEventListener('click', event => {
        event.stopPropagation();
        colorHandler(hexColorString);
      });
      swatch.addEventListener('touchend', event => {
        event.stopPropagation();
        colorHandler(hexColorString);
      });
    };

    class Popover {
      constructor(parent, title) {
        this.parent = parent; // popover

        this.popover = div({
          class: "igv-ui-popover"
        });
        parent.appendChild(this.popover); // header

        const popoverHeader = div();
        this.popover.appendChild(popoverHeader);
        const titleElement = div();
        popoverHeader.appendChild(titleElement);

        if (title) {
          titleElement.textContent = title;
        }

        attachDialogCloseHandlerWithParent(popoverHeader, () => this.hide());
        makeDraggable(this.popover, popoverHeader); // content

        this.popoverContent = div();
        this.popover.appendChild(this.popoverContent);
        this.popover.style.display = 'none';
      }

      presentContentWithEvent(e, content) {
        this.popover.style.display = 'block';
        this.popoverContent.innerHTML = content;
        present$1(e, this.popover, this.popoverContent);
      }

      presentMenu(e, menuItems) {
        if (0 === menuItems.length) {
          return;
        }

        this.popover.style.display = 'block';
        const menuElements = createMenuElements$1(menuItems, this.popover);

        for (let item of menuElements) {
          this.popoverContent.appendChild(item.object);
        }

        present$1(e, this.popover, this.popoverContent);
      }

      hide() {
        this.popover.style.display = 'none';
        this.dispose();
      }

      dispose() {
        if (this.popover) {
          this.popover.parentNode.removeChild(this.popover);
        }

        const keys = Object.keys(this);

        for (let key of keys) {
          this[key] = undefined;
        }
      }

    }

    function present$1(e, popover, popoverContent) {
      const {
        x,
        y,
        width
      } = translateMouseCoordinates(e, popover.parentNode);
      popover.style.top = `${y}px`;
      const {
        width: w
      } = popover.getBoundingClientRect();
      const xmax = x + w;
      const delta = xmax - width;
      popover.style.left = `${xmax > width ? x - delta : x}px`;
      popoverContent.style.maxWidth = `${Math.min(w, width)}px`;
    }

    function createMenuElements$1(itemList, popover) {
      const list = itemList.map(function (item, i) {
        let elem;

        if (typeof item === 'string') {
          elem = div();
          elem.innerHTML = item;
        } else if (typeof item === 'Node') {
          elem = item;
        } else {
          if (typeof item.init === 'function') {
            item.init();
          }

          if ("checkbox" === item.type) {
            elem = createCheckbox("Show all bases", item.value);
          } else if ("color" === item.type) {
            const colorPicker = new ColorPicker({
              parent: popover.parentElement,
              width: 364,
              //defaultColor: 'aqua',
              colorHandler: color => item.click(color)
            });
            elem = div();

            if (typeof item.label === 'string') {
              elem.innerHTML = item.label;
            }

            const clickHandler = e => {
              colorPicker.show();
              hide(popover);
              e.preventDefault();
              e.stopPropagation();
            };

            elem.addEventListener('click', clickHandler);
            elem.addEventListener('touchend', clickHandler);
            elem.addEventListener('mouseup', function (e) {
              e.preventDefault();
              e.stopPropagation();
            });
          } else {
            elem = div();

            if (typeof item.label === 'string') {
              elem.innerHTML = item.label;
            }
          }

          if (item.click && "color" !== item.type) {
            elem.addEventListener('click', handleClick);
            elem.addEventListener('touchend', handleClick);
            elem.addEventListener('mouseup', function (e) {
              e.preventDefault();
              e.stopPropagation();
            }); // eslint-disable-next-line no-inner-declarations

            function handleClick(e) {
              item.click();
              hide(popover);
              e.preventDefault();
              e.stopPropagation();
            }
          }
        }

        return {
          object: elem,
          init: item.init
        };
      });
      return list;
    }

    function embedCSS$2() {
      var css = '.igv-ui-popover {\n  cursor: default;\n  position: absolute;\n  z-index: 2048;\n  border-color: #7F7F7F;\n  border-radius: 4px;\n  border-style: solid;\n  border-width: 1px;\n  font-family: \"Open Sans\", sans-serif;\n  font-size: small;\n  background-color: white; }\n  .igv-ui-popover > div:first-child {\n    display: flex;\n    flex-direction: row;\n    flex-wrap: nowrap;\n    justify-content: space-between;\n    align-items: center;\n    width: 100%;\n    height: 24px;\n    cursor: move;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n    border-bottom-color: #7F7F7F;\n    border-bottom-style: solid;\n    border-bottom-width: thin;\n    background-color: #eee; }\n    .igv-ui-popover > div:first-child > div:first-child {\n      margin-left: 4px; }\n    .igv-ui-popover > div:first-child > div:last-child {\n      margin-right: 4px;\n      height: 12px;\n      width: 12px;\n      color: #7F7F7F; }\n    .igv-ui-popover > div:first-child > div:last-child:hover {\n      cursor: pointer;\n      color: #444; }\n  .igv-ui-popover > div:last-child {\n    overflow-y: auto;\n    overflow-x: hidden;\n    max-height: 400px;\n    max-width: 800px;\n    background-color: white; }\n    .igv-ui-popover > div:last-child > div {\n      -webkit-user-select: text;\n      -moz-user-select: text;\n      -ms-user-select: text;\n      user-select: text;\n      margin-left: 4px;\n      margin-right: 4px;\n      min-width: 220px;\n      overflow-x: hidden;\n      text-overflow: ellipsis;\n      white-space: nowrap; }\n      .igv-ui-popover > div:last-child > div > span {\n        font-weight: bolder; }\n    .igv-ui-popover > div:last-child hr {\n      width: 100%; }\n\n.igv-ui-alert-dialog-container {\n  box-sizing: content-box;\n  position: absolute;\n  z-index: 2048;\n  top: 50%;\n  left: 50%;\n  width: 400px;\n  height: 200px;\n  border-color: #7F7F7F;\n  border-radius: 4px;\n  border-style: solid;\n  border-width: thin;\n  outline: none;\n  font-family: \"Open Sans\", sans-serif;\n  font-size: 15px;\n  font-weight: 400;\n  background-color: white;\n  display: flex;\n  flex-flow: column;\n  flex-wrap: nowrap;\n  justify-content: space-between;\n  align-items: center; }\n  .igv-ui-alert-dialog-container > div:first-child {\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: flex-start;\n    align-items: center;\n    width: 100%;\n    height: 24px;\n    cursor: move;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n    border-bottom-color: #7F7F7F;\n    border-bottom-style: solid;\n    border-bottom-width: thin;\n    background-color: #eee; }\n    .igv-ui-alert-dialog-container > div:first-child div:first-child {\n      padding-left: 8px; }\n  .igv-ui-alert-dialog-container .igv-ui-alert-dialog-body {\n    -webkit-user-select: text;\n    -moz-user-select: text;\n    -ms-user-select: text;\n    user-select: text;\n    color: #373737;\n    width: 100%;\n    height: calc(100% - 24px - 64px);\n    overflow-y: scroll; }\n    .igv-ui-alert-dialog-container .igv-ui-alert-dialog-body .igv-ui-alert-dialog-body-copy {\n      margin: 16px;\n      width: auto;\n      height: auto;\n      overflow-wrap: break-word;\n      word-break: break-word;\n      background-color: white;\n      border: unset; }\n  .igv-ui-alert-dialog-container > div:last-child {\n    width: 100%;\n    margin-bottom: 10px;\n    background-color: white;\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: center;\n    align-items: center; }\n    .igv-ui-alert-dialog-container > div:last-child div {\n      margin: unset;\n      width: 40px;\n      height: 30px;\n      line-height: 30px;\n      text-align: center;\n      color: white;\n      font-family: \"Open Sans\", sans-serif;\n      font-size: small;\n      font-weight: 400;\n      border-color: #2B81AF;\n      border-style: solid;\n      border-width: thin;\n      border-radius: 4px;\n      background-color: #2B81AF; }\n    .igv-ui-alert-dialog-container > div:last-child div:hover {\n      cursor: pointer;\n      border-color: #25597f;\n      background-color: #25597f; }\n\n.igv-ui-color-swatch {\n  position: relative;\n  box-sizing: content-box;\n  display: flex;\n  flex-flow: row;\n  flex-wrap: wrap;\n  justify-content: center;\n  align-items: center;\n  width: 32px;\n  height: 32px;\n  border-style: solid;\n  border-width: 2px;\n  border-color: white;\n  border-radius: 4px; }\n\n.igv-ui-color-swatch:hover {\n  border-color: dimgray; }\n\n.igv-ui-colorpicker-menu-close-button {\n  display: flex;\n  flex-flow: row;\n  flex-wrap: nowrap;\n  justify-content: flex-end;\n  align-items: center;\n  width: 100%;\n  height: 32px;\n  margin-top: 4px;\n  margin-bottom: 4px;\n  padding-right: 8px; }\n  .igv-ui-colorpicker-menu-close-button i.fa {\n    display: block;\n    margin-left: 4px;\n    margin-right: 4px;\n    color: #5f5f5f; }\n  .igv-ui-colorpicker-menu-close-button i.fa:hover,\n  .igv-ui-colorpicker-menu-close-button i.fa:focus,\n  .igv-ui-colorpicker-menu-close-button i.fa:active {\n    cursor: pointer;\n    color: #0f0f0f; }\n\n.igv-ui-generic-dialog-container {\n  box-sizing: content-box;\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 300px;\n  height: 200px;\n  border-color: #7F7F7F;\n  border-radius: 4px;\n  border-style: solid;\n  border-width: thin;\n  font-family: \"Open Sans\", sans-serif;\n  font-size: medium;\n  font-weight: 400;\n  z-index: 2048;\n  background-color: white;\n  display: flex;\n  flex-flow: column;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: center; }\n  .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header {\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: flex-end;\n    align-items: center;\n    width: 100%;\n    height: 24px;\n    cursor: move;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n    border-bottom-color: #7F7F7F;\n    border-bottom-style: solid;\n    border-bottom-width: thin;\n    background-color: #eee; }\n    .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div {\n      margin-right: 4px;\n      margin-bottom: 2px;\n      height: 12px;\n      width: 12px;\n      color: #7F7F7F; }\n    .igv-ui-generic-dialog-container .igv-ui-generic-dialog-header div:hover {\n      cursor: pointer;\n      color: #444; }\n  .igv-ui-generic-dialog-container .igv-ui-generic-dialog-one-liner {\n    color: #373737;\n    width: 95%;\n    height: 24px;\n    line-height: 24px;\n    text-align: left;\n    margin-top: 8px;\n    padding-left: 8px;\n    overflow-wrap: break-word;\n    background-color: white; }\n  .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input {\n    margin-top: 8px;\n    width: 95%;\n    height: 24px;\n    color: #373737;\n    line-height: 24px;\n    padding-left: 8px;\n    background-color: white;\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: flex-start;\n    align-items: center; }\n    .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input div {\n      width: 30%;\n      height: 100%;\n      font-size: 16px;\n      text-align: right;\n      padding-right: 8px;\n      background-color: white; }\n    .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n      display: block;\n      height: 100%;\n      width: 100%;\n      padding-left: 4px;\n      font-family: \"Open Sans\", sans-serif;\n      font-weight: 400;\n      color: #373737;\n      text-align: left;\n      outline: none;\n      border-style: solid;\n      border-width: thin;\n      border-color: #7F7F7F;\n      background-color: white; }\n    .igv-ui-generic-dialog-container .igv-ui-generic-dialog-label-input input {\n      width: 50%;\n      font-size: 16px; }\n  .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input {\n    margin-top: 8px;\n    width: calc(100% - 16px);\n    height: 24px;\n    color: #373737;\n    line-height: 24px;\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: space-around;\n    align-items: center; }\n    .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n      display: block;\n      height: 100%;\n      width: 100%;\n      padding-left: 4px;\n      font-family: \"Open Sans\", sans-serif;\n      font-weight: 400;\n      color: #373737;\n      text-align: left;\n      outline: none;\n      border-style: solid;\n      border-width: thin;\n      border-color: #7F7F7F;\n      background-color: white; }\n    .igv-ui-generic-dialog-container .igv-ui-generic-dialog-input input {\n      font-size: 16px; }\n  .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel {\n    width: 100%;\n    height: 28px;\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: space-around;\n    align-items: center; }\n    .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div {\n      margin-top: 32px;\n      color: white;\n      font-family: \"Open Sans\", sans-serif;\n      font-size: 14px;\n      font-weight: 400;\n      width: 75px;\n      height: 28px;\n      line-height: 28px;\n      text-align: center;\n      border-color: transparent;\n      border-style: solid;\n      border-width: thin;\n      border-radius: 2px; }\n    .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child {\n      margin-left: 32px;\n      margin-right: 0;\n      background-color: #5ea4e0; }\n    .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child {\n      margin-left: 0;\n      margin-right: 32px;\n      background-color: #c4c4c4; }\n    .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:first-child:hover {\n      cursor: pointer;\n      background-color: #3b5c7f; }\n    .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok-cancel div:last-child:hover {\n      cursor: pointer;\n      background-color: #7f7f7f; }\n  .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok {\n    width: 100%;\n    height: 36px;\n    margin-top: 32px;\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: space-around;\n    align-items: center; }\n    .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div {\n      width: 98px;\n      height: 36px;\n      line-height: 36px;\n      text-align: center;\n      color: white;\n      font-family: \"Open Sans\", sans-serif;\n      font-size: medium;\n      font-weight: 400;\n      border-color: white;\n      border-style: solid;\n      border-width: thin;\n      border-radius: 4px;\n      background-color: #2B81AF; }\n    .igv-ui-generic-dialog-container .igv-ui-generic-dialog-ok div:hover {\n      cursor: pointer;\n      background-color: #25597f; }\n\n.igv-ui-generic-container {\n  box-sizing: content-box;\n  position: absolute;\n  z-index: 2048;\n  background-color: white;\n  cursor: pointer;\n  display: flex;\n  flex-direction: row;\n  flex-wrap: wrap;\n  justify-content: flex-start;\n  align-items: center; }\n  .igv-ui-generic-container > div:first-child {\n    cursor: move;\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: flex-end;\n    align-items: center;\n    height: 24px;\n    width: 100%;\n    background-color: #dddddd; }\n    .igv-ui-generic-container > div:first-child > div {\n      display: block;\n      color: #5f5f5f;\n      cursor: pointer;\n      width: 14px;\n      height: 14px;\n      margin-right: 8px;\n      margin-bottom: 4px; }\n\n.igv-ui-dialog {\n  z-index: 2048;\n  position: fixed;\n  width: fit-content;\n  height: fit-content;\n  display: flex;\n  flex-flow: column;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  background-color: white;\n  border-color: #7F7F7F;\n  border-radius: 4px;\n  border-style: solid;\n  border-width: thin;\n  font-family: \"Open Sans\", sans-serif;\n  font-size: medium;\n  font-weight: 400; }\n  .igv-ui-dialog .igv-ui-dialog-header {\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: flex-end;\n    align-items: center;\n    width: 100%;\n    height: 24px;\n    cursor: move;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n    border-bottom-color: #7F7F7F;\n    border-bottom-style: solid;\n    border-bottom-width: thin;\n    background-color: #eee; }\n    .igv-ui-dialog .igv-ui-dialog-header div {\n      margin-right: 4px;\n      margin-bottom: 2px;\n      height: 12px;\n      width: 12px;\n      color: #7F7F7F; }\n    .igv-ui-dialog .igv-ui-dialog-header div:hover {\n      cursor: pointer;\n      color: #444; }\n  .igv-ui-dialog .igv-ui-dialog-one-liner {\n    width: 95%;\n    height: 24px;\n    line-height: 24px;\n    text-align: left;\n    margin: 8px;\n    overflow-wrap: break-word;\n    background-color: white;\n    font-weight: bold; }\n  .igv-ui-dialog .igv-ui-dialog-ok-cancel {\n    width: 100%;\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: space-around;\n    align-items: center; }\n    .igv-ui-dialog .igv-ui-dialog-ok-cancel div {\n      margin: 16px;\n      margin-top: 32px;\n      color: white;\n      font-family: \"Open Sans\", sans-serif;\n      font-size: 14px;\n      font-weight: 400;\n      width: 75px;\n      height: 28px;\n      line-height: 28px;\n      text-align: center;\n      border-color: transparent;\n      border-style: solid;\n      border-width: thin;\n      border-radius: 2px; }\n    .igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child {\n      background-color: #5ea4e0; }\n    .igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child {\n      background-color: #c4c4c4; }\n    .igv-ui-dialog .igv-ui-dialog-ok-cancel div:first-child:hover {\n      cursor: pointer;\n      background-color: #3b5c7f; }\n    .igv-ui-dialog .igv-ui-dialog-ok-cancel div:last-child:hover {\n      cursor: pointer;\n      background-color: #7f7f7f; }\n  .igv-ui-dialog .igv-ui-dialog-ok {\n    width: 100%;\n    height: 36px;\n    margin-top: 32px;\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: space-around;\n    align-items: center; }\n    .igv-ui-dialog .igv-ui-dialog-ok div {\n      width: 98px;\n      height: 36px;\n      line-height: 36px;\n      text-align: center;\n      color: white;\n      font-family: \"Open Sans\", sans-serif;\n      font-size: medium;\n      font-weight: 400;\n      border-color: white;\n      border-style: solid;\n      border-width: thin;\n      border-radius: 4px;\n      background-color: #2B81AF; }\n    .igv-ui-dialog .igv-ui-dialog-ok div:hover {\n      cursor: pointer;\n      background-color: #25597f; }\n\n.igv-ui-panel, .igv-ui-panel-column, .igv-ui-panel-row {\n  z-index: 2048;\n  background-color: white;\n  font-family: \"Open Sans\", sans-serif;\n  font-size: medium;\n  font-weight: 400;\n  display: flex;\n  justify-content: flex-start;\n  align-items: flex-start; }\n\n.igv-ui-panel-column {\n  display: flex;\n  flex-direction: column; }\n\n.igv-ui-panel-row {\n  display: flex;\n  flex-direction: row; }\n\n.igv-ui-textbox {\n  background-color: white;\n  font-family: \"Open Sans\", sans-serif;\n  font-size: medium;\n  font-weight: 400;\n  display: flex;\n  justify-content: flex-start;\n  align-items: flex-start; }\n\n/*# sourceMappingURL=igv-ui.css.map */\n';
      var style = document.createElement('style');
      style.setAttribute('type', 'text/css');
      style.innerHTML = css;
      document.head.insertBefore(style, document.head.childNodes[document.head.childNodes.length - 1]);
    }

    if (typeof document !== 'undefined') {
      if (!stylesheetExists("igv-ui.css")) {
        // console.log('igv-ui. will call embedCSS() ...');
        embedCSS$2(); // console.log('... done.');
      }

      function stylesheetExists(stylesheetName) {
        for (let ss of document.styleSheets) {
          ss = ss.href ? ss.href.replace(/^.*[\\\/]/, '') : '';

          if (ss === stylesheetName) {
            return true;
          }
        }

        return false;
      }
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class DataRangeDialog {
      constructor($parent, alert) {
        // dialog container
        this.$container = $$1("<div>", {
          class: 'igv-generic-dialog-container'
        });
        $parent.append(this.$container);
        this.$container.offset({
          left: 0,
          top: 0
        }); // dialog header

        const $header = $$1("<div>", {
          class: 'igv-generic-dialog-header'
        });
        this.$container.append($header);
        attachDialogCloseHandlerWithParent$1($header[0], () => {
          this.$minimum_input.val(undefined);
          this.$maximum_input.val(undefined);
          this.$container.offset({
            left: 0,
            top: 0
          });
          this.$container.hide();
        }); // minimun

        this.$minimum = $$1("<div>", {
          class: 'igv-generic-dialog-label-input'
        });
        this.$container.append(this.$minimum);
        const $mindiv = $$1('<div>');
        $mindiv.text('Minimum');
        this.$minimum.append($mindiv);
        this.$minimum_input = $$1("<input>");
        this.$minimum.append(this.$minimum_input); // maximum

        this.$maximum = $$1("<div>", {
          class: 'igv-generic-dialog-label-input'
        });
        this.$container.append(this.$maximum);
        const $maxdiv = $$1('<div>');
        $maxdiv.text('Maximum');
        this.$maximum.append($maxdiv);
        this.$maximum_input = $$1("<input>");
        this.$maximum.append(this.$maximum_input); // ok | cancel

        const $buttons = $$1("<div>", {
          class: 'igv-generic-dialog-ok-cancel'
        });
        this.$container.append($buttons); // ok

        this.$ok = $$1("<div>");
        $buttons.append(this.$ok);
        this.$ok.text('OK'); // cancel

        this.$cancel = $$1("<div>");
        $buttons.append(this.$cancel);
        this.$cancel.text('Cancel');
        this.$cancel.on('click', () => {
          this.$minimum_input.val(undefined);
          this.$maximum_input.val(undefined);
          this.$container.offset({
            left: 0,
            top: 0
          });
          this.$container.hide();
        }); //this.$container.draggable({ handle:$header.get(0) });

        makeDraggable$1(this.$container.get(0), $header.get(0));
        this.$container.hide();
      }

      configure(trackView) {
        const dataRange = trackView.dataRange();
        let min;
        let max;

        if (dataRange) {
          min = dataRange.min;
          max = dataRange.max;
        } else {
          min = 0;
          max = 100;
        }

        this.$minimum_input.val(min);
        this.$maximum_input.val(max);
        this.$minimum_input.unbind();
        this.$minimum_input.on('keyup', e => {
          if (13 === e.keyCode) {
            this.processResults(trackView);
          }
        });
        this.$maximum_input.unbind();
        this.$maximum_input.on('keyup', e => {
          if (13 === e.keyCode) {
            this.processResults(trackView);
          }
        });
        this.$ok.unbind();
        this.$ok.on('click', e => {
          this.processResults(trackView);
        });
      }

      processResults(trackView) {
        const min = Number(this.$minimum_input.val());
        const max = Number(this.$maximum_input.val());

        if (isNaN(min) || isNaN(max)) {
          Alert.presentAlert(new Error('Must input numeric values'), undefined);
        } else {
          trackView.setDataRange(min, max);
        }

        this.$minimum_input.val(undefined);
        this.$maximum_input.val(undefined);
        this.$container.offset({
          left: 0,
          top: 0
        });
        this.$container.hide();
      }

      present($parent) {
        const offset_top = $parent.offset().top;
        const scroll_top = $$1('body').scrollTop();
        this.$container.offset({
          left: $parent.width() - this.$container.width(),
          top: offset_top + scroll_top
        });
        this.$container.show();
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of ctx software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and ctx permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    const IGVGraphics = {
      configureHighDPICanvas: function (ctx, w, h) {
        const scaleFactor = window.devicePixelRatio; // const scaleFactor = 1

        ctx.canvas.style.width = `${w}px`;
        ctx.canvas.width = Math.floor(scaleFactor * w);
        ctx.canvas.style.height = `${h}px`;
        ctx.canvas.height = Math.floor(scaleFactor * h);
        ctx.scale(scaleFactor, scaleFactor);
      },
      setProperties: function (ctx, properties) {
        for (var key in properties) {
          if (properties.hasOwnProperty(key)) {
            var value = properties[key];
            ctx[key] = value;
          }
        }
      },
      strokeLine: function (ctx, x1, y1, x2, y2, properties) {
        x1 = Math.floor(x1) + 0.5;
        y1 = Math.floor(y1) + 0.5;
        x2 = Math.floor(x2) + 0.5;
        y2 = Math.floor(y2) + 0.5;

        if (properties) {
          ctx.save();
          IGVGraphics.setProperties(ctx, properties);
        }

        ctx.beginPath();
        ctx.moveTo(x1, y1);
        ctx.lineTo(x2, y2);
        ctx.stroke();
        if (properties) ctx.restore();
      },
      fillRect: function (ctx, x, y, w, h, properties) {
        x = Math.round(x);
        y = Math.round(y);

        if (properties) {
          ctx.save();
          IGVGraphics.setProperties(ctx, properties);
        }

        ctx.fillRect(x, y, w, h);
        if (properties) ctx.restore();
      },
      fillPolygon: function (ctx, x, y, properties) {
        if (properties) {
          ctx.save();
          IGVGraphics.setProperties(ctx, properties);
        }

        doPath(ctx, x, y);
        ctx.fill();
        if (properties) ctx.restore();
      },
      strokePolygon: function (ctx, x, y, properties) {
        if (properties) {
          ctx.save();
          IGVGraphics.setProperties(ctx, properties);
        }

        doPath(ctx, x, y);
        ctx.stroke();
        if (properties) ctx.restore();
      },
      fillText: function (ctx, text, x, y, properties, transforms) {
        if (properties || transforms) {
          ctx.save();
        }

        if (properties) {
          IGVGraphics.setProperties(ctx, properties);
        }

        if (transforms) {
          // Slow path with context saving and extra translate
          ctx.translate(x, y);

          for (var transform in transforms) {
            var value = transforms[transform]; // TODO: Add error checking for robustness

            if (transform === 'translate') {
              ctx.translate(value['x'], value['y']);
            }

            if (transform === 'rotate') {
              ctx.rotate(value['angle'] * Math.PI / 180);
            }
          }

          ctx.fillText(text, 0, 0);
        } else {
          ctx.fillText(text, x, y);
        }

        if (properties || transforms) ctx.restore();
      },
      strokeText: function (ctx, text, x, y, properties, transforms) {
        if (properties || transforms) {
          ctx.save();
        }

        if (properties) {
          IGVGraphics.setProperties(ctx, properties);
        }

        if (transforms) {
          ctx.translate(x, y);

          for (var transform in transforms) {
            var value = transforms[transform]; // TODO: Add error checking for robustness

            if (transform === 'translate') {
              ctx.translate(value['x'], value['y']);
            }

            if (transform === 'rotate') {
              ctx.rotate(value['angle'] * Math.PI / 180);
            }
          }

          ctx.strokeText(text, 0, 0);
        } else {
          ctx.strokeText(text, x, y);
        }

        if (properties || transforms) ctx.restore();
      },
      strokeCircle: function (ctx, x, y, radius, properties) {
        if (properties) {
          ctx.save();
          IGVGraphics.setProperties(ctx, properties);
        }

        ctx.beginPath();
        ctx.arc(x, y, radius, 0, 2 * Math.PI);
        ctx.stroke();
        if (properties) ctx.restore();
      },
      fillCircle: function (ctx, x, y, radius, properties) {
        if (properties) {
          ctx.save();
          IGVGraphics.setProperties(ctx, properties);
        }

        ctx.beginPath();
        ctx.arc(x, y, radius, 0, 2 * Math.PI);
        ctx.fill();
        if (properties) ctx.restore();
      },
      drawArrowhead: function (ctx, x, y, size, lineWidth) {
        ctx.save();

        if (!size) {
          size = 5;
        }

        if (lineWidth) {
          ctx.lineWidth = lineWidth;
        }

        ctx.beginPath();
        ctx.moveTo(x, y - size / 2);
        ctx.lineTo(x, y + size / 2);
        ctx.lineTo(x + size, y);
        ctx.lineTo(x, y - size / 2);
        ctx.closePath();
        ctx.fill();
        ctx.restore();
      },
      dashedLine: function (ctx, x1, y1, x2, y2, dashLen) {
        let properties = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {};
        if (dashLen === undefined) dashLen = 2;
        ctx.setLineDash([dashLen, dashLen]);
        IGVGraphics.strokeLine(ctx, x1, y1, x2, y2, properties);
        ctx.setLineDash([]);
      },
      roundRect: function (ctx, x, y, width, height, radius, fill, stroke) {
        if (typeof stroke == "undefined") {
          stroke = true;
        }

        if (typeof radius === "undefined") {
          radius = 5;
        }

        ctx.beginPath();
        ctx.moveTo(x + radius, y);
        ctx.lineTo(x + width - radius, y);
        ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
        ctx.lineTo(x + width, y + height - radius);
        ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
        ctx.lineTo(x + radius, y + height);
        ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
        ctx.lineTo(x, y + radius);
        ctx.quadraticCurveTo(x, y, x + radius, y);
        ctx.closePath();

        if (stroke) {
          ctx.stroke();
        }

        if (fill) {
          ctx.fill();
        }
      },
      polygon: function (ctx, x, y, fill, stroke) {
        if (typeof stroke == "undefined") {
          stroke = true;
        }

        ctx.beginPath();
        var len = x.length;
        ctx.moveTo(x[0], y[0]);

        for (var i = 1; i < len; i++) {
          ctx.lineTo(x[i], y[i]); // this.moveTo(x[i], y[i]);
        }

        ctx.closePath();

        if (stroke) {
          ctx.stroke();
        }

        if (fill) {
          ctx.fill();
        }
      }
    };

    function doPath(ctx, x, y) {
      var i,
          len = x.length;

      for (i = 0; i < len; i++) {
        x[i] = Math.round(x[i]);
        y[i] = Math.round(y[i]);
      }

      ctx.beginPath();
      ctx.moveTo(x[0], y[0]);

      for (i = 1; i < len; i++) {
        ctx.lineTo(x[i], y[i]);
      }

      ctx.closePath();
    }

    var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};

    function createCommonjsModule(fn) {
      var module = { exports: {} };
    	return fn(module, module.exports), module.exports;
    }

    var check = function (it) {
      return it && it.Math == Math && it;
    }; // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028


    var global$1 = // eslint-disable-next-line es-x/no-global-this -- safe
    check(typeof globalThis == 'object' && globalThis) || check(typeof window == 'object' && window) || // eslint-disable-next-line no-restricted-globals -- safe
    check(typeof self == 'object' && self) || check(typeof commonjsGlobal == 'object' && commonjsGlobal) || // eslint-disable-next-line no-new-func -- fallback
    function () {
      return this;
    }() || Function('return this')();

    var fails = function (exec) {
      try {
        return !!exec();
      } catch (error) {
        return true;
      }
    };

    var descriptors = !fails(function () {
      // eslint-disable-next-line es-x/no-object-defineproperty -- required for testing
      return Object.defineProperty({}, 1, {
        get: function () {
          return 7;
        }
      })[1] != 7;
    });

    var functionBindNative = !fails(function () {
      // eslint-disable-next-line es-x/no-function-prototype-bind -- safe
      var test = function () {
        /* empty */
      }.bind(); // eslint-disable-next-line no-prototype-builtins -- safe


      return typeof test != 'function' || test.hasOwnProperty('prototype');
    });

    var call$2 = Function.prototype.call;
    var functionCall = functionBindNative ? call$2.bind(call$2) : function () {
      return call$2.apply(call$2, arguments);
    };

    var $propertyIsEnumerable = {}.propertyIsEnumerable; // eslint-disable-next-line es-x/no-object-getownpropertydescriptor -- safe

    var getOwnPropertyDescriptor$1 = Object.getOwnPropertyDescriptor; // Nashorn ~ JDK8 bug

    var NASHORN_BUG = getOwnPropertyDescriptor$1 && !$propertyIsEnumerable.call({
      1: 2
    }, 1); // `Object.prototype.propertyIsEnumerable` method implementation
    // https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable

    var f$5 = NASHORN_BUG ? function propertyIsEnumerable(V) {
      var descriptor = getOwnPropertyDescriptor$1(this, V);
      return !!descriptor && descriptor.enumerable;
    } : $propertyIsEnumerable;
    var objectPropertyIsEnumerable = {
      f: f$5
    };

    var createPropertyDescriptor = function (bitmap, value) {
      return {
        enumerable: !(bitmap & 1),
        configurable: !(bitmap & 2),
        writable: !(bitmap & 4),
        value: value
      };
    };

    var FunctionPrototype$2 = Function.prototype;
    var bind$1 = FunctionPrototype$2.bind;
    var call$1 = FunctionPrototype$2.call;
    var uncurryThis = functionBindNative && bind$1.bind(call$1, call$1);
    var functionUncurryThis = functionBindNative ? function (fn) {
      return fn && uncurryThis(fn);
    } : function (fn) {
      return fn && function () {
        return call$1.apply(fn, arguments);
      };
    };

    var toString$1 = functionUncurryThis({}.toString);
    var stringSlice = functionUncurryThis(''.slice);

    var classofRaw = function (it) {
      return stringSlice(toString$1(it), 8, -1);
    };

    var Object$5 = global$1.Object;
    var split = functionUncurryThis(''.split); // fallback for non-array-like ES3 and non-enumerable old V8 strings

    var indexedObject = fails(function () {
      // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346
      // eslint-disable-next-line no-prototype-builtins -- safe
      return !Object$5('z').propertyIsEnumerable(0);
    }) ? function (it) {
      return classofRaw(it) == 'String' ? split(it, '') : Object$5(it);
    } : Object$5;

    var TypeError$a = global$1.TypeError; // `RequireObjectCoercible` abstract operation
    // https://tc39.es/ecma262/#sec-requireobjectcoercible

    var requireObjectCoercible = function (it) {
      if (it == undefined) throw TypeError$a("Can't call method on " + it);
      return it;
    };

    var toIndexedObject = function (it) {
      return indexedObject(requireObjectCoercible(it));
    };

    // `IsCallable` abstract operation
    // https://tc39.es/ecma262/#sec-iscallable
    var isCallable = function (argument) {
      return typeof argument == 'function';
    };

    var isObject = function (it) {
      return typeof it == 'object' ? it !== null : isCallable(it);
    };

    var aFunction = function (argument) {
      return isCallable(argument) ? argument : undefined;
    };

    var getBuiltIn = function (namespace, method) {
      return arguments.length < 2 ? aFunction(global$1[namespace]) : global$1[namespace] && global$1[namespace][method];
    };

    var objectIsPrototypeOf = functionUncurryThis({}.isPrototypeOf);

    var engineUserAgent = getBuiltIn('navigator', 'userAgent') || '';

    var process$2 = global$1.process;
    var Deno = global$1.Deno;
    var versions = process$2 && process$2.versions || Deno && Deno.version;
    var v8 = versions && versions.v8;
    var match, version$1;

    if (v8) {
      match = v8.split('.'); // in old Chrome, versions of V8 isn't V8 = Chrome / 10
      // but their correct versions are not interesting for us

      version$1 = match[0] > 0 && match[0] < 4 ? 1 : +(match[0] + match[1]);
    } // BrowserFS NodeJS `process` polyfill incorrectly set `.v8` to `0.0`
    // so check `userAgent` even if `.v8` exists, but 0


    if (!version$1 && engineUserAgent) {
      match = engineUserAgent.match(/Edge\/(\d+)/);

      if (!match || match[1] >= 74) {
        match = engineUserAgent.match(/Chrome\/(\d+)/);
        if (match) version$1 = +match[1];
      }
    }

    var engineV8Version = version$1;

    /* eslint-disable es-x/no-symbol -- required for testing */

    var nativeSymbol = !!Object.getOwnPropertySymbols && !fails(function () {
      var symbol = Symbol(); // Chrome 38 Symbol has incorrect toString conversion
      // `get-own-property-symbols` polyfill symbols converted to object are not Symbol instances

      return !String(symbol) || !(Object(symbol) instanceof Symbol) || // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances
      !Symbol.sham && engineV8Version && engineV8Version < 41;
    });

    /* eslint-disable es-x/no-symbol -- required for testing */
    var useSymbolAsUid = nativeSymbol && !Symbol.sham && typeof Symbol.iterator == 'symbol';

    var Object$4 = global$1.Object;
    var isSymbol = useSymbolAsUid ? function (it) {
      return typeof it == 'symbol';
    } : function (it) {
      var $Symbol = getBuiltIn('Symbol');
      return isCallable($Symbol) && objectIsPrototypeOf($Symbol.prototype, Object$4(it));
    };

    var String$4 = global$1.String;

    var tryToString = function (argument) {
      try {
        return String$4(argument);
      } catch (error) {
        return 'Object';
      }
    };

    var TypeError$9 = global$1.TypeError; // `Assert: IsCallable(argument) is true`

    var aCallable = function (argument) {
      if (isCallable(argument)) return argument;
      throw TypeError$9(tryToString(argument) + ' is not a function');
    };

    // https://tc39.es/ecma262/#sec-getmethod

    var getMethod = function (V, P) {
      var func = V[P];
      return func == null ? undefined : aCallable(func);
    };

    var TypeError$8 = global$1.TypeError; // `OrdinaryToPrimitive` abstract operation
    // https://tc39.es/ecma262/#sec-ordinarytoprimitive

    var ordinaryToPrimitive = function (input, pref) {
      var fn, val;
      if (pref === 'string' && isCallable(fn = input.toString) && !isObject(val = functionCall(fn, input))) return val;
      if (isCallable(fn = input.valueOf) && !isObject(val = functionCall(fn, input))) return val;
      if (pref !== 'string' && isCallable(fn = input.toString) && !isObject(val = functionCall(fn, input))) return val;
      throw TypeError$8("Can't convert object to primitive value");
    };

    var defineProperty$1 = Object.defineProperty;

    var setGlobal = function (key, value) {
      try {
        defineProperty$1(global$1, key, {
          value: value,
          configurable: true,
          writable: true
        });
      } catch (error) {
        global$1[key] = value;
      }

      return value;
    };

    var SHARED = '__core-js_shared__';
    var store$1 = global$1[SHARED] || setGlobal(SHARED, {});
    var sharedStore = store$1;

    var shared = createCommonjsModule(function (module) {
      (module.exports = function (key, value) {
        return sharedStore[key] || (sharedStore[key] = value !== undefined ? value : {});
      })('versions', []).push({
        version: '3.22.4',
        mode: 'global',
        copyright: '© 2014-2022 Denis Pushkarev (zloirock.ru)',
        license: 'https://github.com/zloirock/core-js/blob/v3.22.4/LICENSE',
        source: 'https://github.com/zloirock/core-js'
      });
    });

    var Object$3 = global$1.Object; // `ToObject` abstract operation
    // https://tc39.es/ecma262/#sec-toobject

    var toObject = function (argument) {
      return Object$3(requireObjectCoercible(argument));
    };

    var hasOwnProperty = functionUncurryThis({}.hasOwnProperty); // `HasOwnProperty` abstract operation
    // https://tc39.es/ecma262/#sec-hasownproperty
    // eslint-disable-next-line es-x/no-object-hasown -- safe

    var hasOwnProperty_1 = Object.hasOwn || function hasOwn(it, key) {
      return hasOwnProperty(toObject(it), key);
    };

    var id = 0;
    var postfix = Math.random();
    var toString = functionUncurryThis(1.0.toString);

    var uid = function (key) {
      return 'Symbol(' + (key === undefined ? '' : key) + ')_' + toString(++id + postfix, 36);
    };

    var WellKnownSymbolsStore = shared('wks');
    var Symbol$1 = global$1.Symbol;
    var symbolFor = Symbol$1 && Symbol$1['for'];
    var createWellKnownSymbol = useSymbolAsUid ? Symbol$1 : Symbol$1 && Symbol$1.withoutSetter || uid;

    var wellKnownSymbol = function (name) {
      if (!hasOwnProperty_1(WellKnownSymbolsStore, name) || !(nativeSymbol || typeof WellKnownSymbolsStore[name] == 'string')) {
        var description = 'Symbol.' + name;

        if (nativeSymbol && hasOwnProperty_1(Symbol$1, name)) {
          WellKnownSymbolsStore[name] = Symbol$1[name];
        } else if (useSymbolAsUid && symbolFor) {
          WellKnownSymbolsStore[name] = symbolFor(description);
        } else {
          WellKnownSymbolsStore[name] = createWellKnownSymbol(description);
        }
      }

      return WellKnownSymbolsStore[name];
    };

    var TypeError$7 = global$1.TypeError;
    var TO_PRIMITIVE = wellKnownSymbol('toPrimitive'); // `ToPrimitive` abstract operation
    // https://tc39.es/ecma262/#sec-toprimitive

    var toPrimitive = function (input, pref) {
      if (!isObject(input) || isSymbol(input)) return input;
      var exoticToPrim = getMethod(input, TO_PRIMITIVE);
      var result;

      if (exoticToPrim) {
        if (pref === undefined) pref = 'default';
        result = functionCall(exoticToPrim, input, pref);
        if (!isObject(result) || isSymbol(result)) return result;
        throw TypeError$7("Can't convert object to primitive value");
      }

      if (pref === undefined) pref = 'number';
      return ordinaryToPrimitive(input, pref);
    };

    // https://tc39.es/ecma262/#sec-topropertykey

    var toPropertyKey = function (argument) {
      var key = toPrimitive(argument, 'string');
      return isSymbol(key) ? key : key + '';
    };

    var document$1 = global$1.document; // typeof document.createElement is 'object' in old IE

    var EXISTS$1 = isObject(document$1) && isObject(document$1.createElement);

    var documentCreateElement = function (it) {
      return EXISTS$1 ? document$1.createElement(it) : {};
    };

    var ie8DomDefine = !descriptors && !fails(function () {
      // eslint-disable-next-line es-x/no-object-defineproperty -- required for testing
      return Object.defineProperty(documentCreateElement('div'), 'a', {
        get: function () {
          return 7;
        }
      }).a != 7;
    });

    var $getOwnPropertyDescriptor$1 = Object.getOwnPropertyDescriptor; // `Object.getOwnPropertyDescriptor` method
    // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor

    var f$4 = descriptors ? $getOwnPropertyDescriptor$1 : function getOwnPropertyDescriptor(O, P) {
      O = toIndexedObject(O);
      P = toPropertyKey(P);
      if (ie8DomDefine) try {
        return $getOwnPropertyDescriptor$1(O, P);
      } catch (error) {
        /* empty */
      }
      if (hasOwnProperty_1(O, P)) return createPropertyDescriptor(!functionCall(objectPropertyIsEnumerable.f, O, P), O[P]);
    };
    var objectGetOwnPropertyDescriptor = {
      f: f$4
    };

    // https://bugs.chromium.org/p/v8/issues/detail?id=3334

    var v8PrototypeDefineBug = descriptors && fails(function () {
      // eslint-disable-next-line es-x/no-object-defineproperty -- required for testing
      return Object.defineProperty(function () {
        /* empty */
      }, 'prototype', {
        value: 42,
        writable: false
      }).prototype != 42;
    });

    var String$3 = global$1.String;
    var TypeError$6 = global$1.TypeError; // `Assert: Type(argument) is Object`

    var anObject = function (argument) {
      if (isObject(argument)) return argument;
      throw TypeError$6(String$3(argument) + ' is not an object');
    };

    var TypeError$5 = global$1.TypeError; // eslint-disable-next-line es-x/no-object-defineproperty -- safe

    var $defineProperty = Object.defineProperty; // eslint-disable-next-line es-x/no-object-getownpropertydescriptor -- safe

    var $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
    var ENUMERABLE = 'enumerable';
    var CONFIGURABLE$1 = 'configurable';
    var WRITABLE = 'writable'; // `Object.defineProperty` method
    // https://tc39.es/ecma262/#sec-object.defineproperty

    var f$3 = descriptors ? v8PrototypeDefineBug ? function defineProperty(O, P, Attributes) {
      anObject(O);
      P = toPropertyKey(P);
      anObject(Attributes);

      if (typeof O === 'function' && P === 'prototype' && 'value' in Attributes && WRITABLE in Attributes && !Attributes[WRITABLE]) {
        var current = $getOwnPropertyDescriptor(O, P);

        if (current && current[WRITABLE]) {
          O[P] = Attributes.value;
          Attributes = {
            configurable: CONFIGURABLE$1 in Attributes ? Attributes[CONFIGURABLE$1] : current[CONFIGURABLE$1],
            enumerable: ENUMERABLE in Attributes ? Attributes[ENUMERABLE] : current[ENUMERABLE],
            writable: false
          };
        }
      }

      return $defineProperty(O, P, Attributes);
    } : $defineProperty : function defineProperty(O, P, Attributes) {
      anObject(O);
      P = toPropertyKey(P);
      anObject(Attributes);
      if (ie8DomDefine) try {
        return $defineProperty(O, P, Attributes);
      } catch (error) {
        /* empty */
      }
      if ('get' in Attributes || 'set' in Attributes) throw TypeError$5('Accessors not supported');
      if ('value' in Attributes) O[P] = Attributes.value;
      return O;
    };
    var objectDefineProperty = {
      f: f$3
    };

    var createNonEnumerableProperty = descriptors ? function (object, key, value) {
      return objectDefineProperty.f(object, key, createPropertyDescriptor(1, value));
    } : function (object, key, value) {
      object[key] = value;
      return object;
    };

    var FunctionPrototype$1 = Function.prototype; // eslint-disable-next-line es-x/no-object-getownpropertydescriptor -- safe

    var getDescriptor = descriptors && Object.getOwnPropertyDescriptor;
    var EXISTS = hasOwnProperty_1(FunctionPrototype$1, 'name'); // additional protection from minified / mangled / dropped function names

    var PROPER = EXISTS && function something() {
      /* empty */
    }.name === 'something';

    var CONFIGURABLE = EXISTS && (!descriptors || descriptors && getDescriptor(FunctionPrototype$1, 'name').configurable);
    var functionName = {
      EXISTS: EXISTS,
      PROPER: PROPER,
      CONFIGURABLE: CONFIGURABLE
    };

    var functionToString = functionUncurryThis(Function.toString); // this helper broken in `core-js@3.4.1-3.4.4`, so we can't use `shared` helper

    if (!isCallable(sharedStore.inspectSource)) {
      sharedStore.inspectSource = function (it) {
        return functionToString(it);
      };
    }

    var inspectSource = sharedStore.inspectSource;

    var WeakMap$1 = global$1.WeakMap;
    var nativeWeakMap = isCallable(WeakMap$1) && /native code/.test(inspectSource(WeakMap$1));

    var keys = shared('keys');

    var sharedKey = function (key) {
      return keys[key] || (keys[key] = uid(key));
    };

    var hiddenKeys$1 = {};

    var OBJECT_ALREADY_INITIALIZED = 'Object already initialized';
    var TypeError$4 = global$1.TypeError;
    var WeakMap = global$1.WeakMap;
    var set$1, get, has;

    var enforce = function (it) {
      return has(it) ? get(it) : set$1(it, {});
    };

    var getterFor = function (TYPE) {
      return function (it) {
        var state;

        if (!isObject(it) || (state = get(it)).type !== TYPE) {
          throw TypeError$4('Incompatible receiver, ' + TYPE + ' required');
        }

        return state;
      };
    };

    if (nativeWeakMap || sharedStore.state) {
      var store = sharedStore.state || (sharedStore.state = new WeakMap());
      var wmget = functionUncurryThis(store.get);
      var wmhas = functionUncurryThis(store.has);
      var wmset = functionUncurryThis(store.set);

      set$1 = function (it, metadata) {
        if (wmhas(store, it)) throw new TypeError$4(OBJECT_ALREADY_INITIALIZED);
        metadata.facade = it;
        wmset(store, it, metadata);
        return metadata;
      };

      get = function (it) {
        return wmget(store, it) || {};
      };

      has = function (it) {
        return wmhas(store, it);
      };
    } else {
      var STATE = sharedKey('state');
      hiddenKeys$1[STATE] = true;

      set$1 = function (it, metadata) {
        if (hasOwnProperty_1(it, STATE)) throw new TypeError$4(OBJECT_ALREADY_INITIALIZED);
        metadata.facade = it;
        createNonEnumerableProperty(it, STATE, metadata);
        return metadata;
      };

      get = function (it) {
        return hasOwnProperty_1(it, STATE) ? it[STATE] : {};
      };

      has = function (it) {
        return hasOwnProperty_1(it, STATE);
      };
    }

    var internalState = {
      set: set$1,
      get: get,
      has: has,
      enforce: enforce,
      getterFor: getterFor
    };

    var makeBuiltIn_1 = createCommonjsModule(function (module) {
      var defineProperty = objectDefineProperty.f;
      var CONFIGURABLE_FUNCTION_NAME = functionName.CONFIGURABLE;
      var enforceInternalState = internalState.enforce;
      var getInternalState = internalState.get;
      var CONFIGURABLE_LENGTH = !fails(function () {
        return defineProperty(function () {
          /* empty */
        }, 'length', {
          value: 8
        }).length !== 8;
      });
      var TEMPLATE = String(String).split('String');

      var makeBuiltIn = module.exports = function (value, name, options) {
        if (String(name).slice(0, 7) === 'Symbol(') {
          name = '[' + String(name).replace(/^Symbol\(([^)]*)\)/, '$1') + ']';
        }

        if (options && options.getter) name = 'get ' + name;
        if (options && options.setter) name = 'set ' + name;

        if (!hasOwnProperty_1(value, 'name') || CONFIGURABLE_FUNCTION_NAME && value.name !== name) {
          defineProperty(value, 'name', {
            value: name,
            configurable: true
          });
        }

        if (CONFIGURABLE_LENGTH && options && hasOwnProperty_1(options, 'arity') && value.length !== options.arity) {
          defineProperty(value, 'length', {
            value: options.arity
          });
        }

        var state = enforceInternalState(value);

        if (!hasOwnProperty_1(state, 'source')) {
          state.source = TEMPLATE.join(typeof name == 'string' ? name : '');
        }

        return value;
      }; // add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative
      // eslint-disable-next-line no-extend-native -- required


      Function.prototype.toString = makeBuiltIn(function toString() {
        return isCallable(this) && getInternalState(this).source || inspectSource(this);
      }, 'toString');
    });

    var defineBuiltIn = function (O, key, value, options) {
      var unsafe = options ? !!options.unsafe : false;
      var simple = options ? !!options.enumerable : false;
      var noTargetGet = options ? !!options.noTargetGet : false;
      var name = options && options.name !== undefined ? options.name : key;
      if (isCallable(value)) makeBuiltIn_1(value, name, options);

      if (O === global$1) {
        if (simple) O[key] = value;else setGlobal(key, value);
        return O;
      } else if (!unsafe) {
        delete O[key];
      } else if (!noTargetGet && O[key]) {
        simple = true;
      }

      if (simple) O[key] = value;else createNonEnumerableProperty(O, key, value);
      return O;
    };

    var ceil = Math.ceil;
    var floor = Math.floor; // `ToIntegerOrInfinity` abstract operation
    // https://tc39.es/ecma262/#sec-tointegerorinfinity

    var toIntegerOrInfinity = function (argument) {
      var number = +argument; // eslint-disable-next-line no-self-compare -- safe

      return number !== number || number === 0 ? 0 : (number > 0 ? floor : ceil)(number);
    };

    var max = Math.max;
    var min$1 = Math.min; // Helper for a popular repeating case of the spec:
    // Let integer be ? ToInteger(index).
    // If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length).

    var toAbsoluteIndex = function (index, length) {
      var integer = toIntegerOrInfinity(index);
      return integer < 0 ? max(integer + length, 0) : min$1(integer, length);
    };

    var min = Math.min; // `ToLength` abstract operation
    // https://tc39.es/ecma262/#sec-tolength

    var toLength = function (argument) {
      return argument > 0 ? min(toIntegerOrInfinity(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991
    };

    // https://tc39.es/ecma262/#sec-lengthofarraylike

    var lengthOfArrayLike = function (obj) {
      return toLength(obj.length);
    };

    var createMethod = function (IS_INCLUDES) {
      return function ($this, el, fromIndex) {
        var O = toIndexedObject($this);
        var length = lengthOfArrayLike(O);
        var index = toAbsoluteIndex(fromIndex, length);
        var value; // Array#includes uses SameValueZero equality algorithm
        // eslint-disable-next-line no-self-compare -- NaN check

        if (IS_INCLUDES && el != el) while (length > index) {
          value = O[index++]; // eslint-disable-next-line no-self-compare -- NaN check

          if (value != value) return true; // Array#indexOf ignores holes, Array#includes - not
        } else for (; length > index; index++) {
          if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0;
        }
        return !IS_INCLUDES && -1;
      };
    };

    var arrayIncludes = {
      // `Array.prototype.includes` method
      // https://tc39.es/ecma262/#sec-array.prototype.includes
      includes: createMethod(true),
      // `Array.prototype.indexOf` method
      // https://tc39.es/ecma262/#sec-array.prototype.indexof
      indexOf: createMethod(false)
    };

    var indexOf = arrayIncludes.indexOf;
    var push = functionUncurryThis([].push);

    var objectKeysInternal = function (object, names) {
      var O = toIndexedObject(object);
      var i = 0;
      var result = [];
      var key;

      for (key in O) !hasOwnProperty_1(hiddenKeys$1, key) && hasOwnProperty_1(O, key) && push(result, key); // Don't enum bug & hidden keys


      while (names.length > i) if (hasOwnProperty_1(O, key = names[i++])) {
        ~indexOf(result, key) || push(result, key);
      }

      return result;
    };

    // IE8- don't enum bug keys
    var enumBugKeys = ['constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf'];

    var hiddenKeys = enumBugKeys.concat('length', 'prototype'); // `Object.getOwnPropertyNames` method
    // https://tc39.es/ecma262/#sec-object.getownpropertynames
    // eslint-disable-next-line es-x/no-object-getownpropertynames -- safe

    var f$2 = Object.getOwnPropertyNames || function getOwnPropertyNames(O) {
      return objectKeysInternal(O, hiddenKeys);
    };

    var objectGetOwnPropertyNames = {
      f: f$2
    };

    // eslint-disable-next-line es-x/no-object-getownpropertysymbols -- safe
    var f$1 = Object.getOwnPropertySymbols;
    var objectGetOwnPropertySymbols = {
      f: f$1
    };

    var concat = functionUncurryThis([].concat); // all object keys, includes non-enumerable and symbols

    var ownKeys = getBuiltIn('Reflect', 'ownKeys') || function ownKeys(it) {
      var keys = objectGetOwnPropertyNames.f(anObject(it));
      var getOwnPropertySymbols = objectGetOwnPropertySymbols.f;
      return getOwnPropertySymbols ? concat(keys, getOwnPropertySymbols(it)) : keys;
    };

    var copyConstructorProperties = function (target, source, exceptions) {
      var keys = ownKeys(source);
      var defineProperty = objectDefineProperty.f;
      var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;

      for (var i = 0; i < keys.length; i++) {
        var key = keys[i];

        if (!hasOwnProperty_1(target, key) && !(exceptions && hasOwnProperty_1(exceptions, key))) {
          defineProperty(target, key, getOwnPropertyDescriptor(source, key));
        }
      }
    };

    var replacement = /#|\.prototype\./;

    var isForced = function (feature, detection) {
      var value = data[normalize$1(feature)];
      return value == POLYFILL ? true : value == NATIVE ? false : isCallable(detection) ? fails(detection) : !!detection;
    };

    var normalize$1 = isForced.normalize = function (string) {
      return String(string).replace(replacement, '.').toLowerCase();
    };

    var data = isForced.data = {};
    var NATIVE = isForced.NATIVE = 'N';
    var POLYFILL = isForced.POLYFILL = 'P';
    var isForced_1 = isForced;

    var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
    /*
      options.target      - name of the target object
      options.global      - target is the global object
      options.stat        - export as static methods of target
      options.proto       - export as prototype methods of target
      options.real        - real prototype method for the `pure` version
      options.forced      - export even if the native feature is available
      options.bind        - bind methods to the target, required for the `pure` version
      options.wrap        - wrap constructors to preventing global pollution, required for the `pure` version
      options.unsafe      - use the simple assignment of property instead of delete + defineProperty
      options.sham        - add a flag to not completely full polyfills
      options.enumerable  - export as enumerable property
      options.noTargetGet - prevent calling a getter on target
      options.name        - the .name of the function if it does not match the key
    */

    var _export = function (options, source) {
      var TARGET = options.target;
      var GLOBAL = options.global;
      var STATIC = options.stat;
      var FORCED, target, key, targetProperty, sourceProperty, descriptor;

      if (GLOBAL) {
        target = global$1;
      } else if (STATIC) {
        target = global$1[TARGET] || setGlobal(TARGET, {});
      } else {
        target = (global$1[TARGET] || {}).prototype;
      }

      if (target) for (key in source) {
        sourceProperty = source[key];

        if (options.noTargetGet) {
          descriptor = getOwnPropertyDescriptor(target, key);
          targetProperty = descriptor && descriptor.value;
        } else targetProperty = target[key];

        FORCED = isForced_1(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced); // contained in target

        if (!FORCED && targetProperty !== undefined) {
          if (typeof sourceProperty == typeof targetProperty) continue;
          copyConstructorProperties(sourceProperty, targetProperty);
        } // add a flag to not completely full polyfills


        if (options.sham || targetProperty && targetProperty.sham) {
          createNonEnumerableProperty(sourceProperty, 'sham', true);
        }

        defineBuiltIn(target, key, sourceProperty, options);
      }
    };

    // https://tc39.es/ecma262/#sec-object.keys
    // eslint-disable-next-line es-x/no-object-keys -- safe

    var objectKeys = Object.keys || function keys(O) {
      return objectKeysInternal(O, enumBugKeys);
    };

    // https://tc39.es/ecma262/#sec-object.defineproperties
    // eslint-disable-next-line es-x/no-object-defineproperties -- safe

    var f = descriptors && !v8PrototypeDefineBug ? Object.defineProperties : function defineProperties(O, Properties) {
      anObject(O);
      var props = toIndexedObject(Properties);
      var keys = objectKeys(Properties);
      var length = keys.length;
      var index = 0;
      var key;

      while (length > index) objectDefineProperty.f(O, key = keys[index++], props[key]);

      return O;
    };
    var objectDefineProperties = {
      f: f
    };

    var html = getBuiltIn('document', 'documentElement');

    /* global ActiveXObject -- old IE, WSH */
    var GT = '>';
    var LT = '<';
    var PROTOTYPE = 'prototype';
    var SCRIPT = 'script';
    var IE_PROTO$1 = sharedKey('IE_PROTO');

    var EmptyConstructor = function () {
      /* empty */
    };

    var scriptTag = function (content) {
      return LT + SCRIPT + GT + content + LT + '/' + SCRIPT + GT;
    }; // Create object with fake `null` prototype: use ActiveX Object with cleared prototype


    var NullProtoObjectViaActiveX = function (activeXDocument) {
      activeXDocument.write(scriptTag(''));
      activeXDocument.close();
      var temp = activeXDocument.parentWindow.Object;
      activeXDocument = null; // avoid memory leak

      return temp;
    }; // Create object with fake `null` prototype: use iframe Object with cleared prototype


    var NullProtoObjectViaIFrame = function () {
      // Thrash, waste and sodomy: IE GC bug
      var iframe = documentCreateElement('iframe');
      var JS = 'java' + SCRIPT + ':';
      var iframeDocument;
      iframe.style.display = 'none';
      html.appendChild(iframe); // https://github.com/zloirock/core-js/issues/475

      iframe.src = String(JS);
      iframeDocument = iframe.contentWindow.document;
      iframeDocument.open();
      iframeDocument.write(scriptTag('document.F=Object'));
      iframeDocument.close();
      return iframeDocument.F;
    }; // Check for document.domain and active x support
    // No need to use active x approach when document.domain is not set
    // see https://github.com/es-shims/es5-shim/issues/150
    // variation of https://github.com/kitcambridge/es5-shim/commit/4f738ac066346
    // avoid IE GC bug


    var activeXDocument;

    var NullProtoObject = function () {
      try {
        activeXDocument = new ActiveXObject('htmlfile');
      } catch (error) {
        /* ignore */
      }

      NullProtoObject = typeof document != 'undefined' ? document.domain && activeXDocument ? NullProtoObjectViaActiveX(activeXDocument) // old IE
      : NullProtoObjectViaIFrame() : NullProtoObjectViaActiveX(activeXDocument); // WSH

      var length = enumBugKeys.length;

      while (length--) delete NullProtoObject[PROTOTYPE][enumBugKeys[length]];

      return NullProtoObject();
    };

    hiddenKeys$1[IE_PROTO$1] = true; // `Object.create` method
    // https://tc39.es/ecma262/#sec-object.create
    // eslint-disable-next-line es-x/no-object-create -- safe

    var objectCreate = Object.create || function create(O, Properties) {
      var result;

      if (O !== null) {
        EmptyConstructor[PROTOTYPE] = anObject(O);
        result = new EmptyConstructor();
        EmptyConstructor[PROTOTYPE] = null; // add "__proto__" for Object.getPrototypeOf polyfill

        result[IE_PROTO$1] = O;
      } else result = NullProtoObject();

      return Properties === undefined ? result : objectDefineProperties.f(result, Properties);
    };

    var UNSCOPABLES = wellKnownSymbol('unscopables');
    var ArrayPrototype = Array.prototype; // Array.prototype[@@unscopables]
    // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables

    if (ArrayPrototype[UNSCOPABLES] == undefined) {
      objectDefineProperty.f(ArrayPrototype, UNSCOPABLES, {
        configurable: true,
        value: objectCreate(null)
      });
    } // add a key to Array.prototype[@@unscopables]


    var addToUnscopables = function (key) {
      ArrayPrototype[UNSCOPABLES][key] = true;
    };

    var $includes = arrayIncludes.includes; // FF99+ bug

    var BROKEN_ON_SPARSE = fails(function () {
      return !Array(1).includes();
    }); // `Array.prototype.includes` method
    // https://tc39.es/ecma262/#sec-array.prototype.includes

    _export({
      target: 'Array',
      proto: true,
      forced: BROKEN_ON_SPARSE
    }, {
      includes: function includes(el
      /* , fromIndex = 0 */
      ) {
        return $includes(this, el, arguments.length > 1 ? arguments[1] : undefined);
      }
    }); // https://tc39.es/ecma262/#sec-array.prototype-@@unscopables

    addToUnscopables('includes');

    const FileFormats = {
      gwascatalog: {
        fields: ['bin', 'chr', 'start', 'end', 'name', 'pubMedID', 'author', 'pubDate', 'journal', 'title', 'trait', 'initSample', 'replSample', 'region', 'genes', 'riskAllele', 'riskAlFreq', 'pValue', 'pValueDesc', 'orOrBeta', 'ci95', 'platform', 'cnv']
      },
      wgrna: {
        fields: ['bin', 'chr', 'start', 'end', 'name', 'score', 'strand', 'thickStart', 'thickEnd', 'type']
      },
      cpgislandext: {
        fields: ['bin', 'chr', 'start', 'end', 'name', 'length', 'cpgNum', 'gcNum', 'perCpg', 'perGc', 'obsExp']
      },
      clinVarMain: {
        fields: ['chr1', 'start', 'end', 'name', 'score', 'strand', 'thickStart', 'thickEnd', 'reserved', 'blockCount', // Number of blocks
        'blockSizes', // Comma separated list of block sizes
        'chromStarts', // Start positions relative to chromStart
        'origName', // NM_198053.2(CD247):c.462C>T (p.Asp154=)	ClinVar Variation Report
        'clinSign', // Likely benign	Clinical significance
        'reviewStatus', // 	based on: criteria provided,single submitter	Review Status
        'type', // single nucleotide variant	Type of Variant
        'geneId', // CD247	Gene Symbol
        'snpId', //	181656780	dbSNP ID
        'nsvId', //		dbVar ID
        'rcvAcc', //	RCV000642347	ClinVar Allele Submission
        'testedInGtr', //	N	Genetic Testing Registry
        'phenotypeList', //	Immunodeficiency due to defect in cd3-zeta	Phenotypes
        'phenotype', //	MedGen:C1857798, OMIM:610163	Phenotype identifiers
        'origin', //	germline	Data origin
        'assembly', //	GRCh37	Genome assembly
        'cytogenetic', //	1q24.2	Cytogenetic status
        'hgvsCod', //	NM_198053.2:c.462C>T	Nucleotide HGVS
        'hgvsProt', //	NP_932170.1:p.Asp154=	Protein HGVS
        'numSubmit', //	1	Number of submitters
        'lastEval', //	Dec 19,2017	Last evaluation
        'guidelines', //		Guidelines
        'otherIds']
      }
    };

    const knownFileExtensions = new Set(["narrowpeak", "broadpeak", "regionpeak", "peaks", "bedgraph", "wig", "gff3", "gff", "gtf", "fusionjuncspan", "refflat", "seg", "aed", "bed", "vcf", "bb", "bigbed", "biginteract", "biggenepred", "bignarrowpeak", "bw", "bigwig", "bam", "tdf", "refgene", "genepred", "genepredext", "bedpe", "bp", "snp", "rmsk", "cram", "gwas", "maf", "mut"]);
    /**
     * Return a custom format object with the given name.
     * @param name
     * @returns {*}
     */

    function getFormat(name) {
      if (FileFormats && FileFormats[name]) {
        return expandFormat(FileFormats[name]);
      } else {
        return undefined;
      }

      function expandFormat(format) {
        const fields = format.fields;
        const keys = ['chr', 'start', 'end'];

        for (let i = 0; i < fields.length; i++) {
          for (let key of keys) {
            if (key === fields[i]) {
              format[key] = i;
            }
          }
        }

        return format;
      }
    }

    function inferFileFormat(fn) {
      var idx, ext;
      fn = fn.toLowerCase(); // Special case -- UCSC refgene files

      if (fn.endsWith("refgene.txt.gz") || fn.endsWith("refgene.txt.bgz") || fn.endsWith("refgene.txt") || fn.endsWith("refgene.sorted.txt.gz") || fn.endsWith("refgene.sorted.txt.bgz")) {
        return "refgene";
      } //Strip parameters -- handle local files later


      idx = fn.indexOf("?");

      if (idx > 0) {
        fn = fn.substr(0, idx);
      } //Strip aux extensions .gz, .tab, and .txt


      if (fn.endsWith(".gz")) {
        fn = fn.substr(0, fn.length - 3);
      }

      if (fn.endsWith(".txt") || fn.endsWith(".tab") || fn.endsWith(".bgz")) {
        fn = fn.substr(0, fn.length - 4);
      }

      idx = fn.lastIndexOf(".");
      ext = idx < 0 ? fn : fn.substr(idx + 1);

      switch (ext) {
        case "bw":
          return "bigwig";

        case "bb":
          return "bigbed";

        default:
          if (knownFileExtensions.has(ext)) {
            return ext;
          } else {
            return undefined;
          }

      }
    }

    function inferIndexPath(url, extension) {
      if (isString$3(url)) {
        if (url.includes("?")) {
          const idx = url.indexOf("?");
          return url.substring(0, idx) + "." + extension + url.substring(idx);
        } else {
          return url + "." + extension;
        }
      } else {
        return undefined;
      }
    }

    function inferTrackType(config) {
      translateDeprecatedTypes(config);

      if (config.type) {
        return config.type;
      }

      if (config.format) {
        const format = config.format.toLowerCase();

        switch (format) {
          case "bw":
          case "bigwig":
          case "wig":
          case "bedgraph":
          case "tdf":
            return "wig";

          case "vcf":
            return "variant";

          case "seg":
            return "seg";

          case "mut":
          case "maf":
            return "mut";

          case "bam":
          case "cram":
            return "alignment";

          case "bedpe":
          case "bedpe-loop":
          case "biginteract":
            return "interact";

          case "bp":
            return "arc";

          case "gwas":
            return "gwas";

          case "bed":
          case "bigbed":
          case "bb":
          case "biggenepred":
          case "bignarrowpeak":
            return "bedtype";

          default:
            return "annotation";
        }
      }
    }

    function translateDeprecatedTypes(config) {
      if (config.featureType) {
        // Translate deprecated "feature" type
        config.type = config.type || config.featureType;
        config.featureType = undefined;
      }

      if ("junctions" === config.type) {
        config.type = "junction";
      } else if ("bed" === config.type) {
        config.type = "annotation";
        config.format = config.format || "bed";
      } else if ("annotations" === config.type) {
        config.type = "annotation";
      } else if ("alignments" === config.type) {
        config.type = "alignment";
      } else if ("bam" === config.type) {
        config.type = "alignment";
        config.format = "bam";
      } else if ("vcf" === config.type) {
        config.type = "variant";
        config.format = "vcf";
      } else if ("t2d" === config.type) {
        config.type = "gwas";
      } else if ("FusionJuncSpan" === config.type && !config.format) {
        config.format = "fusionjuncspan";
      } else if ("aed" === config.type) {
        config.type = "annotation";
        config.format = config.format || "aed";
      }
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    /**
     * Test if the given value is a string or number.  Not using typeof as it fails on boxed primitives.
     *
     * @param value
     * @returns boolean
     */


    function isSimpleType(value) {
      const simpleTypes = new Set(["boolean", "number", "string", "symbol"]);
      const valueType = typeof value;
      return value !== undefined && (simpleTypes.has(valueType) || value.substring || value.toFixed);
    }

    function buildOptions(config, options) {
      var defaultOptions = {
        oauthToken: config.oauthToken,
        headers: config.headers,
        withCredentials: config.withCredentials,
        filename: config.filename
      };
      return Object.assign(defaultOptions, options);
    }
    /**
     * isMobile test from http://detectmobilebrowsers.com
     * TODO -- improve UI design so this isn't neccessary
     * @returns {boolean}
     */
    // igv.isMobile = function () {
    //
    //     const a = (navigator.userAgent || navigator.vendor || window.opera);
    //     return (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) ||
    //         /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4)))
    //
    // }


    const doAutoscale = function (features) {
      var min, max;

      if (features.length > 0) {
        min = Number.MAX_VALUE;
        max = -Number.MAX_VALUE;
        features.forEach(function (f) {
          if (!Number.isNaN(f.value)) {
            min = Math.min(min, f.value);
            max = Math.max(max, f.value);
          }
        }); // Insure we have a zero baseline

        if (max > 0) min = Math.min(0, min);
        if (max < 0) max = 0;
      } else {
        // No features -- default
        min = 0;
        max = 100;
      }

      return {
        min: min,
        max: max
      };
    };

    const validateLocusExtent = function (chromosomeLengthBP, extent, minimumBP) {
      let ss = extent.start;
      let ee = extent.end;

      if (undefined === ee) {
        ss -= minimumBP / 2;
        ee = ss + minimumBP;

        if (ee > chromosomeLengthBP) {
          ee = chromosomeLengthBP;
          ss = ee - minimumBP;
        } else if (ss < 0) {
          ss = 0;
          ee = minimumBP;
        }
      } else if (ee - ss < minimumBP) {
        const center = (ee + ss) / 2;

        if (center - minimumBP / 2 < 0) {
          ss = 0;
          ee = ss + minimumBP;
        } else if (center + minimumBP / 2 > chromosomeLengthBP) {
          ee = chromosomeLengthBP;
          ss = ee - minimumBP;
        } else {
          ss = center - minimumBP / 2;
          ee = ss + minimumBP;
        }
      }

      extent.start = Math.ceil(ss);
      extent.end = Math.floor(ee);
    };
    /*!
     * is-number <https://github.com/jonschlinkert/is-number>
     *
     * Copyright (c) 2014-present, Jon Schlinkert.
     * Released under the MIT License.
     */


    const isNumber = function (num) {
      if (typeof num === 'number') {
        return num - num === 0;
      }

      if (typeof num === 'string' && num.trim() !== '') {
        return Number.isFinite ? Number.isFinite(+num) : isFinite(+num);
      }

      return false;
    };

    async function getFilename(url) {
      if (isString$3(url) && url.startsWith("https://drive.google.com")) {
        // This will fail if Google API key is not defined
        if (getApiKey() === undefined) {
          throw Error("Google drive is referenced, but API key is not defined.  An API key is required for Google Drive access");
        }

        const json = await getDriveFileInfo(url);
        return json.originalFileName || json.name;
      } else {
        return getFilename$1(url);
      }
    }

    function prettyBasePairNumber(raw) {
      var denom, units, value, floored;

      if (raw > 1e7) {
        denom = 1e6;
        units = " mb";
      } else if (raw > 1e4) {
        denom = 1e3;
        units = " kb";
        value = raw / denom;
        floored = Math.floor(value);
        return numberFormatter$1(floored) + units;
      } else {
        return numberFormatter$1(raw) + " bp";
      }

      value = raw / denom;
      floored = Math.floor(value);
      return floored.toString() + units;
    }

    function isDataURL(obj) {
      return isString$3(obj) && obj.startsWith("data:");
    }

    function createColumn(columnContainer, className) {
      const column = div$1({
        class: className
      });
      columnContainer.appendChild(column);
    }

    function insertElementBefore(element, referenceNode) {
      referenceNode.parentNode.insertBefore(element, referenceNode);
    }

    function insertElementAfter(element, referenceNode) {
      referenceNode.parentNode.insertBefore(element, referenceNode.nextSibling);
    }
    /**
     * Test to see if page is loaded in a secure context, that is by https or is localhost.
     */


    function isSecureContext() {
      return window.location.protocol === "https:" || window.location.hostname === "localhost";
    }

    const pairs = [['A', 'T'], ['G', 'C'], ['Y', 'R'], ['W', 'S'], ['K', 'M'], ['D', 'H'], ['B', 'V']];
    const complements = new Map();

    for (let p of pairs) {
      const p1 = p[0];
      const p2 = p[1];
      complements.set(p1, p2);
      complements.set(p2, p1);
      complements.set(p1.toLowerCase(), p2.toLowerCase());
      complements.set(p2.toLowerCase(), p1.toLowerCase());
    }

    function reverseComplementSequence(sequence) {
      let comp = '';
      let idx = sequence.length;

      while (idx-- > 0) {
        const base = sequence[idx];
        comp += complements.has(base) ? complements.get(base) : base;
      }

      return comp;
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const defaultSequenceTrackOrder = Number.MIN_SAFE_INTEGER;
    const translationDict = {
      'TTT': 'F',
      'TTC': 'F',
      'TTA': 'L',
      'TTG': 'L',
      'CTT': 'L',
      'CTC': 'L',
      'CTA': 'L',
      'CTG': 'L',
      'ATT': 'I',
      'ATC': 'I',
      'ATA': 'I',
      'ATG': 'M',
      'GTT': 'V',
      'GTC': 'V',
      'GTA': 'V',
      'GTG': 'V',
      'TCT': 'S',
      'TCC': 'S',
      'TCA': 'S',
      'TCG': 'S',
      'CCT': 'P',
      'CCC': 'P',
      'CCA': 'P',
      'CCG': 'P',
      'ACT': 'T',
      'ACC': 'T',
      'ACA': 'T',
      'ACG': 'T',
      'GCT': 'A',
      'GCC': 'A',
      'GCA': 'A',
      'GCG': 'A',
      'TAT': 'Y',
      'TAC': 'Y',
      'TAA': 'STOP',
      'TAG': 'STOP',
      'CAT': 'H',
      'CAC': 'H',
      'CAA': 'Q',
      'CAG': 'Q',
      'AAT': 'N',
      'AAC': 'N',
      'AAA': 'K',
      'AAG': 'K',
      'GAT': 'D',
      'GAC': 'D',
      'GAA': 'E',
      'GAG': 'E',
      'TGT': 'C',
      'TGC': 'C',
      'TGA': 'STOP',
      'TGG': 'W',
      'CGT': 'R',
      'CGC': 'R',
      'CGA': 'R',
      'CGG': 'R',
      'AGT': 'S',
      'AGC': 'S',
      'AGA': 'R',
      'AGG': 'R',
      'GGT': 'G',
      'GGC': 'G',
      'GGA': 'G',
      'GGG': 'G'
    };
    const complement = {};
    const t1 = ['A', 'G', 'C', 'T', 'Y', 'R', 'W', 'S', 'K', 'M', 'D', 'V', 'H', 'B', 'N', 'X'];
    const t2 = ['T', 'C', 'G', 'A', 'R', 'Y', 'W', 'S', 'M', 'K', 'H', 'B', 'D', 'V', 'N', 'X'];

    for (let i = 0; i < t1.length; i++) {
      complement[t1[i]] = t2[i];
      complement[t1[i].toLowerCase()] = t2[i].toLowerCase();
    }

    const DEFAULT_HEIGHT = 25;
    const TRANSLATED_HEIGHT = 115;
    const SEQUENCE_HEIGHT = 15;
    const FRAME_HEIGHT = 25;
    const FRAME_BORDER = 5;

    class SequenceTrack {
      constructor(config, browser) {
        this.type = "sequence";
        this.browser = browser;
        this.removable = false;
        this.config = config;
        this.name = "Sequence";
        this.id = "sequence";
        this.sequenceType = config.sequenceType || "dna"; //   dna | rna | prot

        this.disableButtons = false;
        this.order = config.order || defaultSequenceTrackOrder;
        this.ignoreTrackMenu = false;
        this.reversed = config.reversed === true;
        this.frameTranslate = config.frameTranslate === true;
        this.height = this.frameTranslate ? TRANSLATED_HEIGHT : DEFAULT_HEIGHT;
      }

      menuItemList() {
        return [{
          name: this.reversed ? "Forward" : "Reverse",
          click: () => {
            this.reversed = !this.reversed;
            this.trackView.repaintViews();
          }
        }, {
          name: this.frameTranslate ? "Close Translation" : "Three-frame Translate",
          click: () => {
            this.frameTranslate = !this.frameTranslate;

            if (this.frameTranslate) {
              for (let vp of this.trackView.viewports) {
                vp.setContentHeight(TRANSLATED_HEIGHT);
              }

              this.trackView.setTrackHeight(TRANSLATED_HEIGHT);
            } else {
              for (let vp of this.trackView.viewports) {
                vp.setContentHeight(DEFAULT_HEIGHT);
              }

              this.trackView.setTrackHeight(DEFAULT_HEIGHT);
            }

            this.trackView.repaintViews();
          }
        }];
      }

      contextMenuItemList(clickState) {
        const viewport = clickState.viewport;

        if (viewport.referenceFrame.bpPerPixel <= 1) {
          const pixelWidth = viewport.getWidth();
          const bpWindow = pixelWidth * viewport.referenceFrame.bpPerPixel;
          const chr = viewport.referenceFrame.chr;
          const start = Math.floor(viewport.referenceFrame.start);
          const end = Math.ceil(start + bpWindow);
          const items = [{
            label: this.reversed ? 'View visible sequence (reversed)...' : 'View visible sequence...',
            click: async () => {
              let seq = await this.browser.genome.sequence.getSequence(chr, start, end);

              if (this.reversed) {
                seq = reverseComplementSequence(seq);
              }

              Alert.presentAlert(seq);
            }
          }];

          if (isSecureContext()) {
            items.push({
              label: 'Copy visible sequence',
              click: async () => {
                let seq = await this.browser.genome.sequence.getSequence(chr, start, end);

                if (this.reversed) {
                  seq = reverseComplementSequence(seq);
                }

                try {
                  await navigator.clipboard.writeText(seq);
                } catch (e) {
                  console.error(e);
                  Alert.presentAlert(`error copying sequence to clipboard ${e}`);
                }
              }
            });
          }

          items.push('<hr/>');
          return items;
        } else {
          return undefined;
        }
      }

      translateSequence(seq) {
        const threeFrame = [[], [], []];

        for (let fNum of [0, 1, 2]) {
          let idx = fNum;

          while (seq.length - idx >= 3) {
            let st = seq.slice(idx, idx + 3);

            if (this.reversed) {
              st = st.split('').reverse().join('');
            }

            const aa = translationDict[st.toUpperCase()] || "";
            threeFrame[fNum].push({
              codons: st,
              aminoA: aa
            });
            idx += 3;
          }
        }

        return threeFrame;
      }

      async getFeatures(chr, start, end, bpPerPixel) {
        start = Math.floor(start);
        end = Math.floor(end);

        if (bpPerPixel && bpPerPixel > 1) {
          return null;
        } else {
          const sequence = await this.browser.genome.sequence.getSequence(chr, start, end);
          return {
            bpStart: start,
            sequence: sequence
          };
        }
      }

      draw(options) {
        const ctx = options.context;

        if (options.features) {
          let sequence = options.features.sequence;

          if (this.reversed) {
            sequence = sequence.split('').map(function (cv) {
              return complement[cv];
            }).join('');
          }

          const sequenceBpStart = options.features.bpStart; // genomic position at start of sequence

          const bpEnd = 1 + options.bpStart + options.pixelWidth * options.bpPerPixel;

          for (let bp = Math.floor(options.bpStart); bp <= bpEnd; bp++) {
            const seqIdx = Math.floor(bp - sequenceBpStart);

            if (seqIdx >= 0 && seqIdx < sequence.length) {
              const baseLetter = sequence[seqIdx];
              const offsetBP = bp - options.bpStart;
              const aPixel = offsetBP / options.bpPerPixel;
              const pixelWidth = 1 / options.bpPerPixel;
              const color = this.fillColor(baseLetter);

              if (options.bpPerPixel > 1 / 10) {
                IGVGraphics.fillRect(ctx, aPixel, 5, pixelWidth, SEQUENCE_HEIGHT - 5, {
                  fillStyle: color
                });
              } else {
                let textPixel = aPixel + 0.5 * (pixelWidth - ctx.measureText(baseLetter).width);
                IGVGraphics.strokeText(ctx, baseLetter, textPixel, SEQUENCE_HEIGHT, {
                  strokeStyle: color
                });
              }
            }
          }

          if (this.frameTranslate) {
            let y = SEQUENCE_HEIGHT + 2 * FRAME_BORDER;
            const translatedSequence = this.translateSequence(sequence);

            for (let fNum = 0; fNum < translatedSequence.length; fNum++) {
              // == 3, 1 for each frame
              const aaSequence = translatedSequence[fNum]; // AA sequence for this frame

              for (let idx = 0; idx < aaSequence.length; idx++) {
                let color = 0 === idx % 2 ? 'rgb(160,160,160)' : 'rgb(224,224,224)';
                const cv = aaSequence[idx];
                const bpPos = sequenceBpStart + fNum + idx * 3;
                const bpOffset = bpPos - options.bpStart;
                const p0 = Math.floor(bpOffset / options.bpPerPixel);
                const p1 = Math.floor((bpOffset + 3) / options.bpPerPixel);
                const pc = Math.round((p0 + p1) / 2);

                if (p1 < 0) {
                  continue; // off left edge
                } else if (p0 > options.pixelWidth) {
                  break; // off right edge
                }

                let aaLabel = cv.aminoA;

                if (cv.aminoA.indexOf('STOP') > -1) {
                  color = 'rgb(255, 0, 0)';
                  aaLabel = 'STOP'; //Color blind accessible
                } else if (cv.aminoA === 'M') {
                  color = 'rgb(0, 153, 0)';
                  aaLabel = 'START'; //Color blind accessible
                }

                IGVGraphics.fillRect(ctx, p0, y, p1 - p0, FRAME_HEIGHT, {
                  fillStyle: color
                });

                if (options.bpPerPixel <= 1 / 10) {
                  IGVGraphics.strokeText(ctx, aaLabel, pc - ctx.measureText(aaLabel).width / 2, y + 15);
                }
              }

              y += FRAME_HEIGHT + FRAME_BORDER;
            }
          }
        }
      }

      get supportsWholeGenome() {
        return false;
      }

      computePixelHeight(ignore) {
        this.height = this.frameTranslate ? TRANSLATED_HEIGHT : DEFAULT_HEIGHT;
        return this.height;
      }

      fillColor(index) {
        if (this.color) {
          return this.color;
        } else if ("dna" === this.sequenceType) {
          return this.browser.nucleotideColors[index] || 'gray';
        } else {
          return 'rgb(0, 0, 150)';
        }
      }
      /**
       * Return the current state of the track.  Used to create sessions and bookmarks.
       *
       * @returns {*|{}}
       */


      getState() {
        const config = {
          type: "sequence"
        };

        if (this.order !== defaultSequenceTrackOrder) {
          config.order = this.order;
        }

        if (this.reversed) {
          config.revealed = true;
        }

        return config;
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class Viewport {
      constructor(trackView, viewportColumn, referenceFrame, width) {
        this.guid = guid$2();
        this.trackView = trackView;
        this.referenceFrame = referenceFrame;
        this.browser = trackView.browser;
        this.$viewport = $$1('<div class="igv-viewport">');
        viewportColumn.appendChild(this.$viewport.get(0));

        if (trackView.track.height) {
          this.$viewport.get(0).style.height = `${trackView.track.height}px`;
        } // Create an alert dialog for the sequence track to copy ref sequence to.


        if (trackView.track instanceof SequenceTrack) {
          this.alert = new AlertDialog(this.$viewport.get(0));
        }

        this.$content = $$1("<div>", {
          class: 'igv-viewport-content'
        });
        this.$viewport.append(this.$content);
        this.$content.height(this.$viewport.height());
        this.contentDiv = this.$content.get(0); // this.$canvas = $('<canvas>')
        // this.$content.append(this.$canvas)
        //
        // this.canvas = this.$canvas.get(0)
        // this.ctx = this.canvas.getContext("2d")

        this.$viewport.width(width);
        this.initializationHelper();
      }

      initializationHelper() {}

      showMessage(message) {
        if (!this.messageDiv) {
          this.messageDiv = document.createElement('div');
          this.messageDiv.className = 'igv-viewport-message';
          this.contentDiv.append(this.messageDiv);
        }

        this.messageDiv.textContent = message;
        this.messageDiv.style.display = 'inline-block';
      }

      hideMessage(message) {
        if (this.messageDiv) this.messageDiv.style.display = 'none';
      }

      setTrackLabel(label) {}

      startSpinner() {}

      stopSpinner() {}

      checkZoomIn() {
        return true;
      }

      shift() {}

      setTop(contentTop) {
        const viewportHeight = this.$viewport.height();
        const viewTop = -contentTop;
        const viewBottom = viewTop + viewportHeight;
        this.$content.css('top', `${contentTop}px`);

        if (undefined === this.canvasVerticalRange || this.canvasVerticalRange.bottom < viewBottom || this.canvasVerticalRange.top > viewTop) {
          this.repaint();
        }
      }

      async loadFeatures() {
        return undefined;
      }

      async repaint() {
        console.log('Viewport - repaint()');
      }

      draw(drawConfiguration, features, roiFeatures) {
        console.log('Viewport - draw(drawConfiguration, features, roiFeatures)');
      }

      checkContentHeight(features) {
        let track = this.trackView.track;
        features = features || this.cachedFeatures;

        if ("FILL" === track.displayMode) {
          this.setContentHeight(this.$viewport.height());
        } else if (typeof track.computePixelHeight === 'function') {
          if (features && features.length > 0) {
            let requiredContentHeight = track.computePixelHeight(features);
            let currentContentHeight = this.$content.height();

            if (requiredContentHeight !== currentContentHeight) {
              this.setContentHeight(requiredContentHeight);
            }
          }
        }
      }

      getContentHeight() {
        return this.$content.height();
      }

      setContentHeight(contentHeight) {
        // Maximum height of a canvas is ~32,000 pixels on Chrome, possibly smaller on other platforms
        contentHeight = Math.min(contentHeight, 32000);
        this.$content.height(contentHeight);
      }

      isLoading() {
        return false;
      }

      saveSVG() {}

      isVisible() {
        return this.$viewport.width();
      }

      setWidth(width) {
        this.$viewport.width(width);
      }

      getWidth() {
        return this.$viewport.width();
      }

      getContentTop() {
        return this.contentDiv.offsetTop;
      }

      containsPosition(chr, position) {
        console.log('Viewport - containsPosition(chr, position)');
      }

      addMouseHandlers() {}

      removeMouseHandlers() {}
      /**
       * Called when the associated track is removed.  Do any needed cleanup here.
       */


      dispose() {
        if (this.popover) {
          this.popover.dispose();
        }

        this.$viewport.get(0).remove(); // Null out all properties -- this should not be neccessary, but just in case there is a
        // reference to self somewhere we want to free memory.

        for (let key of Object.keys(this)) {
          this[key] = undefined;
        }
      }

    }

    /*!!
     *  Canvas 2 Svg v1.0.19
     *  A low level canvas to SVG converter. Uses a mock canvas context to build an SVG document.
     *
     *  Licensed under the MIT license:
     *  http://www.opensource.org/licenses/mit-license.php
     *
     *  Author:
     *  Kerry Liu
     *
     *  Copyright (c) 2014 Gliffy Inc.
     */

    function format(str, args) {
      var keys = Object.keys(args),
          i;

      for (i = 0; i < keys.length; i++) {
        str = str.replace(new RegExp("\\{" + keys[i] + "\\}", "gi"), args[keys[i]]);
      }

      return str;
    } //helper function that generates a random string


    function randomString(holder) {
      var chars, randomstring, i;

      if (!holder) {
        throw new Error("cannot create a random attribute name for an undefined object");
      }

      chars = "ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
      randomstring = "";

      do {
        randomstring = "";

        for (i = 0; i < 12; i++) {
          randomstring += chars[Math.floor(Math.random() * chars.length)];
        }
      } while (holder[randomstring]);

      return randomstring;
    } //helper function to map named to numbered entities


    function createNamedToNumberedLookup(items, radix) {
      var i,
          entity,
          lookup = {},
          base10;
      items = items.split(',');
      radix = radix || 10; // Map from named to numbered entities.

      for (i = 0; i < items.length; i += 2) {
        entity = '&' + items[i + 1] + ';';
        base10 = parseInt(items[i], radix);
        lookup[entity] = '&#' + base10 + ';';
      } //FF and IE need to create a regex from hex values ie &nbsp; == \xa0


      lookup["\\xa0"] = '&#160;';
      return lookup;
    } //helper function to map canvas-textAlign to svg-textAnchor


    function getTextAnchor(textAlign) {
      //TODO: support rtl languages
      var mapping = {
        "left": "start",
        "right": "end",
        "center": "middle",
        "start": "start",
        "end": "end"
      };
      return mapping[textAlign] || mapping.start;
    } //helper function to map canvas-textBaseline to svg-dominantBaseline


    function getDominantBaseline(textBaseline) {
      //INFO: not supported in all browsers
      var mapping = {
        "alphabetic": "alphabetic",
        "hanging": "hanging",
        "top": "text-before-edge",
        "bottom": "text-after-edge",
        "middle": "central"
      };
      return mapping[textBaseline] || mapping.alphabetic;
    }
    /**
     * Return a new normalized vector of given vector
     */


    function normalize(vector) {
      var len = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]);
      return [vector[0] / len, vector[1] / len];
    }

    function intersectRect(rect1, rect2) {
      return rect1.x < rect2.x + rect2.width && rect1.x + rect1.width > rect2.x && rect1.y < rect2.y + rect2.height && rect1.y + rect1.height > rect2.y;
    } // Unpack entities lookup where the numbers are in radix 32 to reduce the size
    // entity mapping courtesy of tinymce


    const namedEntities = createNamedToNumberedLookup('50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' + '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' + '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' + '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' + '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' + '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' + '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' + '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' + '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' + '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' + 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' + 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' + 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' + 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' + 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' + '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' + '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' + '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' + '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' + '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' + 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' + 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' + 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' + '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' + '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32); //Some basic mappings for attributes and default values.

    const STYLES = {
      "strokeStyle": {
        svgAttr: "stroke",
        //corresponding svg attribute
        canvas: "#000000",
        //canvas default
        svg: "none",
        //svg default
        apply: "stroke" //apply on stroke() or fill()

      },
      "fillStyle": {
        svgAttr: "fill",
        canvas: "#000000",
        svg: null,
        //svg default is black, but we need to special case this to handle canvas stroke without fill
        apply: "fill"
      },
      "lineCap": {
        svgAttr: "stroke-linecap",
        canvas: "butt",
        svg: "butt",
        apply: "stroke"
      },
      "lineJoin": {
        svgAttr: "stroke-linejoin",
        canvas: "miter",
        svg: "miter",
        apply: "stroke"
      },
      "miterLimit": {
        svgAttr: "stroke-miterlimit",
        canvas: 10,
        svg: 4,
        apply: "stroke"
      },
      "lineWidth": {
        svgAttr: "stroke-width",
        canvas: 1,
        svg: 1,
        apply: "stroke"
      },
      "globalAlpha": {
        svgAttr: "opacity",
        canvas: 1,
        svg: 1,
        apply: "fill stroke"
      },
      "font": {
        //font converts to multiple svg attributes, there is custom logic for this
        canvas: "10px sans-serif"
      },
      "shadowColor": {
        canvas: "#000000"
      },
      "shadowOffsetX": {
        canvas: 0
      },
      "shadowOffsetY": {
        canvas: 0
      },
      "shadowBlur": {
        canvas: 0
      },
      "textAlign": {
        canvas: "start"
      },
      "textBaseline": {
        canvas: "alphabetic"
      },
      "lineDash": {
        svgAttr: "stroke-dasharray",
        canvas: [],
        svg: null,
        apply: "stroke"
      }
    };
    /**
     *
     * @param gradientNode - reference to the gradient
     * @constructor
     */

    class CanvasGradient {
      constructor(gradientNode, ctx) {
        this.__root = gradientNode;
        this.__ctx = ctx;
      }
      /**
       * Adds a color stop to the gradient root
       */


      addColorStop(offset, color) {
        var stop = this.__ctx.__createElement("stop"),
            regex,
            matches;

        stop.setAttribute("offset", offset);

        if (color && color.indexOf("rgba") !== -1) {
          //separate alpha value, since webkit can't handle it
          regex = /rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d?\.?\d*)\s*\)/gi;
          matches = regex.exec(color);
          stop.setAttribute("stop-color", format("rgb({r},{g},{b})", {
            r: matches[1],
            g: matches[2],
            b: matches[3]
          }));
          stop.setAttribute("stop-opacity", matches[4]);
        } else {
          stop.setAttribute("stop-color", color);
        }

        this.__root.appendChild(stop);
      }

    }

    class CanvasPattern {
      constructor(pattern, ctx) {
        this.__root = pattern;
        this.__ctx = ctx;
      }

    }
    /**
     * The mock canvas context
     * @param config - options include:
     * ctx - existing Context2D to wrap around
     * width - width of your canvas (defaults to 500)
     * height - height of your canvas (defaults to 500)
     * enableMirroring - enables canvas mirroring (get image data) (defaults to false)
     * document - the document object (defaults to the current document)
     */


    class ctx {
      constructor(config) {
        if (!(this instanceof ctx)) {
          //did someone call this without new?
          return new ctx(config);
        } // clone config


        this.config = config; //setup options

        this.width = config.width;
        this.height = config.height;
        this.enableMirroring = config.enableMirroring || false;
        this.canvas = this; ///point back to this instance!

        this.__document = document; // allow passing in an existing context to wrap around
        // if a context is passed in, we know a canvas already exist

        if (config.ctx) {
          this.__ctx = config.ctx;
        } else {
          this.__canvas = this.__document.createElement("canvas");
          this.__ctx = this.__canvas.getContext("2d");
        } // give this canvas a type


        this.isSVG = true;

        this.__setDefaultStyles();

        this.__stack = [this.__getStyleState()];
        this.__groupStack = []; // root svg element

        this.__root = this.__createElement("svg");

        this.__root.setAttribute("width", this.width);

        this.__root.setAttribute("height", this.height); // allow contents to overflow svg bbox


        this.__root.setAttribute('overflow', 'visible'); // viewbox


        if (config.viewbox) {
          const str = config.viewbox.x + ' ' + config.viewbox.y + ' ' + config.viewbox.width + ' ' + config.viewbox.height;

          this.__root.setAttribute("viewBox", str);

          this.viewbox = config.viewbox;
        } // make sure we don't generate the same ids in defs


        this.__ids = {}; // defs

        this.__defs = this.__createElement("defs");

        this.__root.appendChild(this.__defs);

        this.multiLocusGap = config.multiLocusGap; // svg background color

        let backdropConfig = {
          id: 'svg_output_backdrop',
          width: '100%',
          height: '100%',
          fill: config.backdropColor || 'white'
        };

        let backdropRect = this.__createElement('rect', backdropConfig);

        this.__root.appendChild(backdropRect); // root group


        this.__rootGroup = this.__createElement('g', {
          id: 'root-group'
        });

        this.__root.appendChild(this.__rootGroup); // point current element to root group


        this.__currentElement = this.__rootGroup;
      }

      setWidth(width) {
        this.width = width;

        this.__root.setAttribute("width", this.width);

        const str = this.config.viewbox.x + ' ' + this.config.viewbox.y + ' ' + width + ' ' + this.config.viewbox.height;

        this.__root.setAttribute("viewBox", str);
      }

      setHeight(height) {
        this.height = height;

        this.__root.setAttribute("height", this.height);

        const str = this.config.viewbox.x + ' ' + this.config.viewbox.y + ' ' + this.config.viewbox.width + ' ' + height;

        this.__root.setAttribute("viewBox", str);
      }

      /**
       * Creates the specified svg element
       * @private
       */
      __createElement(elementName, properties, resetFill) {
        if (typeof properties === "undefined") {
          properties = {};
        }

        let element = this.__document.createElementNS("http://www.w3.org/2000/svg", elementName);

        if (resetFill) {
          //if fill or stroke is not specified, the svg element should not display. By default SVG's fill is black.
          element.setAttribute("fill", "none");
          element.setAttribute("stroke", "none");
        }

        for (let key of Object.keys(properties)) {
          element.setAttribute(key, properties[key]);
        }

        return element;
      }

      /**
       * Applies default canvas styles to the context
       * @private
       */
      __setDefaultStyles() {
        //default 2d canvas context properties see:http://www.w3.org/TR/2dcontext/
        var keys = Object.keys(STYLES),
            i,
            key;

        for (i = 0; i < keys.length; i++) {
          key = keys[i];
          this[key] = STYLES[key].canvas;
        }
      }

      /**
       * Applies styles on restore
       * @param styleState
       * @private
       */
      __applyStyleState(styleState) {
        var keys = Object.keys(styleState),
            i,
            key;

        for (i = 0; i < keys.length; i++) {
          key = keys[i];
          this[key] = styleState[key];
        }
      }

      /**
       * Gets the current style state
       * @return {Object}
       * @private
       */
      __getStyleState() {
        var i,
            styleState = {},
            keys = Object.keys(STYLES),
            key;

        for (i = 0; i < keys.length; i++) {
          key = keys[i];
          styleState[key] = this[key];
        }

        return styleState;
      }

      /**
       * Apples the current styles to the current SVG element. On "ctx.fill" or "ctx.stroke"
       * @param type
       * @private
       */
      __applyStyleToCurrentElement(type) {
        var currentElement = this.__currentElement;
        var currentStyleGroup = this.__currentElementsToStyle;

        if (currentStyleGroup) {
          currentElement.setAttribute(type, "");
          currentElement = currentStyleGroup.element;
          currentStyleGroup.children.forEach(function (node) {
            node.setAttribute(type, "");
          });
        }

        var keys = Object.keys(STYLES),
            i,
            style,
            value,
            id,
            regex,
            matches;

        for (i = 0; i < keys.length; i++) {
          style = STYLES[keys[i]];
          value = this[keys[i]];

          if (style.apply) {
            //is this a gradient or pattern?
            if (value instanceof CanvasPattern) {
              //pattern
              if (value.__ctx) {
                //copy over defs
                while (value.__ctx.__defs.childNodes.length) {
                  id = value.__ctx.__defs.childNodes[0].getAttribute("id");
                  this.__ids[id] = id;

                  this.__defs.appendChild(value.__ctx.__defs.childNodes[0]);
                }
              }

              currentElement.setAttribute(style.apply, format("url(#{id})", {
                id: value.__root.getAttribute("id")
              }));
            } else if (value instanceof CanvasGradient) {
              //gradient
              currentElement.setAttribute(style.apply, format("url(#{id})", {
                id: value.__root.getAttribute("id")
              }));
            } else if (style && style.apply.indexOf(type) !== -1 && style.svg !== value) {
              if ((style.svgAttr === "stroke" || style.svgAttr === "fill") && value && value.indexOf("rgba") !== -1) {
                //separate alpha value, since illustrator can't handle it
                regex = /rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d?\.?\d*)\s*\)/gi;
                matches = regex.exec(value);
                currentElement.setAttribute(style.svgAttr, format("rgb({r},{g},{b})", {
                  r: matches[1],
                  g: matches[2],
                  b: matches[3]
                })); //should take globalAlpha here

                var opacity = matches[4];
                var globalAlpha = this.globalAlpha;

                if (globalAlpha != null) {
                  opacity *= globalAlpha;
                }

                currentElement.setAttribute(style.svgAttr + "-opacity", opacity);
              } else {
                var attr = style.svgAttr;

                if (keys[i] === 'globalAlpha') {
                  attr = type + '-' + style.svgAttr;

                  if (currentElement.getAttribute(attr)) {
                    //fill-opacity or stroke-opacity has already been set by stroke or fill.
                    continue;
                  }
                } //otherwise only update attribute if right type, and not svg default


                currentElement.setAttribute(attr, value);
              }
            }
          }
        }
      }

      /**
       * Will return the closest group or svg node. May return the current element.
       * @private
       */
      __closestGroupOrSvg(node) {
        node = node || this.__currentElement;

        if (node.nodeName === "g" || node.nodeName === "svg") {
          return node;
        } else {
          return this.__closestGroupOrSvg(node.parentNode);
        }
      }

      /**
       * Returns the serialized value of the svg so far
       * @param fixNamedEntities - Standalone SVG doesn't support named entities, which document.createTextNode encodes.
       *                           If true, we attempt to find all named entities and encode it as a numeric entity.
       * @return serialized svg
       */
      getSerializedSvg(fixNamedEntities) {
        var serialized = new XMLSerializer().serializeToString(this.__root),
            keys,
            i,
            key,
            value,
            regexp;
     //IE search for a duplicate xmnls because they didn't implement setAttributeNS correctly
        // xmlns = /xmlns="http:\/\/www\.w3\.org\/2000\/svg".+xmlns="http:\/\/www\.w3\.org\/2000\/svg/gi;
        // if (xmlns.test(serialized)) {
        //     serialized = serialized.replace('xmlns="http://www.w3.org/2000/svg','xmlns:xlink="http://www.w3.org/1999/xlink');
        // }

        if (fixNamedEntities) {
          keys = Object.keys(namedEntities); //loop over each named entity and replace with the proper equivalent.

          for (i = 0; i < keys.length; i++) {
            key = keys[i];
            value = namedEntities[key];
            regexp = new RegExp(key, "gi");

            if (regexp.test(serialized)) {
              serialized = serialized.replace(regexp, value);
            }
          }
        }

        return serialized;
      }

      /**
       * Returns the root svg
       * @return
       */
      getSvg() {
        return this.__root;
      }

      /**
       * Will generate a group tag.
       */
      saveWithTranslationAndClipRect(id, tx, ty, width, height, clipYOffset) {
        // clip rect
        const clip_id = `${id}_clip_rect`;

        let clipPath = this.__createElement('clipPath', {
          id: clip_id
        });

        this.__defs.appendChild(clipPath);

        const config = {
          x: '0',
          y: clipYOffset.toString(),
          width: width.toString(),
          height: height.toString()
        };
        clipPath.appendChild(this.__createElement('rect', config));

        const group = this.__createElement("g");

        group.setAttribute('transform', format('translate({x},{y})', {
          x: tx,
          y: ty
        }));
        group.setAttribute('clip-path', format('url(#{id})', {
          id: clip_id
        }));

        const parent = this.__closestGroupOrSvg();

        parent.appendChild(group);

        this.__groupStack.push(parent);

        this.__currentElement = group;

        this.__stack.push(this.__getStyleState());
      }

      save() {
        var group = this.__createElement("g");

        var parent = this.__closestGroupOrSvg();

        this.__groupStack.push(parent);

        parent.appendChild(group);
        this.__currentElement = group;

        this.__stack.push(this.__getStyleState());
      }

      /**
       * Sets current element to parent, or just root if already root
       */
      restore() {
        this.__currentElement = this.__groupStack.pop();
        this.__currentElementsToStyle = null; //Clearing canvas will make the poped group invalid, currentElement is set to the root group node.

        if (!this.__currentElement) {
          this.__currentElement = this.__root.childNodes[1];
        }

        var state = this.__stack.pop();

        this.__applyStyleState(state);
      }

      /**
       * Helper method to add transform
       * @private
       */
      __addTransform(t) {
        //if the current element has siblings, add another group
        var parent = this.__closestGroupOrSvg();

        if (parent.childNodes.length > 0) {
          if (this.__currentElement.nodeName === "path") {
            if (!this.__currentElementsToStyle) this.__currentElementsToStyle = {
              element: parent,
              children: []
            };

            this.__currentElementsToStyle.children.push(this.__currentElement);

            this.__applyCurrentDefaultPath();
          }

          var group = this.__createElement("g");

          parent.appendChild(group);
          this.__currentElement = group;
        }

        var transform = this.__currentElement.getAttribute("transform");

        if (transform) {
          transform += " ";
        } else {
          transform = "";
        }

        transform += t;

        this.__currentElement.setAttribute("transform", transform);
      }

      addTrackGroupWithTranslationAndClipRect(id, tx, ty, width, height, clipYOffset) {
        // clip rect
        const clip_id = id + '_clip_rect';

        let clipPath = this.__createElement('clipPath', {
          id: clip_id
        });

        this.__defs.appendChild(clipPath);

        clipPath.appendChild(this.__createElement('rect', {
          x: '0',
          y: clipYOffset.toString(),
          width: width.toString(),
          height: height.toString()
        }));

        let group = this.__createElement('g');

        this.__rootGroup.appendChild(group);

        group.setAttribute('transform', format('translate({x},{y})', {
          x: tx,
          y: ty
        }));
        group.setAttribute('id', id + '_group'); // add clip rect

        group.setAttribute('clip-path', format('url(#{id})', {
          id: clip_id
        }));
        this.__currentElement = group;
      }

      /**
       *  scales the current element
       */
      scale(x, y) {
        if (y === undefined) {
          y = x;
        }

        this.__addTransform(format("scale({x},{y})", {
          x: x,
          y: y
        }));
      }

      /**
       * rotates the current element
       */
      rotate(angle) {
        var degrees = angle * 180 / Math.PI;

        this.__addTransform(format("rotate({angle},{cx},{cy})", {
          angle: degrees,
          cx: 0,
          cy: 0
        }));
      }

      /**
       * translates the current element
       */
      translate(x, y) {
        this.__addTransform(format("translate({x},{y})", {
          x: x,
          y: y
        }));
      }

      /**
       * applies a transform to the current element
       */
      transform(a, b, c, d, e, f) {
        this.__addTransform(format("matrix({a},{b},{c},{d},{e},{f})", {
          a: a,
          b: b,
          c: c,
          d: d,
          e: e,
          f: f
        }));
      }

      /**
       * Create a new Path Element
       */
      beginPath() {
        var path, parent; // Note that there is only one current default path, it is not part of the drawing state.
        // See also: https://html.spec.whatwg.org/multipage/scripting.html#current-default-path

        this.__currentDefaultPath = "";
        this.__currentPosition = {};
        path = this.__createElement("path", {}, true);
        parent = this.__closestGroupOrSvg();
        parent.appendChild(path);
        this.__currentElement = path;
      }

      /**
       * Helper function to apply currentDefaultPath to current path element
       * @private
       */
      __applyCurrentDefaultPath() {
        var currentElement = this.__currentElement;

        if (currentElement.nodeName === "path") {
          currentElement.setAttribute("d", this.__currentDefaultPath);
        } else {
          console.error("Attempted to apply path command to node", currentElement.nodeName);
        }
      }

      /**
       * Helper function to add path command
       * @private
       */
      __addPathCommand(command) {
        this.__currentDefaultPath += " ";
        this.__currentDefaultPath += command;
      }

      /**
       * Adds the move command to the current path element,
       * if the currentPathElement is not empty create a new path element
       */
      moveTo(x, y) {
        if (this.__currentElement.nodeName !== "path") {
          this.beginPath();
        } // creates a new subpath with the given point


        this.__currentPosition = {
          x: x,
          y: y
        };

        this.__addPathCommand(format("M {x} {y}", {
          x: x,
          y: y
        }));
      }

      /**
       * Closes the current path
       */
      closePath() {
        if (this.__currentDefaultPath) {
          this.__addPathCommand("Z");
        }
      }

      /**
       * Adds a line to command
       */
      lineTo(x, y) {
        this.__currentPosition = {
          x: x,
          y: y
        };

        if (this.__currentDefaultPath && this.__currentDefaultPath.indexOf('M') > -1) {
          this.__addPathCommand(format("L {x} {y}", {
            x: x,
            y: y
          }));
        } else {
          this.__addPathCommand(format("M {x} {y}", {
            x: x,
            y: y
          }));
        }
      }

      /**
       * Add a bezier command
       */
      bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
        this.__currentPosition = {
          x: x,
          y: y
        };

        this.__addPathCommand(format("C {cp1x} {cp1y} {cp2x} {cp2y} {x} {y}", {
          cp1x: cp1x,
          cp1y: cp1y,
          cp2x: cp2x,
          cp2y: cp2y,
          x: x,
          y: y
        }));
      }

      /**
       * Adds a quadratic curve to command
       */
      quadraticCurveTo(cpx, cpy, x, y) {
        this.__currentPosition = {
          x: x,
          y: y
        };

        this.__addPathCommand(format("Q {cpx} {cpy} {x} {y}", {
          cpx: cpx,
          cpy: cpy,
          x: x,
          y: y
        }));
      }

      /**
       * Adds the arcTo to the current path
       *
       * @see http://www.w3.org/TR/2015/WD-2dcontext-20150514/#dom-context-2d-arcto
       */
      arcTo(x1, y1, x2, y2, radius) {
        // Let the point (x0, y0) be the last point in the subpath.
        var x0 = this.__currentPosition && this.__currentPosition.x;
        var y0 = this.__currentPosition && this.__currentPosition.y; // First ensure there is a subpath for (x1, y1).

        if (typeof x0 == "undefined" || typeof y0 == "undefined") {
          return;
        } // Negative values for radius must cause the implementation to throw an IndexSizeError exception.


        if (radius < 0) {
          throw new Error("IndexSizeError: The radius provided (" + radius + ") is negative.");
        } // If the point (x0, y0) is equal to the point (x1, y1),
        // or if the point (x1, y1) is equal to the point (x2, y2),
        // or if the radius radius is zero,
        // then the method must add the point (x1, y1) to the subpath,
        // and connect that point to the previous point (x0, y0) by a straight line.


        if (x0 === x1 && y0 === y1 || x1 === x2 && y1 === y2 || radius === 0) {
          this.lineTo(x1, y1);
          return;
        } // Otherwise, if the points (x0, y0), (x1, y1), and (x2, y2) all lie on a single straight line,
        // then the method must add the point (x1, y1) to the subpath,
        // and connect that point to the previous point (x0, y0) by a straight line.


        var unit_vec_p1_p0 = normalize([x0 - x1, y0 - y1]);
        var unit_vec_p1_p2 = normalize([x2 - x1, y2 - y1]);

        if (unit_vec_p1_p0[0] * unit_vec_p1_p2[1] === unit_vec_p1_p0[1] * unit_vec_p1_p2[0]) {
          this.lineTo(x1, y1);
          return;
        } // Otherwise, let The Arc be the shortest arc given by circumference of the circle that has radius radius,
        // and that has one point tangent to the half-infinite line that crosses the point (x0, y0) and ends at the point (x1, y1),
        // and that has a different point tangent to the half-infinite line that ends at the point (x1, y1), and crosses the point (x2, y2).
        // The points at which this circle touches these two lines are called the start and end tangent points respectively.
        // note that both vectors are unit vectors, so the length is 1


        var cos = unit_vec_p1_p0[0] * unit_vec_p1_p2[0] + unit_vec_p1_p0[1] * unit_vec_p1_p2[1];
        var theta = Math.acos(Math.abs(cos)); // Calculate origin

        var unit_vec_p1_origin = normalize([unit_vec_p1_p0[0] + unit_vec_p1_p2[0], unit_vec_p1_p0[1] + unit_vec_p1_p2[1]]);
        var len_p1_origin = radius / Math.sin(theta / 2);
        var x = x1 + len_p1_origin * unit_vec_p1_origin[0];
        var y = y1 + len_p1_origin * unit_vec_p1_origin[1]; // Calculate start angle and end angle
        // rotate 90deg clockwise (note that y axis points to its down)

        var unit_vec_origin_start_tangent = [-unit_vec_p1_p0[1], unit_vec_p1_p0[0]]; // rotate 90deg counter clockwise (note that y axis points to its down)

        var unit_vec_origin_end_tangent = [unit_vec_p1_p2[1], -unit_vec_p1_p2[0]];

        var getAngle = function (vector) {
          // get angle (clockwise) between vector and (1, 0)
          var x = vector[0];
          var y = vector[1];

          if (y >= 0) {
            // note that y axis points to its down
            return Math.acos(x);
          } else {
            return -Math.acos(x);
          }
        };

        var startAngle = getAngle(unit_vec_origin_start_tangent);
        var endAngle = getAngle(unit_vec_origin_end_tangent); // Connect the point (x0, y0) to the start tangent point by a straight line

        this.lineTo(x + unit_vec_origin_start_tangent[0] * radius, y + unit_vec_origin_start_tangent[1] * radius); // Connect the start tangent point to the end tangent point by arc
        // and adding the end tangent point to the subpath.

        this.arc(x, y, radius, startAngle, endAngle);
      }

      /**
       * Sets the stroke property on the current element
       */
      stroke() {
        if (this.__currentElement.nodeName === "path") {
          this.__currentElement.setAttribute("paint-order", "fill stroke markers");
        }

        this.__applyCurrentDefaultPath();

        this.__applyStyleToCurrentElement("stroke");
      }

      /**
       * Sets fill properties on the current element
       */
      fill() {
        if (this.__currentElement.nodeName === "path") {
          this.__currentElement.setAttribute("paint-order", "stroke fill markers");
        }

        this.__applyCurrentDefaultPath();

        this.__applyStyleToCurrentElement("fill");
      }

      /**
       *  Adds a rectangle to the path.
       */
      rect(x, y, width, height) {
        if (this.__currentElement.nodeName !== "path") {
          this.beginPath();
        }

        this.moveTo(x, y);
        this.lineTo(x + width, y);
        this.lineTo(x + width, y + height);
        this.lineTo(x, y + height);
        this.lineTo(x, y);
        this.closePath();
      }

      /**
       * adds a rectangle element
       */
      fillRect(x, y, width, height) {
        if (height < 0) {
          y += height;
          height = -height;
        }

        if (width < 0) {
          x += width;
          width = -width;
        } // See if rect intersects current viewbox


        var r2 = {
          x: x,
          y: y,
          width: width,
          height: height
        };

        if (this.viewbox) {
          if (!intersectRect(this.viewbox, r2)) {
            return;
          }
        }

        var rect, parent;
        rect = this.__createElement("rect", r2, true);
        parent = this.__closestGroupOrSvg();
        parent.appendChild(rect);
        this.__currentElement = rect;

        this.__applyStyleToCurrentElement("fill");
      }

      /**
       * Draws a rectangle with no fill
       * @param x
       * @param y
       * @param width
       * @param height
       */
      strokeRect(x, y, width, height) {
        var rect, parent;
        rect = this.__createElement("rect", {
          x: x,
          y: y,
          width: width,
          height: height
        }, true);
        parent = this.__closestGroupOrSvg();
        parent.appendChild(rect);
        this.__currentElement = rect;

        this.__applyStyleToCurrentElement("stroke");
      }

      // stroke ellipse
      strokeEllipse(cx, cy, rx, ry, rotation, startAngle, endAngle, isCCW) {
        this.__ellipse(cx, cy, rx, ry, rotation, startAngle, endAngle, isCCW, 'stroke');
      } // fill ellipse


      fillEllipse(cx, cy, rx, ry, rotation, startAngle, endAngle, isCCW) {
        this.__ellipse(cx, cy, rx, ry, rotation, startAngle, endAngle, isCCW, 'fill');
      } // ellipse helper


      __ellipse(cx, cy, rx, ry, rotation, startAngle, endAngle, isCCW, style) {
        const config = {
          cx,
          cy,
          rx,
          ry
        };

        const element = this.__createElement('ellipse', config, true);

        const parent = this.__closestGroupOrSvg();

        parent.appendChild(element);
        this.__currentElement = element;

        this.__applyStyleToCurrentElement(style);
      }
      /**
       * Clear entire canvas:
       * 1. save current transforms
       * 2. remove all the childNodes of the root g element
       */


      __clearCanvas() {
        var current = this.__closestGroupOrSvg(),
            transform = current.getAttribute("transform");

        var rootGroup = this.__root.childNodes[1];
        var childNodes = rootGroup.childNodes;

        for (var i = childNodes.length - 1; i >= 0; i--) {
          if (childNodes[i]) {
            rootGroup.removeChild(childNodes[i]);
          }
        }

        this.__currentElement = rootGroup; //reset __groupStack as all the child group nodes are all removed.

        this.__groupStack = [];

        if (transform) {
          this.__addTransform(transform);
        }
      }

      /**
       * "Clears" a canvas by just drawing a white rectangle in the current group.
       */
      clearRect(x, y, width, height) {
        //clear entire canvas
        if (x === 0 && y === 0 && width === this.width && height === this.height) {
          this.__clearCanvas();

          return;
        }

        var rect,
            parent = this.__closestGroupOrSvg();

        rect = this.__createElement("rect", {
          x: x,
          y: y,
          width: width,
          height: height,
          fill: "#FFFFFF"
        }, true);
        parent.appendChild(rect);
      }

      /**
       * Adds a linear gradient to a defs tag.
       * Returns a canvas gradient object that has a reference to it's parent def
       */
      createLinearGradient(x1, y1, x2, y2) {
        var grad = this.__createElement("linearGradient", {
          id: randomString(this.__ids),
          x1: x1 + "px",
          x2: x2 + "px",
          y1: y1 + "px",
          y2: y2 + "px",
          "gradientUnits": "userSpaceOnUse"
        }, false);

        this.__defs.appendChild(grad);

        return new CanvasGradient(grad, this);
      }

      /**
       * Adds a radial gradient to a defs tag.
       * Returns a canvas gradient object that has a reference to it's parent def
       */
      createRadialGradient(x0, y0, r0, x1, y1, r1) {
        var grad = this.__createElement("radialGradient", {
          id: randomString(this.__ids),
          cx: x1 + "px",
          cy: y1 + "px",
          r: r1 + "px",
          fx: x0 + "px",
          fy: y0 + "px",
          "gradientUnits": "userSpaceOnUse"
        }, false);

        this.__defs.appendChild(grad);

        return new CanvasGradient(grad, this);
      }

      /**
       * Parses the font string and returns svg mapping
       * @private
       */
      __parseFont() {
        var regex = /^\s*(?=(?:(?:[-a-z]+\s*){0,2}(italic|oblique))?)(?=(?:(?:[-a-z]+\s*){0,2}(small-caps))?)(?=(?:(?:[-a-z]+\s*){0,2}(bold(?:er)?|lighter|[1-9]00))?)(?:(?:normal|\1|\2|\3)\s*){0,3}((?:xx?-)?(?:small|large)|medium|smaller|larger|[.\d]+(?:\%|in|[cem]m|ex|p[ctx]))(?:\s*\/\s*(normal|[.\d]+(?:\%|in|[cem]m|ex|p[ctx])))?\s*([-,\'\"\sa-z0-9]+?)\s*$/i;
        var fontPart = regex.exec(this.font);
        var data = {
          style: fontPart[1] || 'normal',
          size: fontPart[4] || '10px',
          family: fontPart[6] || 'sans-serif',
          weight: fontPart[3] || 'normal',
          decoration: fontPart[2] || 'normal',
          href: null
        }; //canvas doesn't support underline natively, but we can pass this attribute

        if (this.__fontUnderline === "underline") {
          data.decoration = "underline";
        } //canvas also doesn't support linking, but we can pass this as well


        if (this.__fontHref) {
          data.href = this.__fontHref;
        }

        return data;
      }

      /**
       * Helper to link text fragments
       * @param font
       * @param element
       * @return {*}
       * @private
       */
      __wrapTextLink(font, element) {
        if (font.href) {
          var a = this.__createElement("a");

          a.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", font.href);
          a.appendChild(element);
          return a;
        }

        return element;
      }

      /**
       * Fills or strokes text
       * @param text
       * @param x
       * @param y
       * @param action - stroke or fill
       * @private
       */
      __applyText(text, x, y, action) {
        var font = this.__parseFont(),
            parent = this.__closestGroupOrSvg(),
            textElement = this.__createElement("text", {
          "font-family": font.family,
          "font-size": font.size,
          "font-style": font.style,
          "font-weight": font.weight,
          "text-decoration": font.decoration,
          "x": x,
          "y": y,
          "text-anchor": getTextAnchor(this.textAlign),
          "dominant-baseline": getDominantBaseline(this.textBaseline)
        }, true);

        textElement.appendChild(this.__document.createTextNode(text));
        this.__currentElement = textElement;

        this.__applyStyleToCurrentElement(action);

        parent.appendChild(this.__wrapTextLink(font, textElement));
      }

      /**
       * Creates a text element
       * @param text
       * @param x
       * @param y
       */
      fillText(text, x, y) {
        this.__applyText(text, x, y, "fill");
      }

      /**
       * Strokes text
       * @param text
       * @param x
       * @param y
       */
      strokeText(text, x, y) {
        this.__applyText(text, x, y, "stroke");
      }

      /**
       * No need to implement this for svg.
       * @param text
       * @return {TextMetrics}
       */
      measureText(text) {
        this.__ctx.font = this.font;
        return this.__ctx.measureText(text);
      }

      /**
       *  Arc command!
       */
      arc(x, y, radius, startAngle, endAngle, counterClockwise) {
        // in canvas no circle is drawn if no angle is provided.
        if (startAngle === endAngle) {
          return;
        }

        startAngle = startAngle % (2 * Math.PI);
        endAngle = endAngle % (2 * Math.PI);

        if (startAngle === endAngle) {
          //circle time! subtract some of the angle so svg is happy (svg elliptical arc can't draw a full circle)
          endAngle = (endAngle + 2 * Math.PI - 0.001 * (counterClockwise ? -1 : 1)) % (2 * Math.PI);
        }

        var endX = x + radius * Math.cos(endAngle),
            endY = y + radius * Math.sin(endAngle),
            startX = x + radius * Math.cos(startAngle),
            startY = y + radius * Math.sin(startAngle),
            sweepFlag = counterClockwise ? 0 : 1,
            largeArcFlag = 0,
            diff = endAngle - startAngle; // https://github.com/gliffy/canvas2svg/issues/4

        if (diff < 0) {
          diff += 2 * Math.PI;
        }

        if (counterClockwise) {
          largeArcFlag = diff > Math.PI ? 0 : 1;
        } else {
          largeArcFlag = diff > Math.PI ? 1 : 0;
        }

        this.lineTo(startX, startY);

        this.__addPathCommand(format("A {rx} {ry} {xAxisRotation} {largeArcFlag} {sweepFlag} {endX} {endY}", {
          rx: radius,
          ry: radius,
          xAxisRotation: 0,
          largeArcFlag: largeArcFlag,
          sweepFlag: sweepFlag,
          endX: endX,
          endY: endY
        }));

        this.__currentPosition = {
          x: endX,
          y: endY
        };
      }

      /**
       * The ellipse() method creates an elliptical arc centered at (x, y) with the radii radiusX and radiusY. The path
       * starts at startAngle and ends at endAngle, and travels in the direction given by counterclockwise (defaulting to clockwise).
       */
      // ellipse (x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) {
      //     // TODO -- implement
      // }

      /**
       * Generates a ClipPath from the clip command.
       */
      clip() {
        var group = this.__closestGroupOrSvg(),
            clipPath = this.__createElement("clipPath"),
            id = randomString(this.__ids),
            newGroup = this.__createElement("g");

        this.__applyCurrentDefaultPath();

        group.removeChild(this.__currentElement);
        clipPath.setAttribute("id", id);
        clipPath.appendChild(this.__currentElement);

        this.__defs.appendChild(clipPath); //set the clip path to this group


        group.setAttribute("clip-path", format("url(#{id})", {
          id: id
        })); //clip paths can be scaled and transformed, we need to add another wrapper group to avoid later transformations
        // to this path

        group.appendChild(newGroup);
        this.__currentElement = newGroup;
      }

      /**
       * Draws a canvas, image or mock context to this canvas.
       * Note that all svg dom manipulation uses node.childNodes rather than node.children for IE support.
       * http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-drawimage
       */
      drawImage() {
        //convert arguments to a real array
        var args = Array.prototype.slice.call(arguments),
            image = args[0],
            dx,
            dy,
            dw,
            dh,
            sx = 0,
            sy = 0,
            sw,
            sh,
            parent,
            svg,
            defs,
            group,
            svgImage,
            canvas,
            context,
            id;

        if (args.length === 3) {
          dx = args[1];
          dy = args[2];
          sw = image.width;
          sh = image.height;
          dw = sw;
          dh = sh;
        } else if (args.length === 5) {
          dx = args[1];
          dy = args[2];
          dw = args[3];
          dh = args[4];
          sw = image.width;
          sh = image.height;
        } else if (args.length === 9) {
          sx = args[1];
          sy = args[2];
          sw = args[3];
          sh = args[4];
          dx = args[5];
          dy = args[6];
          dw = args[7];
          dh = args[8];
        } else {
          throw new Error("Invalid number of arguments passed to drawImage: " + arguments.length);
        }

        parent = this.__closestGroupOrSvg();
        var translateDirective = "translate(" + dx + ", " + dy + ")";

        if (image instanceof ctx) {
          //canvas2svg mock canvas context. In the future we may want to clone nodes instead.
          //also I'm currently ignoring dw, dh, sw, sh, sx, sy for a mock context.
          svg = image.getSvg().cloneNode(true);

          if (svg.childNodes && svg.childNodes.length > 1) {
            defs = svg.childNodes[0];

            while (defs.childNodes.length) {
              id = defs.childNodes[0].getAttribute("id");
              this.__ids[id] = id;

              this.__defs.appendChild(defs.childNodes[0]);
            }

            group = svg.childNodes[1];

            if (group) {
              //save original transform
              var originTransform = group.getAttribute("transform");
              var transformDirective;

              if (originTransform) {
                transformDirective = originTransform + " " + translateDirective;
              } else {
                transformDirective = translateDirective;
              }

              group.setAttribute("transform", transformDirective);
              parent.appendChild(group);
            }
          }
        } else if (image.nodeName === "CANVAS" || image.nodeName === "IMG") {
          //canvas or image
          svgImage = this.__createElement("image");
          svgImage.setAttribute("width", dw);
          svgImage.setAttribute("height", dh);
          svgImage.setAttribute("preserveAspectRatio", "none");

          if (sx || sy || sw !== image.width || sh !== image.height) {
            //crop the image using a temporary canvas
            canvas = this.__document.createElement("canvas");
            canvas.width = dw;
            canvas.height = dh;
            context = canvas.getContext("2d");
            context.drawImage(image, sx, sy, sw, sh, 0, 0, dw, dh);
            image = canvas;
          }

          svgImage.setAttribute("transform", translateDirective);
          svgImage.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", image.nodeName === "CANVAS" ? image.toDataURL() : image.getAttribute("src"));
          parent.appendChild(svgImage);
        }
      }

      /**
       * Generates a pattern tag
       */
      createPattern(image, repetition) {
        let pattern = this.__document.__createElement("pattern");

        let id = randomString(this.__ids);
        let img;
        pattern.setAttribute("id", id);
        pattern.setAttribute("width", image.width);
        pattern.setAttribute("height", image.height);

        if (image.nodeName === "CANVAS" || image.nodeName === "IMG") {
          img = this.__createElement("image");
          img.setAttribute("width", image.width);
          img.setAttribute("height", image.height);
          img.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", image.nodeName === "CANVAS" ? image.toDataURL() : image.getAttribute("src"));
          pattern.appendChild(img);

          this.__defs.appendChild(pattern);
        } else if (image instanceof ctx) {
          pattern.appendChild(image.__root.childNodes[1]);

          this.__defs.appendChild(pattern);
        }

        return new CanvasPattern(pattern, this);
      }

      setLineDash(dashArray) {
        if (dashArray && dashArray.length > 0) {
          this.lineDash = dashArray.join(",");
        } else {
          this.lineDash = null;
        }
      }

      /**
       * Not yet implemented
       */
      drawFocusRing() {}

      createImageData() {}

      getImageData() {}

      putImageData() {}

      globalCompositeOperation() {}

      setTransform() {}

    }

    const Chromosome = function (name, order, bpStart, bpLength, rangeLocus) {
      this.name = name;
      this.order = order;
      this.bpStart = bpStart;
      this.bpLength = bpLength;
      this.rangeLocus = rangeLocus;
    };

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const splitLines$4 = splitLines$5;
    const reservedProperties$1 = new Set(['fastaURL', 'indexURL', 'cytobandURL', 'indexed']);

    class NonIndexedFasta {
      constructor(reference) {
        this.fastaURL = reference.fastaURL;
        this.withCredentials = reference.withCredentials;
        this.chromosomeNames = [];
        this.chromosomes = {};
        this.sequences = new Map();
        this.offsets = {}; // Build a track-like config object from the referenceObject

        const config = {};

        for (let key in reference) {
          if (reference.hasOwnProperty(key) && !reservedProperties$1.has(key)) {
            config[key] = reference[key];
          }
        }

        this.config = config;
      }

      async init() {
        return this.loadAll();
      }

      async getSequence(chr, start, end) {
        if (this.offsets[chr]) {
          start -= this.offsets[chr];
          end -= this.offsets[chr];
        }

        let prefix = "";

        if (start < 0) {
          for (let i = start; i < Math.min(end, 0); i++) {
            prefix += "*";
          }
        }

        if (end <= 0) {
          return Promise.resolve(prefix);
        }

        const seq = this.sequences.get(chr);
        const seqEnd = Math.min(end, seq.length);
        return prefix + seq.substring(start, seqEnd);
      }

      async loadAll() {
        let data;

        if (isDataURL(this.fastaURL)) {
          let bytes = decodeDataURI$1(this.fastaURL);
          data = "";

          for (let b of bytes) {
            data += String.fromCharCode(b);
          }
        } else {
          data = await igvxhr.load(this.fastaURL, buildOptions(this.config));
        }

        const lines = splitLines$4(data);
        const len = lines.length;
        let lineNo = 0;
        let currentSeq;
        let currentRangeLocus;
        let currentOffset = 0;
        let order = 0;
        let nextLine;
        let currentChr;

        while (lineNo < len) {
          nextLine = lines[lineNo++].trim();

          if (nextLine.startsWith("#") || nextLine.length === 0) ; else if (nextLine.startsWith(">")) {
            // Start the next sequence
            if (currentSeq) {
              this.chromosomeNames.push(currentChr);
              this.sequences.set(currentChr, currentSeq);
              this.chromosomes[currentChr] = new Chromosome(currentChr, order++, currentOffset, currentOffset + currentSeq.length, currentRangeLocus);
            }

            const parts = nextLine.substr(1).split(/\s+/); // Check for samtools style locus string.   This is not perfect, and could fail on weird sequence names

            const nameParts = parts[0].split(':');
            currentChr = nameParts[0];
            currentSeq = "";
            currentOffset = 0;
            currentRangeLocus = undefined;

            if (nameParts.length > 1 && nameParts[1].indexOf('-') > 0) {
              const locusParts = nameParts[1].split('-');

              if (locusParts.length === 2 && /^[0-9]+$/.test(locusParts[0]) && /^[0-9]+$/.test(locusParts[1])) ;

              const from = Number.parseInt(locusParts[0]);
              const to = Number.parseInt(locusParts[1]);

              if (to > from) {
                currentOffset = from - 1;
                this.offsets[currentChr] = currentOffset;
                currentRangeLocus = nameParts[1];
              }
            }
          } else {
            currentSeq += nextLine;
          }
        } // add last seq


        if (currentSeq) {
          this.chromosomeNames.push(currentChr);
          this.sequences.set(currentChr, currentSeq);
          this.chromosomes[currentChr] = new Chromosome(currentChr, order++, currentOffset, currentOffset + currentSeq.length, currentRangeLocus);
        }
      }

    }

    const GenomicInterval = function (chr, start, end, features) {
      this.chr = chr;
      this.start = start;
      this.end = end;
      this.features = features;
    };

    GenomicInterval.prototype.contains = function (chr, start, end) {
      return this.chr === chr && this.start <= start && this.end >= end;
    };

    GenomicInterval.prototype.containsRange = function (range) {
      return this.chr === range.chr && this.start <= range.start && this.end >= range.end;
    };

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const splitLines$3 = splitLines$5;
    const reservedProperties = new Set(['fastaURL', 'indexURL', 'compressedIndexURL', 'cytobandURL', 'indexed']);

    class FastaSequence {
      constructor(reference) {
        this.file = reference.fastaURL;
        this.indexFile = reference.indexURL || reference.indexFile || this.file + ".fai";
        this.compressedIndexFile = reference.compressedIndexURL || false;
        this.withCredentials = reference.withCredentials;
        this.chromosomeNames = [];
        this.chromosomes = {};
        this.sequences = {};
        this.offsets = {}; // Build a track-like config object from the referenceObject

        const config = {};

        for (let key in reference) {
          if (reference.hasOwnProperty(key) && !reservedProperties.has(key)) {
            config[key] = reference[key];
          }
        }

        this.config = config;
      }

      async init() {
        return this.getIndex();
      }

      async getSequence(chr, start, end) {
        const hasCachedSquence = this.interval && this.interval.contains(chr, start, end);

        if (!hasCachedSquence) {
          // Expand query, to minimum of 50kb
          let qstart = start;
          let qend = end;

          if (end - start < 50000) {
            const w = end - start;
            const center = Math.round(start + w / 2);
            qstart = Math.max(0, center - 25000);
            qend = center + 25000;
          }

          const seqBytes = await this.readSequence(chr, qstart, qend);
          this.interval = new GenomicInterval(chr, qstart, qend, seqBytes);
        }

        const offset = start - this.interval.start;
        const n = end - start;
        const seq = this.interval.features ? this.interval.features.substr(offset, n) : null;
        return seq;
      }

      async getIndex() {
        if (this.index) {
          return this.index;
        } else {
          const data = await igvxhr.load(this.indexFile, buildOptions(this.config));
          const lines = splitLines$3(data);
          const len = lines.length;
          let lineNo = 0;
          let order = 0;
          this.index = {};

          while (lineNo < len) {
            const tokens = lines[lineNo++].split("\t");
            const nTokens = tokens.length;

            if (nTokens === 5) {
              // Parse the index line.
              const chr = tokens[0];
              const size = parseInt(tokens[1]);
              const position = parseInt(tokens[2]);
              const basesPerLine = parseInt(tokens[3]);
              const bytesPerLine = parseInt(tokens[4]);
              const indexEntry = {
                size: size,
                position: position,
                basesPerLine: basesPerLine,
                bytesPerLine: bytesPerLine
              };
              this.chromosomeNames.push(chr);
              this.index[chr] = indexEntry;
              this.chromosomes[chr] = new Chromosome(chr, order++, 0, size);
            }
          }

          return this.index;
        }
      } //Code is losely based on https://github.com/GMOD/bgzf-filehandle
      //Reworked however in orde to work with the igvxhr interface for loading files
      //Additionally, replaced calls to the Long.js interface with standard JS calls for ArrayBuffers and the associated views
      //
      //The compressed index is an array of blocks, with each block being a pair: compressed-position & uncompressed-position (both in bytes)


      async getCompressedIndex() {
        const GZI_NUM_BYTES_OFFSET = 8;
        const GZI_NUM_BYTES_BLOCK = 8;

        if (this.compressedIndex) {
          return this.compressedIndex;
        }

        if (!this.compressedIndexFile) {
          this.compressedIndex = [];
          return this.compressedIndex;
        } //In contrast to the 'normal' reference (for which the index is chromosome based), this index is block-based
        //As such there is not need to make it a hash. An array is sufficient.


        this.compressedIndex = [];
        const gziData = await igvxhr.loadArrayBuffer(this.compressedIndexFile, buildOptions(this.config));
        const givenFileSize = gziData.byteLength;

        if (givenFileSize < GZI_NUM_BYTES_OFFSET) {
          console.log("Cannot parse GZI index file: length (" + givenFileSize + " bytes) is insufficient to determine content of index.");
          return this.compressedIndex;
        } //First 8 bytes are a little endian unsigned bigint (64bit), indicating the number of blocks in the index.


        const numBlocksBuffer = gziData.slice(0, GZI_NUM_BYTES_OFFSET);
        const numBlocks = Number(new DataView(numBlocksBuffer).getBigUint64(0, true)); //The remainder of the gzi content are pairs of little endian unsigned bigint (64bit) numbers.
        //The first of the pair is the compressed position of a block
        //The second of the pair is the uncompressed position of a block
        //Sanity check:
        //Is the size of the array-buffer (of the entire file) correct with regards to the number of blocks detailled by the first 8 bytes of the file?
        //Total file-size should be:
        // 8 + 2*(num_entries*8) bytes, with the first 8 bytes indicating the number of entries

        const expectedFileSize = GZI_NUM_BYTES_OFFSET + numBlocks * 2 * GZI_NUM_BYTES_BLOCK;

        if (givenFileSize != expectedFileSize) {
          console.log("Incorrect file size of reference genome index. Expected : " + expectedFileSize + ". Received : " + givenFileSize);
          return this.compressedIndex;
        } //Push the first block to the index: the first block always has positions 0 for both the compressed and uncompressed file


        this.compressedIndex.push([0, 0]); //Further process all the blocks of the GZI index, and keep them in memory

        for (let blockNumber = 0; blockNumber < numBlocks; blockNumber++) {
          const bufferBlockStart = GZI_NUM_BYTES_OFFSET + blockNumber * 2 * GZI_NUM_BYTES_BLOCK;
          const bufferBlockEnd = GZI_NUM_BYTES_OFFSET + blockNumber * 2 * GZI_NUM_BYTES_BLOCK + 2 * GZI_NUM_BYTES_BLOCK;
          const bufferBlock = gziData.slice(bufferBlockStart, bufferBlockEnd);
          const viewBlock = new DataView(bufferBlock);
          const compressedPosition = Number(viewBlock.getBigUint64(0, true)); //First 8 bytes

          const uncompressedPosition = Number(viewBlock.getBigUint64(GZI_NUM_BYTES_BLOCK, true)); //Last 8 bytes

          this.compressedIndex.push([compressedPosition, uncompressedPosition]);
        }

        return this.compressedIndex;
      } //The Fasta-index gives a byte-position of the chromosomal sequences within the FASTA file.
      //These locations need to be remapped to the locations within the zipped reference genome, using the GZI index
      //This function provides this functionality by 
      //1) taking the indicated start/stop byte locations within the UNCOMPRESSED FASTA file
      //2) remapping these byte locations to the correct blocks (and associated positions) within the COMPRESSED FASTA file
      //Subsequently, the calling method can then extract the correct blocks from the compressed FASTA files and uncompressed the data


      async getRelevantCompressedBlockNumbers(queryPositionStart, queryPositionEnd) {
        const UNCOMPRESSED_POSITION = 1; //Fallback for impossible values

        if (queryPositionStart < 0 || queryPositionEnd < 0 || queryPositionEnd < queryPositionStart) {
          console.log("Incompatible query positions for reference-genome. Start:" + queryPositionStart + " | End:" + queryPositionEnd);
          return [];
        } //Ensure compressed index is loaded


        await this.getCompressedIndex();
        let result = []; //Now search for the correct block-numbers (going from 0 to length(compressed-index)) which overlap with the provided byte-positions

        const lowestBlockNumber = 0;
        const highestBlockNumber = this.compressedIndex.length - 1; //Failsafe if for some reason the compressed index wasn't loaded or doesn't contain any data

        if (this.compressedIndex.length == 0) {
          console.log("Compressed index does not contain any content");
          return [];
        } //Failsafe: if the queryPositionStart is greater than the uncompressed-position of the final block,
        //then this final block is the only possible result


        if (queryPositionStart > this.compressedIndex[highestBlockNumber][UNCOMPRESSED_POSITION]) {
          return [highestBlockNumber];
        } //Rather than doing a linear search over all blocks, a binary search is done for speed considerations
        //We are searching for the highest block number for which its position is smaller than the query start position
        //Afterwards we will simply expand the blocks until the entire query range is covered


        let searchLow = lowestBlockNumber;
        let searchHigh = highestBlockNumber;
        let searchPosition = Math.floor(this.compressedIndex.length / 2);
        let maxIterations = this.compressedIndex.length + 1;
        let solutionFound = false; //instead of doing a while(true), this for-loop prevents eternal loops in case of issues

        for (let iteration = 0; iteration < maxIterations; iteration++) {
          const searchUncompressedPosition = this.compressedIndex[searchPosition][UNCOMPRESSED_POSITION];
          const nextSearchUncompressedPosition = searchPosition < this.compressedIndex.length - 1 ? this.compressedIndex[searchPosition + 1][UNCOMPRESSED_POSITION] : Infinity; //The query position lies within the current search block

          if (searchUncompressedPosition <= queryPositionStart && nextSearchUncompressedPosition > queryPositionStart) {
            solutionFound = true;
            break; //searchPosition is the correct block number index
          } //Current block lies before the query position
          else if (searchUncompressedPosition < queryPositionStart) {
            searchLow = searchPosition + 1;
          } //Current block lies after the query position
          else {
            searchHigh = searchPosition - 1;
          }

          searchPosition = Math.ceil((searchHigh - searchLow) / 2) + searchLow;
        } //If for some reason the binary search did not reveal a correct block index, then we return the empty result


        if (!solutionFound) {
          console.log("No blocks within compressed index found that correspond with query positions " + queryPositionStart + "," + queryPositionEnd);
          console.log(this.compressedIndex);
          return [];
        } //Now extend the result by adding additional blocks until the entire query range is covered


        result.push(searchPosition);

        for (let blockIndex = searchPosition + 1; blockIndex < this.compressedIndex.length; blockIndex++) {
          result.push(blockIndex);
          const blockUncompressedPosition = this.compressedIndex[blockIndex][UNCOMPRESSED_POSITION];

          if (blockUncompressedPosition >= queryPositionEnd) {
            break;
          }
        } //It is possible that the query end position lies AFTER the start of the final block
        //If this is the case, we add a 'fake' negative index which will be interpreted by the loadAndUncompressBlocks method as an indicator
        //to read until the end of the file 


        const finalRelevantBlock = result[result.length - 1];
        const finalIndexBlock = this.compressedIndex.length - 1;

        if (finalRelevantBlock === finalIndexBlock && this.compressedIndex[finalRelevantBlock][UNCOMPRESSED_POSITION] < queryPositionEnd) {
          result.push(-1);
        }

        return result;
      } //Load the content from the blockIndices.
      //This is done on a per-block basis
      //Content of the first block will be trimmed in order to match the expected offset


      async loadAndUncompressBlocks(blockIndices, startByte) {
        const COMPRESSED_POSITION = 0;
        const UNCOMPRESSED_POSITION = 1; //Normally the compressed index should already exist, we're just makeing sure here

        await this.getCompressedIndex();

        if (blockIndices.length == 0) {
          return "";
        } //Storing data in seperate array with indices in order to assert order due to async behaviour of loops


        let resultCache = Array(blockIndices.length - 1);

        for (let i = 0; i < blockIndices.length - 1; i++) {
          const currentBlockNumber = blockIndices[i];
          const currentBlockInfo = this.compressedIndex[currentBlockNumber];
          const currentBlockCompressedPosition = currentBlockInfo[COMPRESSED_POSITION];
          const nextBlockNumber = blockIndices[i + 1];
          let compressedBytes = [];

          if (nextBlockNumber != -1) {
            //default : read current entire block only
            const nextBlockInfo = this.compressedIndex[nextBlockNumber];
            const nextBlockCompressedPosition = nextBlockInfo[COMPRESSED_POSITION];
            const compressedLength = nextBlockCompressedPosition - currentBlockCompressedPosition;
            compressedBytes = await igvxhr.loadArrayBuffer(this.file, buildOptions(this.config, {
              range: {
                start: currentBlockCompressedPosition,
                size: compressedLength
              }
            }));
          } else {
            // special case for query within final block: read until the end of the file
            compressedBytes = await igvxhr.loadArrayBuffer(this.file, buildOptions(this.config, {
              range: {
                start: currentBlockCompressedPosition
              }
            }));
          } //now unzip the compressed bytes, and store them in the resultCache


          const uncompressedBytes = await unbgzf(compressedBytes);
          resultCache[i] = uncompressedBytes;
        } //Iterate over the result cache, create sequences from the data, and create a full sequence string from the data


        let result = "";

        for (let i = 0; i < resultCache.length; i++) {
          for (let j = 0; j < resultCache[i].length; j++) {
            const c = String.fromCharCode(resultCache[i][j]);
            result = result + c;
          }
        } //postprocess this data: because entire blocks are read we need to remove the first N bases of the first used block, 
        //which are not included in the original query positions


        const firstBlockInfo = this.compressedIndex[blockIndices[0]];
        const offset = startByte - firstBlockInfo[UNCOMPRESSED_POSITION];
        result = result.substring(offset);
        return result;
      }

      async readSequence(chr, qstart, qend) {
        await this.getIndex();
        await this.getCompressedIndex(); //This will work even if no compressed index file is set

        const idxEntry = this.index[chr];

        if (!idxEntry) {
          console.log("No index entry for chr: " + chr); // Tag interval with null so we don't try again

          this.interval = new GenomicInterval(chr, qstart, qend, null);
          return null;
        }

        const start = Math.max(0, qstart); // qstart should never be < 0

        const end = Math.min(idxEntry.size, qend);
        const bytesPerLine = idxEntry.bytesPerLine;
        const basesPerLine = idxEntry.basesPerLine;
        const position = idxEntry.position;
        const nEndBytes = bytesPerLine - basesPerLine;
        const startLine = Math.floor(start / basesPerLine);
        const endLine = Math.floor(end / basesPerLine);
        const base0 = startLine * basesPerLine; // Base at beginning of start line

        const offset = start - base0;
        const startByte = position + startLine * bytesPerLine + offset;
        const base1 = endLine * basesPerLine;
        const offset1 = end - base1;
        const endByte = position + endLine * bytesPerLine + offset1 - 1;
        const byteCount = endByte - startByte + 1;

        if (byteCount <= 0) {
          console.error("No sequence for " + chr + ":" + qstart + "-" + qend);
          return null;
        } //If the compressed index file is set, then we are dealing with a compressed genome sequence
        //The selection of startByte/endByte is done for the non-compressed genome sequence.
        //These need to be 'converted' to the correct byte positions in the compressed genome sequence,
        //by making use of the compressed index (GZI file)


        let allBytes;

        if (!this.compressedIndexFile) {
          allBytes = await igvxhr.load(this.file, buildOptions(this.config, {
            range: {
              start: startByte,
              size: byteCount
            }
          }));
        } else {
          let relevantBlockIndices = await this.getRelevantCompressedBlockNumbers(startByte, endByte);

          if (relevantBlockIndices.length === 0) {
            console.log("No blocks in the compressed index that correspond with the requested byte positions (" + startByte + "," + endByte + ")");
            return null;
          }

          allBytes = await this.loadAndUncompressBlocks(relevantBlockIndices, startByte);
        }

        if (!allBytes) {
          return null;
        }

        let nBases,
            seqBytes = "",
            srcPos = 0,
            allBytesLength = allBytes.length;

        if (offset > 0) {
          nBases = Math.min(end - start, basesPerLine - offset);
          seqBytes += allBytes.substr(srcPos, nBases);
          srcPos += nBases + nEndBytes;
        }

        while (srcPos < allBytesLength) {
          nBases = Math.min(basesPerLine, allBytesLength - srcPos);
          seqBytes += allBytes.substr(srcPos, nBases);
          srcPos += nBases + nEndBytes;
        }

        return seqBytes;
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const splitLines$2 = splitLines$5;

    class ChromSizes {
      constructor(url) {
        this.url = url;
        this.chromosomeNames = [];
        this.chromosomes = {};
      }

      async init() {
        return this.loadAll();
      }

      async getSequence(chr, start, end) {
        return undefined; // TODO -- return array of "N"s?
      }

      async loadAll() {
        let data;

        if (isDataURL(this.url)) {
          let bytes = decodeDataURI$1(this.fastaURL);
          data = "";

          for (let b of bytes) {
            data += String.fromCharCode(b);
          }
        } else {
          data = await igvxhr.load(this.url, {});
        }

        this.chromosomeNames = [];
        this.chromosomes = {};
        const lines = splitLines$2(data);
        let order = 0;

        for (let nextLine of lines) {
          const tokens = nextLine.split('\t');
          this.chromosomeNames.push(tokens[0]);
          const chrLength = Number.parseInt(tokens[1]);
          const chromosome = new Chromosome(tokens[0], order++, 0, chrLength);
          this.chromosomes[tokens[0]] = chromosome;
        }
      }

    }

    async function loadFasta(reference) {
      let fasta;

      if ("chromsizes" === reference.format) {
        fasta = new ChromSizes(reference.fastaURL);
      } else if (isDataURL(reference.fastaURL) || reference.indexed === false) {
        fasta = new NonIndexedFasta(reference);
      } else {
        fasta = new FastaSequence(reference);
      }

      await fasta.init();
      return fasta;
    }

    const Cytoband = function (start, end, name, typestain) {
      this.start = start;
      this.end = end;
      this.name = name;
      this.stain = 0; // Set the type, either p, n, or c

      if (typestain === 'acen') {
        this.type = 'c';
      } else {
        this.type = typestain.charAt(1);

        if (this.type === 'p') {
          this.stain = parseInt(typestain.substring(4));
        }
      }
    };

    const _version = "2.12.6";

    function version() {
      return _version;
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const DEFAULT_GENOMES_URL = "https://igv.org/genomes/genomes.json";
    const BACKUP_GENOMES_URL = "https://s3.amazonaws.com/igv.org.genomes/genomes.json";
    const splitLines$1 = splitLines$5;
    const GenomeUtils = {
      loadGenome: async function (options) {
        const cytobandUrl = options.cytobandURL;
        const aliasURL = options.aliasURL;
        const sequence = await loadFasta(options);
        let cytobands;

        if (cytobandUrl) {
          cytobands = await loadCytobands(cytobandUrl, sequence.config);
        }

        let aliases;

        if (aliasURL) {
          aliases = await loadAliases(aliasURL, sequence.config);
        }

        return new Genome(options, sequence, cytobands, aliases);
      },
      initializeGenomes: async function (config) {
        if (!GenomeUtils.KNOWN_GENOMES) {
          const table = {}; // Get default genomes

          if (config.loadDefaultGenomes !== false) {
            try {
              const url = DEFAULT_GENOMES_URL + `?randomSeed=${Math.random().toString(36)}&version=${version()}`; // prevent caching

              const jsonArray = await igvxhr.loadJson(url, {
                timeout: 5000
              });
              processJson(jsonArray);
            } catch (e) {
              console.error(e);

              try {
                const url = BACKUP_GENOMES_URL + `?randomSeed=${Math.random().toString(36)}&version=${version()}`; // prevent caching

                const jsonArray = await igvxhr.loadJson(url, {});
                processJson(jsonArray);
              } catch (e) {
                console.error(e);
                console.warn("Errors loading default genome definitions.");
              }
            }
          } // Add user-defined genomes


          const genomeList = config.genomeList || config.genomes;

          if (genomeList) {
            if (typeof genomeList === 'string') {
              const jsonArray = await igvxhr.loadJson(genomeList, {});
              processJson(jsonArray);
            } else {
              processJson(genomeList);
            }
          }

          GenomeUtils.KNOWN_GENOMES = table;

          function processJson(jsonArray) {
            jsonArray.forEach(function (json) {
              table[json.id] = json;
            });
            return table;
          }
        }
      },
      isWholeGenomeView: function (chr) {
        return 'all' === chr.toLowerCase();
      },
      // Expand a genome id to a reference object, if needed
      expandReference: function (idOrConfig) {
        // idOrConfig might be json
        if (isString$3(idOrConfig) && idOrConfig.startsWith("{")) {
          try {
            idOrConfig = JSON.parse(idOrConfig);
          } catch (e) {// Apparently its not json,  could be an ID starting with "{".  Unusual but legal.
          }
        }

        let genomeID;

        if (isString$3(idOrConfig)) {
          genomeID = idOrConfig;
        } else if (idOrConfig.genome) {
          genomeID = idOrConfig.genome;
        } else if (idOrConfig.id !== undefined && idOrConfig.fastaURL === undefined) {
          // Backward compatibility
          genomeID = idOrConfig.id;
        }

        if (genomeID) {
          const knownGenomes = GenomeUtils.KNOWN_GENOMES;
          const reference = knownGenomes[genomeID];

          if (!reference) {
            Alert.presentAlert(new Error(`Unknown genome id: ${genomeID}`), undefined);
          }

          return reference;
        } else {
          return idOrConfig;
        }
      }
    };

    class Genome {
      constructor(config, sequence, ideograms, aliases) {
        this.config = config;
        this.id = config.id || generateGenomeID(config);
        this.sequence = sequence;
        this.chromosomeNames = sequence.chromosomeNames;
        this.chromosomes = sequence.chromosomes; // An object (functions as a dictionary)

        this.ideograms = ideograms;
        this.featureDB = {}; // Hash of name -> feature, used for search function.

        this.wholeGenomeView = config.wholeGenomeView === undefined || config.wholeGenomeView;

        if (this.wholeGenomeView && Object.keys(sequence.chromosomes).length > 1) {
          constructWG(this, config);
        } else {
          this.wgChromosomeNames = sequence.chromosomeNames;
        }
        /**
         * Return the official chromosome name for the (possibly) alias.  Deals with
         * 1 <-> chr1,  chrM <-> MT,  IV <-> chr4, etc.
         * @param str
         */


        var chrAliasTable = {},
            self = this; // The standard mappings

        chrAliasTable["all"] = "all";
        this.chromosomeNames.forEach(function (name) {
          var alias = name.startsWith("chr") ? name.substring(3) : "chr" + name;
          chrAliasTable[alias.toLowerCase()] = name;
          if (name === "chrM") chrAliasTable["mt"] = "chrM";
          if (name === "MT") chrAliasTable["chrm"] = "MT";
          chrAliasTable[name.toLowerCase()] = name;
        }); // Custom mappings

        if (aliases) {
          aliases.forEach(function (array) {
            // Find the official chr name
            var defName, i;

            for (i = 0; i < array.length; i++) {
              if (self.chromosomes[array[i]]) {
                defName = array[i];
                break;
              }
            }

            if (defName) {
              array.forEach(function (alias) {
                if (alias !== defName) {
                  chrAliasTable[alias.toLowerCase()] = defName;
                  chrAliasTable[alias] = defName; // Should not be needed
                }
              });
            }
          });
        }

        this.chrAliasTable = chrAliasTable;
      }

      showWholeGenomeView() {
        return this.config.wholeGenomeView !== false;
      }

      toJSON() {
        return Object.assign({}, this.config, {
          tracks: undefined
        });
      }

      getInitialLocus() {}

      getHomeChromosomeName() {
        if (this.showWholeGenomeView() && this.chromosomes.hasOwnProperty("all")) {
          return "all";
        } else {
          return this.chromosomeNames[0];
        }
      }

      getChromosomeName(str) {
        const chr = str ? this.chrAliasTable[str.toLowerCase()] : str;
        return chr ? chr : str;
      }

      getChromosome(chr) {
        chr = this.getChromosomeName(chr);
        return this.chromosomes[chr];
      }

      getCytobands(chr) {
        return this.ideograms ? this.ideograms[chr] : null;
      }

      getLongestChromosome() {
        var longestChr,
            chromosomes = this.chromosomes;

        for (let key in chromosomes) {
          if (chromosomes.hasOwnProperty(key)) {
            var chr = chromosomes[key];

            if (longestChr === undefined || chr.bpLength > longestChr.bpLength) {
              longestChr = chr;
            }
          }

          return longestChr;
        }
      }

      getChromosomes() {
        return this.chromosomes;
      }
      /**
       * Return the genome coordinate in kb for the give chromosome and position.
       * NOTE: This might return undefined if the chr is filtered from whole genome view.
       */


      getGenomeCoordinate(chr, bp) {
        var offset = this.getCumulativeOffset(chr);
        if (offset === undefined) return undefined;
        return offset + bp;
      }
      /**
       * Return the chromosome and coordinate in bp for the given genome coordinate
       */


      getChromosomeCoordinate(genomeCoordinate) {
        if (this.cumulativeOffsets === undefined) {
          this.cumulativeOffsets = computeCumulativeOffsets.call(this);
        }

        let lastChr = undefined;
        let lastCoord = 0;

        for (let name of this.wgChromosomeNames) {
          const cumulativeOffset = this.cumulativeOffsets[name];

          if (cumulativeOffset > genomeCoordinate) {
            const position = genomeCoordinate - lastCoord;
            return {
              chr: lastChr,
              position: position
            };
          }

          lastChr = name;
          lastCoord = cumulativeOffset;
        } // If we get here off the end


        return {
          chr: this.wgChromosomeNames[this.wgChromosomeNames.length - 1],
          position: 0
        };
      }

      /**
       * Return the offset in genome coordinates (kb) of the start of the given chromosome
       * NOTE:  This might return undefined if the chromosome is filtered from whole genome view.
       */
      getCumulativeOffset(chr) {
        if (this.cumulativeOffsets === undefined) {
          this.cumulativeOffsets = computeCumulativeOffsets.call(this);
        }

        const queryChr = this.getChromosomeName(chr);
        return this.cumulativeOffsets[queryChr];

        function computeCumulativeOffsets() {
          let self = this;
          let acc = {};
          let offset = 0;

          for (let name of self.wgChromosomeNames) {
            acc[name] = Math.floor(offset);
            const chromosome = self.getChromosome(name);
            offset += chromosome.bpLength;
          }

          return acc;
        }
      }
      /**
       * Return the nominal genome length, this is the length of the main chromosomes (no scaffolds, etc).
       */


      getGenomeLength() {
        let self = this;

        if (!this.bpLength) {
          let bpLength = 0;
          self.wgChromosomeNames.forEach(function (cname) {
            let c = self.chromosomes[cname];
            bpLength += c.bpLength;
          });
          this.bpLength = bpLength;
        }

        return this.bpLength;
      }

      async getSequence(chr, start, end) {
        chr = this.getChromosomeName(chr);
        return this.sequence.getSequence(chr, start, end);
      }

    }

    async function loadCytobands(cytobandUrl, config) {
      let data;

      if (isDataURL(cytobandUrl)) {
        const plain = decodeDataURI$1(cytobandUrl);
        data = "";
        const len = plain.length;

        for (let i = 0; i < len; i++) {
          data += String.fromCharCode(plain[i]);
        }
      } else {
        data = await igvxhr.loadString(cytobandUrl, buildOptions(config));
      } // var bands = [],
      //     lastChr,
      //     n = 0,
      //     c = 1,
      //
      //     len = lines.length,


      const cytobands = {};
      let lastChr;
      let bands = [];
      const lines = splitLines$1(data);

      for (let line of lines) {
        var tokens = line.split("\t");
        var chr = tokens[0];
        if (!lastChr) lastChr = chr;

        if (chr !== lastChr) {
          cytobands[lastChr] = bands;
          bands = [];
          lastChr = chr;
        }

        if (tokens.length === 5) {
          //10	0	3000000	p15.3	gneg
          var start = parseInt(tokens[1]);
          var end = parseInt(tokens[2]);
          var name = tokens[3];
          var stain = tokens[4];
          bands.push(new Cytoband(start, end, name, stain));
        }
      }

      return cytobands;
    }

    function loadAliases(aliasURL, config) {
      return igvxhr.loadString(aliasURL, buildOptions(config)).then(function (data) {
        var lines = splitLines$1(data),
            aliases = [];
        lines.forEach(function (line) {
          if (!line.startsWith("#") && line.length > 0) aliases.push(line.split("\t"));
        });
        return aliases;
      });
    }

    function constructWG(genome, config) {
      let wgChromosomes;

      if (config.chromosomeOrder) {
        if (Array.isArray(config.chromosomeOrder)) {
          genome.wgChromosomeNames = config.chromosomeOrder;
        } else {
          genome.wgChromosomeNames = config.chromosomeOrder.split(',').map(nm => nm.trim());
        }

        wgChromosomes = genome.wgChromosomeNames.map(nm => genome.chromosomes[nm]).filter(chr => chr !== undefined);
      } else {
        // Trim small chromosomes.
        const lengths = Object.keys(genome.chromosomes).map(key => genome.chromosomes[key].bpLength);
        const median = lengths.reduce((a, b) => Math.max(a, b));
        const threshold = median / 50;
        wgChromosomes = Object.values(genome.chromosomes).filter(chr => chr.bpLength > threshold); // Sort chromosomes.  First segregate numeric and alpha names, sort numeric, leave alpha as is

        const numericChromosomes = wgChromosomes.filter(chr => isDigit(chr.name.replace('chr', '')));
        const alphaChromosomes = wgChromosomes.filter(chr => !isDigit(chr.name.replace('chr', '')));
        numericChromosomes.sort((a, b) => Number.parseInt(a.name.replace('chr', '')) - Number.parseInt(b.name.replace('chr', '')));
        const wgChromosomeNames = numericChromosomes.map(chr => chr.name);

        for (let chr of alphaChromosomes) {
          wgChromosomeNames.push(chr.name);
        }

        genome.wgChromosomeNames = wgChromosomeNames;
      } // Compute psuedo-chromosome "all"


      const l = wgChromosomes.reduce((accumulator, currentValue) => accumulator += currentValue.bpLength, 0);
      genome.chromosomes["all"] = {
        name: "all",
        bpLength: l
      };

      function isDigit(val) {
        return /^\d+$/.test(val);
      }
    }

    function generateGenomeID(config) {
      if (config.id !== undefined) {
        return config.id;
      } else if (config.fastaURL && isString$3(config.fastaURL)) {
        return config.fastaURL;
      } else if (config.fastaURL && config.fastaURL.name) {
        return config.fastaURL.name;
      } else {
        return ("0000" + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4);
      }
    }

    /**
     * Created by dat on 9/16/16.
     */
    const NOT_LOADED_MESSAGE = 'Error loading track data';
    let lastClickTime = 0;
    let popupTimerID;

    class TrackViewport extends Viewport {
      constructor(trackView, viewportColumn, referenceFrame, width) {
        super(trackView, viewportColumn, referenceFrame, width);
      }

      initializationHelper() {
        this.$spinner = $$1('<div>', {
          class: 'igv-loading-spinner-container'
        });
        this.$viewport.append(this.$spinner);
        this.$spinner.append($$1('<div>'));
        const track = this.trackView.track;

        if ('sequence' !== track.type) {
          this.$zoomInNotice = this.createZoomInNotice(this.$content);
        }

        if (track.name && "sequence" !== track.id) {
          this.$trackLabel = $$1('<div class="igv-track-label">');
          this.$viewport.append(this.$trackLabel);
          this.setTrackLabel(track.name);

          if (false === this.browser.trackLabelsVisible) {
            this.$trackLabel.hide();
          }
        }

        this.stopSpinner();
        this.addMouseHandlers();
      }

      setContentHeight(contentHeight) {
        super.setContentHeight(contentHeight);
        if (this.featureCache) this.featureCache.redraw = true;
      }

      setTrackLabel(label) {
        this.$trackLabel.empty();
        this.$trackLabel.html(label);
        const txt = this.$trackLabel.text();
        this.$trackLabel.attr('title', txt);
      }

      startSpinner() {
        this.$spinner.show();
      }

      stopSpinner() {
        if (this.$spinner) {
          this.$spinner.hide();
        }
      }
      /**
       * Test to determine if we are zoomed in far enough to see features. Applicable to tracks with visibility windows.
       *
       * As a side effect the viewports canvas is removed if zoomed out.
       *
       * @returns {boolean} true if we are zoomed in past visibility window, false otherwise
       */


      checkZoomIn() {
        const zoomedOutOfWindow = () => {
          if (this.referenceFrame.chr.toLowerCase() === "all" && !this.trackView.track.supportsWholeGenome) {
            return true;
          } else {
            const visibilityWindow = this.trackView.track.visibilityWindow;
            return visibilityWindow !== undefined && visibilityWindow > 0 && this.referenceFrame.bpPerPixel * this.$viewport.width() > visibilityWindow;
          }
        };

        if (this.trackView.track && "sequence" === this.trackView.track.type && this.referenceFrame.bpPerPixel > 1) {
          $$1(this.canvas).remove();
          this.canvas = undefined; //this.featureCache = undefined

          return false;
        }

        if (!this.viewIsReady()) {
          return false;
        }

        if (zoomedOutOfWindow()) {
          // Out of visibility window
          if (this.canvas) {
            $$1(this.canvas).remove();
            this.canvas = undefined; //this.featureCache = undefined
          }

          if (this.trackView.track.autoHeight) {
            const minHeight = this.trackView.minHeight || 0;
            this.setContentHeight(minHeight);
          }

          if (this.$zoomInNotice) {
            this.$zoomInNotice.show();
          }

          return false;
        } else {
          if (this.$zoomInNotice) {
            this.$zoomInNotice.hide();
          }

          return true;
        }
      }
      /**
       * Adjust the canvas to the current genomic state.
       */


      shift() {
        const referenceFrame = this.referenceFrame;

        if (this.canvas && this.canvas._data && this.canvas._data.chr === this.referenceFrame.chr && this.canvas._data.bpPerPixel === referenceFrame.bpPerPixel) {
          const pixelOffset = Math.round((this.canvas._data.startBP - referenceFrame.start) / referenceFrame.bpPerPixel);
          this.canvas.style.left = pixelOffset + "px";
        }
      }

      async loadFeatures() {
        const referenceFrame = this.referenceFrame;
        const chr = referenceFrame.chr; // Expand the requested range so we can pan a bit without reloading.  But not beyond chromosome bounds

        const chrLength = this.browser.genome.getChromosome(chr).bpLength;
        const pixelWidth = this.$content.width(); // * 3;

        const bpWidth = pixelWidth * referenceFrame.bpPerPixel;
        const bpStart = Math.floor(Math.max(0, referenceFrame.start - bpWidth));
        const bpEnd = Math.ceil(Math.min(chrLength, referenceFrame.start + bpWidth + bpWidth)); // Add one screen width to end

        if (this.loading && this.loading.start === bpStart && this.loading.end === bpEnd) {
          return undefined;
        }

        this.loading = {
          start: bpStart,
          end: bpEnd
        };
        this.startSpinner();

        try {
          const track = this.trackView.track;
          const features = await this.getFeatures(track, chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
          let roiFeatures = [];
          const roi = mergeArrays(this.browser.roi, track.roi);

          if (roi) {
            for (let r of roi) {
              const f = await r.getFeatures(chr, bpStart, bpEnd, referenceFrame.bpPerPixel);
              roiFeatures.push({
                track: r,
                features: f
              });
            }
          }

          const mr = track && ("wig" === track.type || "merged" === track.type); // wig tracks are potentially multiresolution (e.g. bigwig)

          this.featureCache = new FeatureCache(chr, bpStart, bpEnd, referenceFrame.bpPerPixel, features, roiFeatures, mr);
          this.loading = false;
          this.hideMessage();
          this.stopSpinner();
          return this.featureCache;
        } catch (error) {
          // Track might have been removed during load
          if (this.trackView && this.trackView.disposed !== true) {
            this.showMessage(NOT_LOADED_MESSAGE);
            Alert.presentAlert(error);
            console.error(error);
          }
        } finally {
          this.loading = false;
          this.stopSpinner();
        }
      }

      repaintDimensions() {
        const isWGV = GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);
        const pixelWidth = isWGV ? this.$viewport.width() : 3 * this.$viewport.width();
        const bpPerPixel = this.referenceFrame.bpPerPixel;
        const startBP = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
        const endBP = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel);
        return {
          startBP,
          endBP,
          pixelWidth
        };
      }
      /**
       * Repaint the canvas using the cached features
       *
       */


      repaint() {
        if (undefined === this.featureCache) {
          return;
        }

        let {
          features,
          roiFeatures
        } = this.featureCache; //this.tile.bpPerPixel = this.referenceFrame.bpPerPixel
        // const isWGV = GenomeUtils.isWholeGenomeView(this.browser.referenceFrameList[0].chr)

        GenomeUtils.isWholeGenomeView(this.referenceFrame.chr); // Canvas dimensions. There is no left-right panning for WGV so canvas width is viewport width.
        // For deep tracks we paint a canvas == 3*viewportHeight centered on the current vertical scroll position

        const {
          startBP,
          endBP,
          pixelWidth
        } = this.repaintDimensions();
        const viewportHeight = this.$viewport.height();
        const contentHeight = this.getContentHeight();
        const minHeight = roiFeatures ? Math.max(contentHeight, viewportHeight) : contentHeight; // Need to fill viewport for ROIs.

        const pixelHeight = Math.min(minHeight, 3 * viewportHeight);

        if (0 === pixelWidth || 0 === pixelHeight) {
          if (this.canvas) {
            $$1(this.canvas).remove();
          }

          return;
        }

        const canvasTop = Math.max(0, -this.$content.position().top - viewportHeight);
        const bpPerPixel = this.referenceFrame.bpPerPixel; //const startBP = this.referenceFrame.start - (isWGV ? 0 : pixelWidth / 3 * bpPerPixel)
        //const endBP = this.referenceFrame.end + (isWGV ? 0 : pixelWidth / 3 * bpPerPixel)

        const pixelXOffset = Math.round((startBP - this.referenceFrame.start) / bpPerPixel);
        const newCanvas = $$1('<canvas class="igv-canvas">').get(0);
        newCanvas.style.width = pixelWidth + "px";
        newCanvas.style.height = pixelHeight + "px";
        newCanvas.style.left = pixelXOffset + "px";
        newCanvas.style.top = canvasTop + "px"; // Always use high DPI if in "FILL" display mode, otherwise use track setting;

        const devicePixelRatio = "FILL" === this.trackView.track.displayMode || this.trackView.track.supportHiDPI !== false ? window.devicePixelRatio : 1;
        newCanvas.width = devicePixelRatio * pixelWidth;
        newCanvas.height = devicePixelRatio * pixelHeight;
        const ctx = newCanvas.getContext("2d");
        ctx.scale(devicePixelRatio, devicePixelRatio);
        ctx.translate(0, -canvasTop);
        const drawConfiguration = {
          context: ctx,
          pixelXOffset,
          pixelWidth,
          pixelHeight,
          pixelTop: canvasTop,
          bpStart: startBP,
          bpEnd: endBP,
          bpPerPixel,
          referenceFrame: this.referenceFrame,
          selection: this.selection,
          viewport: this,
          viewportWidth: this.$viewport.width()
        };
        this.draw(drawConfiguration, features, roiFeatures);
        this.featureCache.canvasTop = canvasTop;
        this.featureCache.height = pixelHeight;

        if (this.canvas) {
          $$1(this.canvas).remove();
        }

        newCanvas._data = {
          chr: this.featureCache.chr,
          bpPerPixel,
          startBP,
          endBP,
          pixelHeight,
          pixelTop: canvasTop
        };
        this.canvas = newCanvas;
        this.$content.append($$1(newCanvas));
      }
      /**
       * Draw the associated track.
       *
       * @param drawConfiguration
       * @param features
       * @param roiFeatures
       */


      draw(drawConfiguration, features, roiFeatures) {
        // console.log(`${ Date.now() } viewport draw(). track ${ this.trackView.track.type }. content-css-top ${ this.$content.css('top') }. canvas-top ${ drawConfiguration.pixelTop }.`)
        if (features) {
          drawConfiguration.features = features;
          this.trackView.track.draw(drawConfiguration);
        }

        if (roiFeatures) {
          for (let r of roiFeatures) {
            drawConfiguration.features = r.features;
            r.track.draw(drawConfiguration);
          }
        }
      }

      containsPosition(chr, position) {
        if (this.referenceFrame.chr === chr && position >= this.referenceFrame.start) {
          return position <= this.referenceFrame.calculateEnd(this.getWidth());
        } else {
          return false;
        }
      }

      isLoading() {
        return this.loading;
      }

      savePNG() {
        if (!this.canvas) return;
        const canvasMetadata = this.featureCache;
        const canvasTop = canvasMetadata ? canvasMetadata.canvasTop : 0;
        const devicePixelRatio = window.devicePixelRatio;
        const w = this.$viewport.width() * devicePixelRatio;
        const h = this.$viewport.height() * devicePixelRatio;
        const x = -$$1(this.canvas).position().left * devicePixelRatio;
        const y = (-this.$content.position().top - canvasTop) * devicePixelRatio;
        const ctx = this.canvas.getContext("2d");
        const imageData = ctx.getImageData(x, y, w, h);
        const exportCanvas = document.createElement('canvas');
        const exportCtx = exportCanvas.getContext('2d');
        exportCanvas.width = imageData.width;
        exportCanvas.height = imageData.height;
        exportCtx.putImageData(imageData, 0, 0); // filename = this.trackView.track.name + ".png";

        const filename = (this.$trackLabel.text() ? this.$trackLabel.text() : "image") + ".png";
        const data = exportCanvas.toDataURL("image/png");
        download(filename, data);
      }

      saveSVG() {
        const {
          width,
          height
        } = this.$viewport.get(0).getBoundingClientRect();
        const config = {
          width,
          height,
          viewbox: {
            x: 0,
            y: -this.$content.position().top,
            width,
            height
          }
        };
        const context = new ctx(config);
        const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, '');
        const index = this.browser.referenceFrameList.indexOf(this.referenceFrame);
        const id = `${str}_referenceFrame_${index}_guid_${guid$2()}`;
        this.drawSVGWithContext(context, width, height, id, 0, 0, 0);
        const svg = context.getSerializedSvg(true);
        const data = URL.createObjectURL(new Blob([svg], {
          type: "application/octet-stream"
        }));
        download(`${id}.svg`, data);
      } // called by trackView.renderSVGContext() when rendering
      // entire browser as SVG


      renderSVGContext(context, _ref) {
        let {
          deltaX,
          deltaY
        } = _ref;

        // Nothing to do if zoomInNotice is active
        if (this.$zoomInNotice && this.$zoomInNotice.is(":visible")) {
          return;
        }

        const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, '');
        const index = this.browser.referenceFrameList.indexOf(this.referenceFrame);
        const id = `${str}_referenceFrame_${index}_guid_${guid$2()}`;
        const {
          top: yScrollDelta
        } = this.$content.position();
        const {
          width,
          height
        } = this.$viewport.get(0).getBoundingClientRect();
        this.drawSVGWithContext(context, width, height, id, deltaX, deltaY + yScrollDelta, -yScrollDelta);

        if (this.$trackLabel && true === this.browser.trackLabelsVisible) {
          const {
            x,
            y,
            width,
            height
          } = relativeDOMBBox(this.$viewport.get(0), this.$trackLabel.get(0));
          this.renderTrackLabelSVG(context, deltaX + x, deltaY + y, width, height);
        }
      } // render track label element called from renderSVGContext()


      renderTrackLabelSVG(context, tx, ty, width, height) {
        const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, '');
        const id = `${str}_track_label_guid_${guid$2()}`;
        context.saveWithTranslationAndClipRect(id, tx, ty, width, height, 0);
        context.fillStyle = "white";
        context.fillRect(0, 0, width, height);
        context.font = "12px Arial";
        context.fillStyle = 'rgb(68, 68, 68)';
        const {
          width: stringWidth
        } = context.measureText(this.$trackLabel.text());
        const dx = 0.25 * (width - stringWidth);
        const dy = 0.7 * (height - 12);
        context.fillText(this.$trackLabel.text(), dx, height - dy);
        context.strokeStyle = 'rgb(68, 68, 68)';
        context.strokeRect(0, 0, width, height);
        context.restore();
      } // called by renderSVGContext()


      drawSVGWithContext(context, width, height, id, x, y, yClipOffset) {
        context.saveWithTranslationAndClipRect(id, x, y, width, height, yClipOffset);
        let {
          start,
          bpPerPixel
        } = this.referenceFrame;
        const config = {
          context,
          viewport: this,
          referenceFrame: this.referenceFrame,
          top: yClipOffset,
          pixelTop: yClipOffset,
          pixelWidth: width,
          pixelHeight: height,
          bpStart: start,
          bpEnd: start + width * bpPerPixel,
          bpPerPixel,
          viewportWidth: width,
          selection: this.selection
        };
        const features = this.featureCache ? this.featureCache.features : [];
        const roiFeatures = this.featureCache ? this.featureCache.roiFeatures : undefined;
        this.draw(config, features, roiFeatures);
        context.restore();
      }

      get cachedFeatures() {
        return this.featureCache ? this.featureCache.features : [];
      }

      async getFeatures(track, chr, start, end, bpPerPixel) {
        if (this.featureCache && this.featureCache.containsRange(chr, start, end, bpPerPixel)) {
          return this.featureCache.features;
        } else if (typeof track.getFeatures === "function") {
          const features = await track.getFeatures(chr, start, end, bpPerPixel, this);
          this.checkContentHeight(features);
          return features;
        } else {
          return undefined;
        }
      }

      needsRepaint() {
        if (!this.canvas) return true;
        const data = this.canvas._data;
        return !data || this.referenceFrame.start < data.startBP || this.referenceFrame.end > data.endBP || this.referenceFrame.chr !== data.chr || this.referenceFrame.bpPerPixel != data.bpPerPixel;
      }

      needsReload() {
        if (!this.featureCache) return true;
        const referenceFrame = this.referenceFrame;
        const chr = this.referenceFrame.chr;
        const bpPerPixel = referenceFrame.bpPerPixel;
        const {
          startBP,
          endBP
        } = this.repaintDimensions();
        return !this.featureCache.containsRange(chr, startBP, endBP, bpPerPixel);
      }

      createZoomInNotice($parent) {
        const $container = $$1('<div>', {
          class: 'igv-zoom-in-notice-container'
        });
        $parent.append($container);
        const $e = $$1('<div>');
        $container.append($e);
        $e.text('Zoom in to see features');
        $container.hide();
        return $container;
      }

      viewIsReady() {
        return this.browser && this.browser.referenceFrameList && this.referenceFrame;
      }

      addMouseHandlers() {
        const viewport = this.$viewport.get(0);
        this.addViewportContextMenuHandler(viewport);

        const md = event => {
          this.enableClick = true;
          this.browser.mouseDownOnViewport(event, this);
          pageCoordinates$1(event);
        };

        viewport.addEventListener('mousedown', md);
        viewport.addEventListener('touchstart', md);

        const mu = event => {
          // Any mouse up cancels drag and scrolling
          if (this.browser.dragObject || this.browser.isScrolling) {
            this.browser.cancelTrackPan(); // event.preventDefault();
            // event.stopPropagation();

            this.enableClick = false; // Until next mouse down
          } else {
            this.browser.cancelTrackPan();
            this.browser.endTrackDrag();
          }
        };

        viewport.addEventListener('mouseup', mu);
        viewport.addEventListener('touchend', mu);
        this.addViewportClickHandler(this.$viewport.get(0));

        if (this.trackView.track.name && "sequence" !== this.trackView.track.config.type) {
          this.addTrackLabelClickHandler(this.$trackLabel.get(0));
        }
      }

      addViewportContextMenuHandler(viewport) {
        viewport.addEventListener('contextmenu', event => {
          // Ignore if we are doing a drag.  This can happen with touch events.
          if (this.browser.dragObject) {
            return false;
          }

          const clickState = createClickState(event, this);

          if (undefined === clickState) {
            return false;
          }

          event.preventDefault(); // Track specific items

          let menuItems = [];

          if (typeof this.trackView.track.contextMenuItemList === "function") {
            const trackMenuItems = this.trackView.track.contextMenuItemList(clickState);

            if (trackMenuItems) {
              menuItems = trackMenuItems;
            }
          } // Add items common to all tracks


          if (menuItems.length > 0) {
            menuItems.push({
              label: $$1('<HR>')
            });
          }

          menuItems.push({
            label: 'Save Image (PNG)',
            click: () => this.savePNG()
          });
          menuItems.push({
            label: 'Save Image (SVG)',
            click: () => this.saveSVG()
          });
          this.browser.menuPopup.presentTrackContextMenu(event, menuItems);
        });
      }

      addViewportClickHandler(viewport) {
        viewport.addEventListener('click', event => {
          if (this.enableClick && this.canvas) {
            if (3 === event.which || event.ctrlKey) {
              return;
            } // Close any currently open popups


            $$1('.igv-popover').hide();

            if (this.browser.dragObject || this.browser.isScrolling) {
              return;
            } // Treat as a mouse click, its either a single or double click.
            // Handle here and stop propogation / default


            event.preventDefault();
            const mouseX = translateMouseCoordinates$1(event, this.$viewport.get(0)).x;
            const mouseXCanvas = translateMouseCoordinates$1(event, this.canvas).x;
            const referenceFrame = this.referenceFrame;
            const xBP = Math.floor(referenceFrame.start + referenceFrame.toBP(mouseXCanvas));
            const time = Date.now();

            if (time - lastClickTime < this.browser.constants.doubleClickDelay) {
              // double-click
              if (popupTimerID) {
                window.clearTimeout(popupTimerID);
                popupTimerID = undefined;
              }

              const centerBP = Math.round(referenceFrame.start + referenceFrame.toBP(mouseX));
              let string;

              if ('all' === this.referenceFrame.chr.toLowerCase()) {
                const chr = this.browser.genome.getChromosomeCoordinate(centerBP).chr;

                if (1 === this.browser.referenceFrameList.length) {
                  string = chr;
                } else {
                  const loci = this.browser.referenceFrameList.map(_ref2 => {
                    let {
                      locusSearchString
                    } = _ref2;
                    return locusSearchString;
                  });
                  const index = this.browser.referenceFrameList.indexOf(this.referenceFrame);
                  loci[index] = chr;
                  string = loci.join(' ');
                }

                this.browser.search(string);
              } else {
                this.browser.zoomWithScaleFactor(0.5, centerBP, this.referenceFrame);
              }
            } else {
              // single-click
              if (event.shiftKey && typeof this.trackView.track.shiftClick === "function") {
                this.trackView.track.shiftClick(xBP, event);
              } else if (typeof this.trackView.track.popupData === "function") {
                popupTimerID = setTimeout(() => {
                  const content = getPopupContent(event, this);

                  if (content) {
                    if (this.popover) this.popover.dispose();
                    this.popover = new Popover(this.browser.columnContainer);
                    this.popover.presentContentWithEvent(event, content);
                  }

                  window.clearTimeout(popupTimerID);
                  popupTimerID = undefined;
                }, this.browser.constants.doubleClickDelay);
              }
            }

            lastClickTime = time;
          }
        });
      }

      addTrackLabelClickHandler(trackLabel) {
        trackLabel.addEventListener('click', event => {
          event.stopPropagation();
          const {
            track
          } = this.trackView;
          let str;

          if (typeof track.description === 'function') {
            str = track.description();
          } else if (track.description) {
            str = `<div>${track.description}</div>`;
          }

          if (str) {
            if (this.popover) {
              this.popover.dispose();
            }

            this.popover = new Popover(this.browser.columnContainer, track.name || '');
            this.popover.presentContentWithEvent(event, str);
          }
        });
      }

    }

    function createClickState(event, viewport) {
      const referenceFrame = viewport.referenceFrame;
      const viewportCoords = translateMouseCoordinates$1(event, viewport.contentDiv);
      const canvasCoords = translateMouseCoordinates$1(event, viewport.canvas);
      const genomicLocation = referenceFrame.start + referenceFrame.toBP(viewportCoords.x);
      return {
        event,
        viewport,
        referenceFrame,
        genomicLocation,
        x: viewportCoords.x,
        y: viewportCoords.y,
        canvasX: canvasCoords.x,
        canvasY: canvasCoords.y
      };
    }

    function getPopupContent(event, viewport) {
      const clickState = createClickState(event, viewport);

      if (undefined === clickState) {
        return;
      }

      let track = viewport.trackView.track;
      const dataList = track.popupData(clickState);
      const popupClickHandlerResult = viewport.browser.fireEvent('trackclick', [track, dataList]);
      let content;

      if (undefined === popupClickHandlerResult || true === popupClickHandlerResult) {
        // Indicates handler did not handle the result, or the handler wishes default behavior to occur
        if (dataList && dataList.length > 0) {
          content = formatPopoverText(dataList);
        }
      } else if (typeof popupClickHandlerResult === 'string') {
        content = popupClickHandlerResult;
      }

      return content;
    }

    function formatPopoverText(nameValues) {
      const rows = nameValues.map(nameValue => {
        if (nameValue.name) {
          const str = `<span>${nameValue.name}</span>&nbsp&nbsp&nbsp${nameValue.value}`;
          return `<div title="${nameValue.value}">${str}</div>`;
        } else if ('<hr>' === nameValue) {
          // this can be retired if nameValue.html is allowed.
          return nameValue;
        } else if (nameValue.html) {
          return nameValue.html;
        } else {
          return `<div title="${nameValue}">${nameValue}</div>`;
        }
      });
      return rows.join('');
    }

    class FeatureCache {
      constructor(chr, tileStart, tileEnd, bpPerPixel, features, roiFeatures, multiresolution) {
        this.chr = chr;
        this.startBP = tileStart;
        this.endBP = tileEnd;
        this.bpPerPixel = bpPerPixel;
        this.features = features;
        this.roiFeatures = roiFeatures;
        this.multiresolution = multiresolution;
      }

      containsRange(chr, start, end, bpPerPixel) {
        // For multi-resolution tracks allow for a 2X change in bpPerPixel
        const r = this.multiresolution ? this.bpPerPixel / bpPerPixel : 1;
        return start >= this.startBP && end <= this.endBP && chr === this.chr && r > 0.5 && r < 2;
      }

      overlapsRange(chr, start, end) {
        return this.chr === chr && end >= this.startBP && start <= this.endBP;
      }

    }
    /**
     * Merge 2 arrays.  a and/or b can be undefined.  If both are undefined, return undefined
     * @param a An array or undefined
     * @param b An array or undefined
     */


    function mergeArrays(a, b) {
      if (a && b) return a.concat(b);else if (a) return a;else return b;
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class RulerSweeper {
      constructor(rulerViewport) {
        this.rulerSweeper = div$1({
          class: 'igv-ruler-sweeper'
        });
        rulerViewport.contentDiv.appendChild(this.rulerSweeper);
        this.rulerViewport = rulerViewport;
        this.isMouseHandlers = undefined;
        this.addBrowserObserver();
      }

      addBrowserObserver() {
        // Viewport Content
        this.boundObserverHandler = observerHandler.bind(this);
        this.rulerViewport.browser.on('locuschange', this.boundObserverHandler);

        function observerHandler() {
          if (this.rulerViewport.referenceFrame) {
            if (GenomeUtils.isWholeGenomeView(this.rulerViewport.referenceFrame.chr)) {
              this.removeMouseHandlers();
            } else {
              this.addMouseHandlers();
            }
          }
        }
      }

      removeBrowserObserver() {
        this.rulerViewport.browser.off('locuschange', this.boundObserverHandler);
      }

      addMouseHandlers() {
        if (true === this.isMouseHandlers) {
          return;
        }

        let isMouseDown;
        let isMouseIn;
        let mouseDownX;
        let left;
        let width;
        let dx;
        let threshold = 1; // Viewport Content

        this.boundContentMouseDownHandler = contentMouseDownHandler.bind(this);
        this.rulerViewport.contentDiv.addEventListener('mousedown', this.boundContentMouseDownHandler);

        function contentMouseDownHandler(event) {
          isMouseDown = true;
          isMouseIn = true;
          const {
            x
          } = translateMouseCoordinates$1(event, this.rulerViewport.contentDiv);
          left = mouseDownX = x;
          width = threshold;
          this.rulerSweeper.style.display = 'block';
          this.rulerSweeper.style.left = `${left}px`;
          this.rulerSweeper.style.width = `${width}px`;
        } // Document


        this.boundDocumentMouseMoveHandler = documentMouseMoveHandler.bind(this);
        document.addEventListener('mousemove', this.boundDocumentMouseMoveHandler);

        function documentMouseMoveHandler(event) {
          // console.log(`${ Date.now() } RulerSweeper - documentMouseMoveHandler - target ${ event.target.nodeName }`)
          let mouseCurrentX;

          if (isMouseDown && isMouseIn) {
            const {
              x
            } = translateMouseCoordinates$1(event, this.rulerViewport.contentDiv);
            mouseCurrentX = Math.max(Math.min(x, this.rulerViewport.contentDiv.clientWidth), 0);
            dx = mouseCurrentX - mouseDownX;
            width = Math.abs(dx);
            this.rulerSweeper.style.width = `${width}px`;

            if (dx < 0) {
              left = mouseDownX + dx;
              this.rulerSweeper.style.left = `${left}px`;
            }
          }
        }

        this.boundDocumentMouseUpHandler = documentMouseUpHandler.bind(this);
        document.addEventListener('mouseup', this.boundDocumentMouseUpHandler);

        function documentMouseUpHandler(event) {
          // console.log(`${ Date.now() } RulerSweeper - documentMouseUpHandler - target ${ event.target.nodeName }`)
          let extent;

          if (true === isMouseDown && true === isMouseIn) {
            isMouseDown = isMouseIn = undefined;
            this.rulerSweeper.style.display = 'none';

            if (width > threshold) {
              extent = {
                start: bp(this.rulerViewport.referenceFrame, left),
                end: bp(this.rulerViewport.referenceFrame, left + width)
              };
              validateLocusExtent(this.rulerViewport.browser.genome.getChromosome(this.rulerViewport.referenceFrame.chr).bpLength, extent, this.rulerViewport.browser.minimumBases());
              const newStart = Math.round(extent.start);
              const newEnd = Math.round(extent.end);
              this.rulerViewport.referenceFrame.bpPerPixel = (newEnd - newStart) / this.rulerViewport.contentDiv.clientWidth;
              this.rulerViewport.referenceFrame.start = newStart;
              this.rulerViewport.referenceFrame.end = newEnd;
              this.rulerViewport.browser.updateViews();
            }
          }
        }

        this.isMouseHandlers = true;
      }

      removeMouseHandlers() {
        this.rulerViewport.contentDiv.removeEventListener('mousedown', this.boundContentMouseDownHandler);
        document.removeEventListener('mousemove', this.boundDocumentMouseMoveHandler);
        document.removeEventListener('mouseup', this.boundDocumentMouseUpHandler);
        this.isMouseHandlers = false;
      }

      dispose() {
        this.removeBrowserObserver();
        this.removeMouseHandlers();
        this.rulerSweeper.remove();
      }

    }

    function bp(referenceFrame, pixel) {
      return referenceFrame.start + pixel * referenceFrame.bpPerPixel;
    }

    // eslint-disable-next-line es-x/no-typed-arrays -- safe
    var arrayBufferNative = typeof ArrayBuffer != 'undefined' && typeof DataView != 'undefined';

    var TO_STRING_TAG$2 = wellKnownSymbol('toStringTag');
    var test = {};
    test[TO_STRING_TAG$2] = 'z';
    var toStringTagSupport = String(test) === '[object z]';

    var TO_STRING_TAG$1 = wellKnownSymbol('toStringTag');
    var Object$2 = global$1.Object; // ES3 wrong here

    var CORRECT_ARGUMENTS = classofRaw(function () {
      return arguments;
    }()) == 'Arguments'; // fallback for IE11 Script Access Denied error

    var tryGet = function (it, key) {
      try {
        return it[key];
      } catch (error) {
        /* empty */
      }
    }; // getting tag from ES6+ `Object.prototype.toString`


    var classof = toStringTagSupport ? classofRaw : function (it) {
      var O, tag, result;
      return it === undefined ? 'Undefined' : it === null ? 'Null' // @@toStringTag case
      : typeof (tag = tryGet(O = Object$2(it), TO_STRING_TAG$1)) == 'string' ? tag // builtinTag case
      : CORRECT_ARGUMENTS ? classofRaw(O) // ES3 arguments fallback
      : (result = classofRaw(O)) == 'Object' && isCallable(O.callee) ? 'Arguments' : result;
    };

    var correctPrototypeGetter = !fails(function () {
      function F() {
        /* empty */
      }

      F.prototype.constructor = null; // eslint-disable-next-line es-x/no-object-getprototypeof -- required for testing

      return Object.getPrototypeOf(new F()) !== F.prototype;
    });

    var IE_PROTO = sharedKey('IE_PROTO');
    var Object$1 = global$1.Object;
    var ObjectPrototype$1 = Object$1.prototype; // `Object.getPrototypeOf` method
    // https://tc39.es/ecma262/#sec-object.getprototypeof

    var objectGetPrototypeOf = correctPrototypeGetter ? Object$1.getPrototypeOf : function (O) {
      var object = toObject(O);
      if (hasOwnProperty_1(object, IE_PROTO)) return object[IE_PROTO];
      var constructor = object.constructor;

      if (isCallable(constructor) && object instanceof constructor) {
        return constructor.prototype;
      }

      return object instanceof Object$1 ? ObjectPrototype$1 : null;
    };

    var String$2 = global$1.String;
    var TypeError$3 = global$1.TypeError;

    var aPossiblePrototype = function (argument) {
      if (typeof argument == 'object' || isCallable(argument)) return argument;
      throw TypeError$3("Can't set " + String$2(argument) + ' as a prototype');
    };

    /* eslint-disable no-proto -- safe */
    // https://tc39.es/ecma262/#sec-object.setprototypeof
    // Works with __proto__ only. Old v8 can't work with null proto objects.
    // eslint-disable-next-line es-x/no-object-setprototypeof -- safe

    var objectSetPrototypeOf = Object.setPrototypeOf || ('__proto__' in {} ? function () {
      var CORRECT_SETTER = false;
      var test = {};
      var setter;

      try {
        // eslint-disable-next-line es-x/no-object-getownpropertydescriptor -- safe
        setter = functionUncurryThis(Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set);
        setter(test, []);
        CORRECT_SETTER = test instanceof Array;
      } catch (error) {
        /* empty */
      }

      return function setPrototypeOf(O, proto) {
        anObject(O);
        aPossiblePrototype(proto);
        if (CORRECT_SETTER) setter(O, proto);else O.__proto__ = proto;
        return O;
      };
    }() : undefined);

    var defineProperty = objectDefineProperty.f;
    var Int8Array$1 = global$1.Int8Array;
    var Int8ArrayPrototype$1 = Int8Array$1 && Int8Array$1.prototype;
    var Uint8ClampedArray$1 = global$1.Uint8ClampedArray;
    var Uint8ClampedArrayPrototype = Uint8ClampedArray$1 && Uint8ClampedArray$1.prototype;
    var TypedArray = Int8Array$1 && objectGetPrototypeOf(Int8Array$1);
    var TypedArrayPrototype = Int8ArrayPrototype$1 && objectGetPrototypeOf(Int8ArrayPrototype$1);
    var ObjectPrototype = Object.prototype;
    var TypeError$2 = global$1.TypeError;
    var TO_STRING_TAG = wellKnownSymbol('toStringTag');
    var TYPED_ARRAY_TAG = uid('TYPED_ARRAY_TAG');
    var TYPED_ARRAY_CONSTRUCTOR = uid('TYPED_ARRAY_CONSTRUCTOR'); // Fixing native typed arrays in Opera Presto crashes the browser, see #595

    var NATIVE_ARRAY_BUFFER_VIEWS = arrayBufferNative && !!objectSetPrototypeOf && classof(global$1.opera) !== 'Opera';
    var TYPED_ARRAY_TAG_REQUIRED = false;
    var NAME, Constructor, Prototype;
    var TypedArrayConstructorsList = {
      Int8Array: 1,
      Uint8Array: 1,
      Uint8ClampedArray: 1,
      Int16Array: 2,
      Uint16Array: 2,
      Int32Array: 4,
      Uint32Array: 4,
      Float32Array: 4,
      Float64Array: 8
    };
    var BigIntArrayConstructorsList = {
      BigInt64Array: 8,
      BigUint64Array: 8
    };

    var isView = function isView(it) {
      if (!isObject(it)) return false;
      var klass = classof(it);
      return klass === 'DataView' || hasOwnProperty_1(TypedArrayConstructorsList, klass) || hasOwnProperty_1(BigIntArrayConstructorsList, klass);
    };

    var isTypedArray = function (it) {
      if (!isObject(it)) return false;
      var klass = classof(it);
      return hasOwnProperty_1(TypedArrayConstructorsList, klass) || hasOwnProperty_1(BigIntArrayConstructorsList, klass);
    };

    var aTypedArray$1 = function (it) {
      if (isTypedArray(it)) return it;
      throw TypeError$2('Target is not a typed array');
    };

    var aTypedArrayConstructor = function (C) {
      if (isCallable(C) && (!objectSetPrototypeOf || objectIsPrototypeOf(TypedArray, C))) return C;
      throw TypeError$2(tryToString(C) + ' is not a typed array constructor');
    };

    var exportTypedArrayMethod$1 = function (KEY, property, forced, options) {
      if (!descriptors) return;
      if (forced) for (var ARRAY in TypedArrayConstructorsList) {
        var TypedArrayConstructor = global$1[ARRAY];
        if (TypedArrayConstructor && hasOwnProperty_1(TypedArrayConstructor.prototype, KEY)) try {
          delete TypedArrayConstructor.prototype[KEY];
        } catch (error) {
          // old WebKit bug - some methods are non-configurable
          try {
            TypedArrayConstructor.prototype[KEY] = property;
          } catch (error2) {
            /* empty */
          }
        }
      }

      if (!TypedArrayPrototype[KEY] || forced) {
        defineBuiltIn(TypedArrayPrototype, KEY, forced ? property : NATIVE_ARRAY_BUFFER_VIEWS && Int8ArrayPrototype$1[KEY] || property, options);
      }
    };

    var exportTypedArrayStaticMethod = function (KEY, property, forced) {
      var ARRAY, TypedArrayConstructor;
      if (!descriptors) return;

      if (objectSetPrototypeOf) {
        if (forced) for (ARRAY in TypedArrayConstructorsList) {
          TypedArrayConstructor = global$1[ARRAY];
          if (TypedArrayConstructor && hasOwnProperty_1(TypedArrayConstructor, KEY)) try {
            delete TypedArrayConstructor[KEY];
          } catch (error) {
            /* empty */
          }
        }

        if (!TypedArray[KEY] || forced) {
          // V8 ~ Chrome 49-50 `%TypedArray%` methods are non-writable non-configurable
          try {
            return defineBuiltIn(TypedArray, KEY, forced ? property : NATIVE_ARRAY_BUFFER_VIEWS && TypedArray[KEY] || property);
          } catch (error) {
            /* empty */
          }
        } else return;
      }

      for (ARRAY in TypedArrayConstructorsList) {
        TypedArrayConstructor = global$1[ARRAY];

        if (TypedArrayConstructor && (!TypedArrayConstructor[KEY] || forced)) {
          defineBuiltIn(TypedArrayConstructor, KEY, property);
        }
      }
    };

    for (NAME in TypedArrayConstructorsList) {
      Constructor = global$1[NAME];
      Prototype = Constructor && Constructor.prototype;
      if (Prototype) createNonEnumerableProperty(Prototype, TYPED_ARRAY_CONSTRUCTOR, Constructor);else NATIVE_ARRAY_BUFFER_VIEWS = false;
    }

    for (NAME in BigIntArrayConstructorsList) {
      Constructor = global$1[NAME];
      Prototype = Constructor && Constructor.prototype;
      if (Prototype) createNonEnumerableProperty(Prototype, TYPED_ARRAY_CONSTRUCTOR, Constructor);
    } // WebKit bug - typed arrays constructors prototype is Object.prototype


    if (!NATIVE_ARRAY_BUFFER_VIEWS || !isCallable(TypedArray) || TypedArray === Function.prototype) {
      // eslint-disable-next-line no-shadow -- safe
      TypedArray = function TypedArray() {
        throw TypeError$2('Incorrect invocation');
      };

      if (NATIVE_ARRAY_BUFFER_VIEWS) for (NAME in TypedArrayConstructorsList) {
        if (global$1[NAME]) objectSetPrototypeOf(global$1[NAME], TypedArray);
      }
    }

    if (!NATIVE_ARRAY_BUFFER_VIEWS || !TypedArrayPrototype || TypedArrayPrototype === ObjectPrototype) {
      TypedArrayPrototype = TypedArray.prototype;
      if (NATIVE_ARRAY_BUFFER_VIEWS) for (NAME in TypedArrayConstructorsList) {
        if (global$1[NAME]) objectSetPrototypeOf(global$1[NAME].prototype, TypedArrayPrototype);
      }
    } // WebKit bug - one more object in Uint8ClampedArray prototype chain


    if (NATIVE_ARRAY_BUFFER_VIEWS && objectGetPrototypeOf(Uint8ClampedArrayPrototype) !== TypedArrayPrototype) {
      objectSetPrototypeOf(Uint8ClampedArrayPrototype, TypedArrayPrototype);
    }

    if (descriptors && !hasOwnProperty_1(TypedArrayPrototype, TO_STRING_TAG)) {
      TYPED_ARRAY_TAG_REQUIRED = true;
      defineProperty(TypedArrayPrototype, TO_STRING_TAG, {
        get: function () {
          return isObject(this) ? this[TYPED_ARRAY_TAG] : undefined;
        }
      });

      for (NAME in TypedArrayConstructorsList) if (global$1[NAME]) {
        createNonEnumerableProperty(global$1[NAME], TYPED_ARRAY_TAG, NAME);
      }
    }

    var arrayBufferViewCore = {
      NATIVE_ARRAY_BUFFER_VIEWS: NATIVE_ARRAY_BUFFER_VIEWS,
      TYPED_ARRAY_CONSTRUCTOR: TYPED_ARRAY_CONSTRUCTOR,
      TYPED_ARRAY_TAG: TYPED_ARRAY_TAG_REQUIRED && TYPED_ARRAY_TAG,
      aTypedArray: aTypedArray$1,
      aTypedArrayConstructor: aTypedArrayConstructor,
      exportTypedArrayMethod: exportTypedArrayMethod$1,
      exportTypedArrayStaticMethod: exportTypedArrayStaticMethod,
      isView: isView,
      isTypedArray: isTypedArray,
      TypedArray: TypedArray,
      TypedArrayPrototype: TypedArrayPrototype
    };

    var RangeError$3 = global$1.RangeError;

    var toPositiveInteger = function (it) {
      var result = toIntegerOrInfinity(it);
      if (result < 0) throw RangeError$3("The argument can't be less than 0");
      return result;
    };

    var RangeError$2 = global$1.RangeError;

    var toOffset = function (it, BYTES) {
      var offset = toPositiveInteger(it);
      if (offset % BYTES) throw RangeError$2('Wrong offset');
      return offset;
    };

    var RangeError$1 = global$1.RangeError;
    var Int8Array = global$1.Int8Array;
    var Int8ArrayPrototype = Int8Array && Int8Array.prototype;
    var $set = Int8ArrayPrototype && Int8ArrayPrototype.set;
    var aTypedArray = arrayBufferViewCore.aTypedArray;
    var exportTypedArrayMethod = arrayBufferViewCore.exportTypedArrayMethod;
    var WORKS_WITH_OBJECTS_AND_GEERIC_ON_TYPED_ARRAYS = !fails(function () {
      // eslint-disable-next-line es-x/no-typed-arrays -- required for testing
      var array = new Uint8ClampedArray(2);
      functionCall($set, array, {
        length: 1,
        0: 3
      }, 1);
      return array[1] !== 3;
    }); // https://bugs.chromium.org/p/v8/issues/detail?id=11294 and other

    var TO_OBJECT_BUG = WORKS_WITH_OBJECTS_AND_GEERIC_ON_TYPED_ARRAYS && arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS && fails(function () {
      var array = new Int8Array(2);
      array.set(1);
      array.set('2', 1);
      return array[0] !== 0 || array[1] !== 2;
    }); // `%TypedArray%.prototype.set` method
    // https://tc39.es/ecma262/#sec-%typedarray%.prototype.set

    exportTypedArrayMethod('set', function set(arrayLike
    /* , offset */
    ) {
      aTypedArray(this);
      var offset = toOffset(arguments.length > 1 ? arguments[1] : undefined, 1);
      var src = toObject(arrayLike);
      if (WORKS_WITH_OBJECTS_AND_GEERIC_ON_TYPED_ARRAYS) return functionCall($set, this, src, offset);
      var length = this.length;
      var len = lengthOfArrayLike(src);
      var index = 0;
      if (len + offset > length) throw RangeError$1('Wrong length');

      while (index < len) this[offset + index] = src[index++];
    }, !WORKS_WITH_OBJECTS_AND_GEERIC_ON_TYPED_ARRAYS || TO_OBJECT_BUG);

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016 University of California San Diego
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    class PairedAlignment {
      constructor(firstAlignment) {
        this.paired = true;
        this.firstAlignment = firstAlignment;
        this.chr = firstAlignment.chr;
        this.readName = firstAlignment.readName;

        if (firstAlignment.start < firstAlignment.mate.position) {
          this.start = firstAlignment.start;
          this.scStart = firstAlignment.scStart;
          this.connectingStart = firstAlignment.start + firstAlignment.lengthOnRef;
          this.connectingEnd = firstAlignment.mate.position;
        } else {
          this.start = firstAlignment.mate.position;
          this.scStart = this.start;
          this.connectingStart = firstAlignment.mate.position;
          this.connectingEnd = firstAlignment.start;
        }

        this.end = Math.max(firstAlignment.mate.position, firstAlignment.start + firstAlignment.lengthOnRef); // Approximate

        this.lengthOnRef = this.end - this.start;
        let scEnd = Math.max(this.end, firstAlignment.scStart + firstAlignment.scLengthOnRef);
        this.scLengthOnRef = scEnd - this.scStart;
      }

      setSecondAlignment(secondAlignment) {
        // TODO -- check the chrs are equal,  error otherwise
        this.secondAlignment = secondAlignment;
        const firstAlignment = this.firstAlignment;

        if (secondAlignment.start > firstAlignment.start) {
          this.connectingEnd = secondAlignment.start;
        } else {
          this.connectingStart = secondAlignment.start + secondAlignment.lengthOnRef;
        }

        this.start = Math.min(firstAlignment.start, secondAlignment.start);
        this.end = Math.max(firstAlignment.start + firstAlignment.lengthOnRef, secondAlignment.start + secondAlignment.lengthOnRef);
        this.lengthOnRef = this.end - this.start;
        this.scStart = Math.min(firstAlignment.scStart, secondAlignment.scStart);
        const scEnd = Math.max(firstAlignment.scStart + firstAlignment.scLengthOnRef, secondAlignment.scStart + secondAlignment.scLengthOnRef);
        this.scLengthOnRef = scEnd - this.scStart;
      }

      containsLocation(genomicLocation, showSoftClips) {
        const s = showSoftClips ? this.scStart : this.start;
        const l = showSoftClips ? this.scLengthOnRef : this.lengthOnRef;
        return genomicLocation >= s && genomicLocation <= s + l;
      }

      alignmentContaining(genomicLocation, showSoftClips) {
        if (this.firstAlignment.containsLocation(genomicLocation, showSoftClips)) {
          return this.firstAlignment;
        } else if (this.secondAlignment && this.secondAlignment.containsLocation(genomicLocation, showSoftClips)) {
          return this.secondAlignment;
        } else {
          return undefined;
        }
      }

      popupData(genomicLocation) {
        let nameValues = this.firstAlignment.popupData(genomicLocation);

        if (this.secondAlignment) {
          nameValues.push("-------------------------------");
          nameValues = nameValues.concat(this.secondAlignment.popupData(genomicLocation));
        }

        return nameValues;
      }

      isPaired() {
        return true; // By definition
      }

      isMateMapped() {
        return true; // By definition
      }

      isProperPair() {
        return this.firstAlignment.isProperPair();
      }

      get fragmentLength() {
        return Math.abs(this.firstAlignment.fragmentLength);
      }

      firstOfPairStrand() {
        if (this.firstAlignment.isFirstOfPair()) {
          return this.firstAlignment.strand;
        } else if (this.secondAlignment && this.secondAlignment.isFirstOfPair()) {
          return this.secondAlignment.strand;
        } else {
          return this.firstAlignment.mate.strand; // Assumption is mate is first-of-pair
        }
      }

      hasTag(str) {
        return this.firstAlignment.hasTag(str) || this.secondAlignment && this.secondAlignment.hasTag(str);
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const isString$2 = isString$3;
    const hashCode = hashCode$1;

    class BamAlignmentRow {
      constructor() {
        this.alignments = [];
        this.score = undefined;
      }

      findAlignment(genomicLocation) {
        const alignmentContains = (a, genomicLocation) => {
          return genomicLocation >= a.start && genomicLocation < a.start + a.lengthOnRef;
        }; // find single alignment that overlaps sort location


        let centerAlignment;

        for (let i = 0; i < this.alignments.length; i++) {
          const a = this.alignments[i];

          if (genomicLocation >= a.start && genomicLocation < a.start + a.lengthOnRef) {
            if (a.paired) {
              if (a.firstAlignment && alignmentContains(a.firstAlignment, genomicLocation)) {
                centerAlignment = a.firstAlignment;
              } else if (a.secondAlignment && alignmentContains(a.secondAlignment, genomicLocation)) {
                centerAlignment = a.secondAlignment;
              }
            } else {
              centerAlignment = a;
            }

            break;
          }
        }

        return centerAlignment;
      }

      updateScore(options, alignmentContainer) {
        this.score = this.calculateScore(options, alignmentContainer);
      }

      calculateScore(_ref, alignmentContainer) {
        let {
          position,
          option,
          direction,
          tag
        } = _ref;
        if (!option) option = "BASE";
        const alignment = this.findAlignment(position);

        if (undefined === alignment) {
          return Number.MAX_VALUE * (direction ? 1 : -1);
        }

        let mate;

        switch (option) {
          case "NUCLEOTIDE":
          case "BASE":
            {
              return calculateBaseScore(alignment, alignmentContainer, position);
            }

          case "STRAND":
            return alignment.strand ? 1 : -1;

          case "START":
            return alignment.start;

          case "TAG":
            {
              const tagValue = alignment.tags()[tag];

              if (tagValue !== undefined) {
                return isString$2(tagValue) ? hashCode(tagValue) : tagValue;
              } else {
                return Number.MAX_VALUE;
              }
            }

          case "READ_NAME":
            return hashCode(alignment.readName);

          case "INSERT_SIZE":
            return -Math.abs(alignment.fragmentLength);

          case "GAP_SIZE":
            return -alignment.gapSizeAt(position);

          case "MATE_CHR":
            mate = alignment.mate;

            if (!mate) {
              return Number.MAX_VALUE;
            } else {
              if (mate.chr === alignment.chr) {
                return Number.MAX_VALUE - 1;
              } else {
                return hashCode(mate.chr);
              }
            }

          case "MQ":
            return alignment.mq === undefined ? Number.MAX_VALUE : -alignment.mq;

          default:
            return Number.MAX_VALUE;
        }

        function calculateBaseScore(alignment, alignmentContainer, genomicLocation) {
          let reference;
          const idx = Math.floor(genomicLocation) - alignmentContainer.start;

          if (idx < alignmentContainer.sequence.length) {
            reference = alignmentContainer.sequence.charAt(idx);
          }

          if (!reference) {
            return 0;
          }

          const base = alignment.readBaseAt(genomicLocation);
          const quality = alignment.readBaseQualityAt(genomicLocation);
          const coverageMap = alignmentContainer.coverageMap;
          const coverageMapIndex = Math.floor(genomicLocation - coverageMap.bpStart);
          const coverage = coverageMap.coverage[coverageMapIndex]; // Insertions.  These are additive with base scores as they occur between bases, so you can have a
          // base mismatch AND an insertion

          let baseScore = 0;

          if (alignment.insertions) {
            for (let ins of alignment.insertions) {
              if (ins.start === genomicLocation) {
                baseScore = -coverage.ins;
              }
            }
          }

          if (!base) {
            // Either deletion or skipped (splice junction)
            const delCount = coverage.del;

            if (delCount > 0) {
              baseScore -= delCount;
            } else if (baseScore === 0) {
              // Don't modify insertion score, if any
              baseScore = 1;
            }
          } else {
            reference = reference.toUpperCase();

            if ('N' === base && baseScore === 0) {
              baseScore = 2;
            } else if ((reference === base || '=' === base) && baseScore === 0) {
              baseScore = 4 - quality / 1000;
            } else if ("X" === base || reference !== base) {
              const count = coverage["pos" + base] + coverage["neg" + base];
              baseScore -= count + quality / 1000;
            }
          }

          return baseScore;
        }
      }

    }

    function canBePaired(alignment) {
      return alignment.isPaired() && alignment.mate && alignment.isMateMapped() && alignment.chr === alignment.mate.chr && (alignment.isFirstOfPair() || alignment.isSecondOfPair()) && !(alignment.isSecondary() || alignment.isSupplementary());
    }

    function pairAlignments(rows) {
      const pairCache = {};
      const result = [];

      for (let row of rows) {
        for (let alignment of row.alignments) {
          if (canBePaired(alignment)) {
            let pairedAlignment = pairCache[alignment.readName];

            if (pairedAlignment) {
              pairedAlignment.setSecondAlignment(alignment);
              pairCache[alignment.readName] = undefined; // Don't need to track this anymore.
            } else {
              pairedAlignment = new PairedAlignment(alignment);
              pairCache[alignment.readName] = pairedAlignment;
              result.push(pairedAlignment);
            }
          } else {
            result.push(alignment);
          }
        }
      }

      return result;
    }

    function unpairAlignments(rows) {
      const result = [];

      for (let row of rows) {
        for (let alignment of row.alignments) {
          if (alignment instanceof PairedAlignment) {
            if (alignment.firstAlignment) result.push(alignment.firstAlignment); // shouldn't need the null test

            if (alignment.secondAlignment) result.push(alignment.secondAlignment);
          } else {
            result.push(alignment);
          }
        }
      }

      return result;
    }

    function packAlignmentRows(alignments, start, end, showSoftClips) {
      if (!alignments) {
        return undefined;
      } else if (alignments.length === 0) {
        return [];
      } else {
        alignments.sort(function (a, b) {
          return showSoftClips ? a.scStart - b.scStart : a.start - b.start;
        }); // bucketStart = Math.max(start, alignments[0].start);

        const firstAlignment = alignments[0];
        let bucketStart = Math.max(start, showSoftClips ? firstAlignment.scStart : firstAlignment.start);
        let nextStart = bucketStart;
        const bucketList = [];

        for (let alignment of alignments) {
          //var buckListIndex = Math.max(0, alignment.start - bucketStart);
          const s = showSoftClips ? alignment.scStart : alignment.start;
          const buckListIndex = Math.max(0, s - bucketStart);

          if (bucketList[buckListIndex] === undefined) {
            bucketList[buckListIndex] = [];
          }

          bucketList[buckListIndex].push(alignment);
        }

        let allocatedCount = 0;
        let lastAllocatedCount = 0;
        const packedAlignmentRows = [];
        const alignmentSpace = 2;

        try {
          while (allocatedCount < alignments.length) {
            const alignmentRow = new BamAlignmentRow();

            while (nextStart <= end) {
              let bucket = undefined;
              let index;

              while (!bucket && nextStart <= end) {
                index = nextStart - bucketStart;

                if (bucketList[index] === undefined) {
                  ++nextStart; // No alignments at this index
                } else {
                  bucket = bucketList[index];
                }
              } // while (bucket)


              if (!bucket) {
                break;
              }

              const alignment = bucket.pop();

              if (0 === bucket.length) {
                bucketList[index] = undefined;
              }

              alignmentRow.alignments.push(alignment);
              nextStart = showSoftClips ? alignment.scStart + alignment.scLengthOnRef + alignmentSpace : alignment.start + alignment.lengthOnRef + alignmentSpace;
              ++allocatedCount;
            } // while (nextStart)


            if (alignmentRow.alignments.length > 0) {
              packedAlignmentRows.push(alignmentRow);
            }

            nextStart = bucketStart;
            if (allocatedCount === lastAllocatedCount) break; // Protect from infinite loops

            lastAllocatedCount = allocatedCount;
          } // while (allocatedCount)

        } catch (e) {
          console.error(e);
          throw e;
        }

        return packedAlignmentRows;
      }
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016 University of California San Diego
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class AlignmentContainer {
      //            this.config.samplingWindowSize, this.config.samplingDepth,
      //             this.config.pairsSupported, this.config.alleleFreqThreshold)
      constructor(chr, start, end, _ref) {
        let {
          samplingWindowSize,
          samplingDepth,
          pairsSupported,
          alleleFreqThreshold
        } = _ref;
        this.chr = chr;
        this.start = Math.floor(start);
        this.end = Math.ceil(end);
        this.length = end - start;
        this.alleleFreqThreshold = alleleFreqThreshold === undefined ? 0.2 : alleleFreqThreshold;
        this.coverageMap = new CoverageMap(chr, start, end, this.alleleFreqThreshold);
        this.alignments = [];
        this.downsampledIntervals = [];
        this.samplingWindowSize = samplingWindowSize === undefined ? 100 : samplingWindowSize;
        this.samplingDepth = samplingDepth === undefined ? 1000 : samplingDepth;
        this.pairsSupported = pairsSupported === undefined ? true : pairsSupported;
        this.paired = false; // false until proven otherwise

        this.pairsCache = {}; // working cache of paired alignments by read name

        this.downsampledReads = new Set();
        this.currentBucket = new DownsampleBucket(this.start, this.start + this.samplingWindowSize, this);

        this.filter = function filter(alignment) {
          // TODO -- pass this in
          return alignment.isMapped() && !alignment.isFailsVendorQualityCheck();
        };
      }

      push(alignment) {
        if (this.filter(alignment) === false) return;
        this.coverageMap.incCounts(alignment); // Count coverage before any downsampling

        if (this.pairsSupported && this.downsampledReads.has(alignment.readName)) {
          return; // Mate already downsampled -- pairs are treated as a single alignment for downsampling
        }

        if (alignment.start >= this.currentBucket.end) {
          this.finishBucket();
          this.currentBucket = new DownsampleBucket(alignment.start, alignment.start + this.samplingWindowSize, this);
        }

        this.currentBucket.addAlignment(alignment);
      }

      forEach(callback) {
        this.alignments.forEach(callback);
      }

      finish() {
        if (this.currentBucket !== undefined) {
          this.finishBucket();
        }

        this.alignments.sort(function (a, b) {
          return a.start - b.start;
        });
        this.pairsCache = undefined;
        this.downsampledReads = undefined;
      }

      contains(chr, start, end) {
        return this.chr === chr && this.start <= start && this.end >= end;
      }

      hasDownsampledIntervals() {
        return this.downsampledIntervals && this.downsampledIntervals.length > 0;
      }

      finishBucket() {
        this.alignments = this.alignments.concat(this.currentBucket.alignments);

        if (this.currentBucket.downsampledCount > 0) {
          this.downsampledIntervals.push(new DownsampledInterval(this.currentBucket.start, this.currentBucket.end, this.currentBucket.downsampledCount));
        }

        this.paired = this.paired || this.currentBucket.paired;
      }

      setViewAsPairs(bool) {
        let alignments;

        if (bool) {
          alignments = pairAlignments(this.packedAlignmentRows);
        } else {
          alignments = unpairAlignments(this.packedAlignmentRows);
        }

        this.packedAlignmentRows = packAlignmentRows(alignments, this.start, this.end);
      }

      setShowSoftClips(bool) {
        const alignments = this.allAlignments();
        this.packedAlignmentRows = packAlignmentRows(alignments, this.start, this.end, bool);
      }

      repack(bpPerPixel, showSoftClips) {
        const alignments = this.allAlignments();
        this.packedAlignmentRows = packAlignmentRows(alignments, this.start, this.end, showSoftClips);
      }

      allAlignments() {
        const alignments = [];

        for (let row of this.packedAlignmentRows) {
          for (let alignment of row.alignments) {
            alignments.push(alignment);
          }
        }

        return alignments;
      }

      getMax(start, end) {
        return this.coverageMap.getMax(start, end);
      }

    }

    class DownsampleBucket {
      constructor(start, end, alignmentContainer) {
        this.start = start;
        this.end = end;
        this.alignments = [];
        this.downsampledCount = 0;
        this.samplingDepth = alignmentContainer.samplingDepth;
        this.pairsSupported = alignmentContainer.pairsSupported;
        this.downsampledReads = alignmentContainer.downsampledReads;
        this.pairsCache = alignmentContainer.pairsCache;
      }

      addAlignment(alignment) {
        var idx, replacedAlignment, pairedAlignment;

        if (this.pairsSupported && canBePaired(alignment)) {
          pairedAlignment = this.pairsCache[alignment.readName];

          if (pairedAlignment) {
            // Not subject to downsampling, just update the existing alignment
            pairedAlignment.setSecondAlignment(alignment);
            this.pairsCache[alignment.readName] = undefined; // Don't need to track this anymore. NOTE: Don't "delete", causes runtime performance issues

            return;
          }
        }

        if (this.alignments.length < this.samplingDepth) {
          if (this.pairsSupported && canBePaired(alignment)) {
            // First alignment in a pair
            pairedAlignment = new PairedAlignment(alignment);
            this.paired = true;
            this.pairsCache[alignment.readName] = pairedAlignment;
            this.alignments.push(pairedAlignment);
          } else {
            this.alignments.push(alignment);
          }
        } else {
          idx = Math.floor(Math.random() * (this.samplingDepth + this.downsampledCount - 1));

          if (idx < this.samplingDepth) {
            // Keep the new item
            //  idx = Math.floor(Math.random() * (this.alignments.length - 1));
            replacedAlignment = this.alignments[idx]; // To be replaced

            if (this.pairsSupported && canBePaired(alignment)) {
              if (this.pairsCache[replacedAlignment.readName] !== undefined) {
                this.pairsCache[replacedAlignment.readName] = undefined;
              }

              pairedAlignment = new PairedAlignment(alignment);
              this.paired = true;
              this.pairsCache[alignment.readName] = pairedAlignment;
              this.alignments[idx] = pairedAlignment;
            } else {
              this.alignments[idx] = alignment;
            }

            this.downsampledReads.add(replacedAlignment.readName);
          } else {
            this.downsampledReads.add(alignment.readName);
          }

          this.downsampledCount++;
        }
      }

    }

    class CoverageMap {
      constructor(chr, start, end, alleleFreqThreshold) {
        this.chr = chr;
        this.bpStart = start;
        this.length = end - start;
        this.coverage = new Array(this.length);
        this.maximum = 0;
        this.threshold = alleleFreqThreshold;
        this.qualityWeight = true;
      }
      /**
       * Return the maximum coverage value between start and end.  This is used for autoscaling.
       * @param start
       * @param end
       */


      getMax(start, end) {
        let max = 0;
        const len = this.coverage.length;

        for (let i = 0; i < len; i++) {
          const pos = this.bpStart + i;
          if (pos > end) break;
          const cov = this.coverage[i];

          if (pos >= start && cov) {
            max = Math.max(max, cov.total);
          }
        }

        return max;
      }

      incCounts(alignment) {
        var self = this;

        if (alignment.blocks === undefined) {
          incBlockCount(alignment);
        } else {
          alignment.blocks.forEach(function (block) {
            incBlockCount(block);
          });
        }

        if (alignment.gaps) {
          for (let del of alignment.gaps) {
            if (del.type === 'D') {
              const offset = del.start - self.bpStart;

              for (let i = offset; i < offset + del.len; i++) {
                if (i < 0) continue;

                if (!this.coverage[i]) {
                  this.coverage[i] = new Coverage(self.threshold);
                }

                this.coverage[i].del++;
              }
            }
          }
        }

        if (alignment.insertions) {
          for (let del of alignment.insertions) {
            const i = del.start - this.bpStart;
            if (i < 0) continue;

            if (!this.coverage[i]) {
              this.coverage[i] = new Coverage(self.threshold);
            }

            this.coverage[i].ins++;
          }
        }

        function incBlockCount(block) {
          if ('S' === block.type) return;
          const seq = alignment.seq;
          const qual = alignment.qual;
          const seqOffset = block.seqOffset;

          for (let i = block.start - self.bpStart, j = 0; j < block.len; i++, j++) {
            if (!self.coverage[i]) {
              self.coverage[i] = new Coverage(self.threshold);
            }

            const base = seq == undefined ? "N" : seq.charAt(seqOffset + j);
            const key = alignment.strand ? "pos" + base : "neg" + base;
            const q = qual && seqOffset + j < qual.length ? qual[seqOffset + j] : 30;
            self.coverage[i][key] += 1;
            self.coverage[i]["qual" + base] += q;
            self.coverage[i].total += 1;
            self.coverage[i].qual += q;
            self.maximum = Math.max(self.coverage[i].total, self.maximum);
          }
        }
      }

    }

    class Coverage {
      constructor(alleleThreshold) {
        this.qualityWeight = true;
        this.posA = 0;
        this.negA = 0;
        this.posT = 0;
        this.negT = 0;
        this.posC = 0;
        this.negC = 0;
        this.posG = 0;
        this.negG = 0;
        this.posN = 0;
        this.negN = 0;
        this.pos = 0;
        this.neg = 0;
        this.qualA = 0;
        this.qualT = 0;
        this.qualC = 0;
        this.qualG = 0;
        this.qualN = 0;
        this.qual = 0;
        this.total = 0;
        this.del = 0;
        this.ins = 0;
        this.threshold = alleleThreshold;
      }

      isMismatch(refBase) {
        const threshold = this.threshold * (this.qualityWeight && this.qual ? this.qual : this.total);
        let mismatchQualitySum = 0;

        for (let base of ["A", "T", "C", "G"]) {
          if (base !== refBase) {
            mismatchQualitySum += this.qualityWeight && this.qual ? this["qual" + base] : this["pos" + base] + this["neg" + base];
          }
        }

        return mismatchQualitySum >= threshold;
      }

    }

    class DownsampledInterval {
      constructor(start, end, counts) {
        this.start = start;
        this.end = end;
        this.counts = counts;
      }

      popupData(genomicLocation) {
        return [{
          name: "start",
          value: this.start + 1
        }, {
          name: "end",
          value: this.end
        }, {
          name: "# downsampled:",
          value: this.counts
        }];
      }

    }

    class SupplementaryAlignment {
      constructor(rec) {
        const tokens = rec.split(',');
        this.chr = tokens[0];
        this.start = parseInt(tokens[1]);
        this.strand = tokens[2].charAt(0);
        this.mapQ = parseInt(tokens[4]);
        this.numMismatches = parseInt(tokens[5]);
        this.lenOnRef = BamUtils.computeLengthOnReference(tokens[3]);
      }

      printString() {
        return this.chr + ":" + numberFormatter$1(this.start) + "-" + numberFormatter$1(this.start + this.lenOnRef) + " (" + this.strand + ") = " + numberFormatter$1(this.lenOnRef) + "bp @MAPQ: " + this.mapQ + " NM: " + this.numMismatches;
      }

    }

    function createSupplementaryAlignments(str) {
      const tokens = str.split(';');
      return tokens.filter(t => t.length > 0).map(str => new SupplementaryAlignment(str));
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const READ_PAIRED_FLAG = 0x1;
    const PROPER_PAIR_FLAG = 0x2;
    const READ_UNMAPPED_FLAG = 0x4;
    const MATE_UNMAPPED_FLAG = 0x8;
    const READ_STRAND_FLAG$2 = 0x10;
    const MATE_STRAND_FLAG$2 = 0x20;
    const FIRST_OF_PAIR_FLAG = 0x40;
    const SECOND_OF_PAIR_FLAG = 0x80;
    const SECONDARY_ALIGNMNET_FLAG = 0x100;
    const READ_FAILS_VENDOR_QUALITY_CHECK_FLAG = 0x200;
    const DUPLICATE_READ_FLAG = 0x400;
    const SUPPLEMENTARY_ALIGNMENT_FLAG = 0x800;
    const ELEMENT_SIZE = {
      c: 1,
      C: 1,
      s: 2,
      S: 2,
      i: 4,
      I: 4,
      f: 4
    };
    const MAX_CIGAR = 50;
    /**
     * readName
     * chr
     * cigar
     * lengthOnRef
     * start
     * seq
     * qual
     * mq
     * strand
     * blocks
     */

    class BamAlignment {
      constructor() {
        this.hidden = false;
      }

      isMapped() {
        return (this.flags & READ_UNMAPPED_FLAG) === 0;
      }

      isPaired() {
        return (this.flags & READ_PAIRED_FLAG) !== 0;
      }

      isProperPair() {
        return (this.flags & PROPER_PAIR_FLAG) !== 0;
      }

      isFirstOfPair() {
        return (this.flags & FIRST_OF_PAIR_FLAG) !== 0;
      }

      isSecondOfPair() {
        return (this.flags & SECOND_OF_PAIR_FLAG) !== 0;
      }

      isSecondary() {
        return (this.flags & SECONDARY_ALIGNMNET_FLAG) !== 0;
      }

      isSupplementary() {
        return (this.flags & SUPPLEMENTARY_ALIGNMENT_FLAG) !== 0;
      }

      isFailsVendorQualityCheck() {
        return (this.flags & READ_FAILS_VENDOR_QUALITY_CHECK_FLAG) !== 0;
      }

      isDuplicate() {
        return (this.flags & DUPLICATE_READ_FLAG) !== 0;
      }

      isMateMapped() {
        return (this.flags & MATE_UNMAPPED_FLAG) === 0;
      }

      isNegativeStrand() {
        return (this.flags & READ_STRAND_FLAG$2) !== 0;
      }

      isMateNegativeStrand() {
        return (this.flags & MATE_STRAND_FLAG$2) !== 0;
      }

      hasTag(tag) {
        const tmpTags = this.tagDict || decodeTags(this.tagBA);
        return tmpTags.hasOwnProperty(tag);
      }

      tags() {
        if (!this.tagDict) {
          if (this.tagBA) {
            this.tagDict = decodeTags(this.tagBA);
            this.tagBA = undefined;
          } else {
            this.tagDict = {}; // Mark so we don't try again.  The record has no tags
          }
        }

        return this.tagDict;
      }
      /**
       * Does alignment (or alignment extended by soft clips) contain the genomic location?
       *
       * @param genomicLocation
       * @param showSoftClips
       * @returns {boolean|boolean}
       */


      containsLocation(genomicLocation, showSoftClips) {
        const s = showSoftClips ? this.scStart : this.start;
        const l = showSoftClips ? this.scLengthOnRef : this.lengthOnRef;
        return genomicLocation >= s && genomicLocation <= s + l;
      }

      popupData(genomicLocation) {
        // if the user clicks on a base next to an insertion, show just the
        // inserted bases in a popup (like in desktop IGV).
        const nameValues = []; // Consert genomic location to int

        genomicLocation = Math.floor(genomicLocation);

        if (this.insertions) {
          const seq = this.seq;

          for (let insertion of this.insertions) {
            var ins_start = insertion.start;

            if (genomicLocation === ins_start || genomicLocation === ins_start - 1) {
              nameValues.push({
                name: 'Insertion',
                value: seq.substr(insertion.seqOffset, insertion.len)
              });
              nameValues.push({
                name: 'Location',
                value: ins_start
              });
              return nameValues;
            }
          }
        }

        nameValues.push({
          name: 'Read Name',
          value: this.readName
        }); // Sample
        // Read group

        nameValues.push('<hr/>'); // Add 1 to genomic location to map from 0-based computer units to user-based units

        nameValues.push({
          name: 'Alignment Start',
          value: numberFormatter$1(1 + this.start),
          borderTop: true
        });
        nameValues.push({
          name: 'Read Strand',
          value: true === this.strand ? '(+)' : '(-)',
          borderTop: true
        }); // Abbreviate long cigar strings, keeping the beginning and end to show cliping

        let cigar = this.cigar;

        if (cigar && cigar.length > MAX_CIGAR) {
          const half = MAX_CIGAR / 2;
          cigar = `${cigar.substring(0, half - 2)} ... ${cigar.substring(cigar.length - half + 2)}`;
        }

        nameValues.push({
          name: 'Cigar',
          value: cigar
        });
        nameValues.push({
          name: 'Mapping Quality',
          value: this.mq
        });
        nameValues.push({
          name: 'Secondary',
          value: yesNo(this.isSecondary())
        });
        nameValues.push({
          name: 'Supplementary',
          value: yesNo(this.isSupplementary())
        });
        nameValues.push({
          name: 'Duplicate',
          value: yesNo(this.isDuplicate())
        });
        nameValues.push({
          name: 'Failed QC',
          value: yesNo(this.isFailsVendorQualityCheck())
        });

        if (this.isPaired()) {
          nameValues.push('<hr/>');
          nameValues.push({
            name: 'First in Pair',
            value: !this.isSecondOfPair(),
            borderTop: true
          });
          nameValues.push({
            name: 'Mate is Mapped',
            value: yesNo(this.isMateMapped())
          });

          if (this.pairOrientation) {
            nameValues.push({
              name: 'Pair Orientation',
              value: this.pairOrientation
            });
          }

          if (this.isMateMapped()) {
            nameValues.push({
              name: 'Mate Chromosome',
              value: this.mate.chr
            });
            nameValues.push({
              name: 'Mate Start',
              value: this.mate.position + 1
            });
            nameValues.push({
              name: 'Mate Strand',
              value: true === this.mate.strand ? '(+)' : '(-)'
            });
            nameValues.push({
              name: 'Insert Size',
              value: this.fragmentLength
            }); // Mate Start
            // Mate Strand
            // Insert Size
          } // First in Pair
          // Pair Orientation

        }

        const tagDict = this.tags();

        if (tagDict.hasOwnProperty('SA')) {
          nameValues.push('<hr/>');
          nameValues.push({
            name: 'Supplementary Alignments',
            value: ''
          });
          const sa = createSupplementaryAlignments(tagDict['SA']);

          if (sa) {
            nameValues.push('<ul>');

            for (let s of sa) {
              nameValues.push(`<li>${s.printString()}</li>`);
            }

            nameValues.push('</ul>');
          }
        }

        const hiddenTags = new Set(['SA', 'MD']);
        nameValues.push('<hr/>');

        for (let key in tagDict) {
          if (!hiddenTags.has(key)) {
            nameValues.push({
              name: key,
              value: tagDict[key]
            });
          }
        }

        nameValues.push({
          name: 'Hidden Tags',
          value: 'SA, MD'
        });
        nameValues.push('<hr/>');
        nameValues.push({
          name: 'Genomic Location: ',
          value: numberFormatter$1(1 + genomicLocation)
        });
        nameValues.push({
          name: 'Read Base:',
          value: this.readBaseAt(genomicLocation)
        });
        nameValues.push({
          name: 'Base Quality:',
          value: this.readBaseQualityAt(genomicLocation)
        });
        return nameValues;

        function yesNo(bool) {
          return bool ? 'Yes' : 'No';
        }
      }

      readBaseAt(genomicLocation) {
        const block = blockAtGenomicLocation(this.blocks, genomicLocation);

        if (block) {
          if ("*" === this.seq) {
            return "*";
          } else {
            const idx = block.seqIndexAt(genomicLocation); // if (idx >= 0 && idx < this.seq.length) {

            return this.seq[idx]; //  }
          }
        } else {
          return undefined;
        }
      }

      readBaseQualityAt(genomicLocation) {
        const block = blockAtGenomicLocation(this.blocks, genomicLocation);

        if (block) {
          if ("*" === this.qual) {
            return 30;
          } else {
            const idx = block.seqIndexAt(genomicLocation);

            if (idx >= 0 && this.qual && idx < this.qual.length) {
              return this.qual[idx];
            } else {
              return 30;
            }
          }
        } else {
          return undefined;
        }
      }

      gapSizeAt(genomicLocation) {
        if (this.gaps) {
          for (let gap of this.gaps) {
            if (genomicLocation >= gap.start && genomicLocation < gap.start + gap.len) {
              return gap.len;
            }
          }
        }

        return 0;
      }

    }

    function blockAtGenomicLocation(blocks, genomicLocation) {
      for (let i = 0; i < blocks.length; i++) {
        const block = blocks[i];

        if (genomicLocation >= block.start && genomicLocation < block.start + block.len) {
          return block;
        }
      }

      return undefined;
    }

    function decodeTags(ba) {
      let p = 0;
      const len = ba.length;
      const tags = {};

      while (p < len) {
        const tag = String.fromCharCode(ba[p]) + String.fromCharCode(ba[p + 1]);
        p += 2;
        const type = String.fromCharCode(ba[p++]);
        let value;

        if (type === 'A') {
          value = String.fromCharCode(ba[p]);
          p++;
        } else if (type === 'i' || type === 'I') {
          value = readInt$1(ba, p);
          p += 4;
        } else if (type === 'c') {
          value = readInt8(ba, p);
          p++;
        } else if (type === 'C') {
          value = readUInt8(ba, p);
          p++;
        } else if (type === 's' || type === 'S') {
          value = readShort(ba, p);
          p += 2;
        } else if (type === 'f') {
          value = readFloat(ba, p);
          p += 4;
        } else if (type === 'Z') {
          value = '';

          for (;;) {
            var cc = ba[p++];

            if (cc === 0) {
              break;
            } else {
              value += String.fromCharCode(cc);
            }
          }
        } else if (type === 'B') {
          const elementType = String.fromCharCode(ba[p++]);
          let elementSize = ELEMENT_SIZE[elementType];

          if (elementSize === undefined) {
            tags[tag] = `Error: unknown element type '${elementType}'`;
            break;
          }

          const numElements = readInt$1(ba, p);
          p += 4 + numElements * elementSize;
          value = '[not shown]';
        } else {
          //'Unknown type ' + type;
          value = 'Error unknown type: ' + type;
          tags[tag] = value;
          break;
        }

        tags[tag] = value;
      }

      return tags;
    }

    function readInt$1(ba, offset) {
      return ba[offset + 3] << 24 | ba[offset + 2] << 16 | ba[offset + 1] << 8 | ba[offset];
    }

    function readShort(ba, offset) {
      return ba[offset + 1] << 8 | ba[offset];
    }

    function readFloat(ba, offset) {
      const dataView = new DataView(ba.buffer);
      return dataView.getFloat32(offset);
    }

    function readInt8(ba, offset) {
      const dataView = new DataView(ba.buffer);
      return dataView.getInt8(offset);
    }

    function readUInt8(ba, offset) {
      const dataView = new DataView(ba.buffer);
      return dataView.getUint8(offset);
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    /**
     * Created by jrobinso on 4/5/18.
     */
    class AlignmentBlock {
      constructor(b) {
        if (b) {
          Object.assign(this, b);
        }
      }

      seqIndexAt(genomicLocation) {
        return Math.floor(genomicLocation) - this.start + this.seqOffset;
      }

    }

    class BamFilter {
      constructor(options) {
        if (!options) options = {};
        this.vendorFailed = options.vendorFailed === undefined ? true : options.vendorFailed;
        this.duplicates = options.duplicates === undefined ? true : options.duplicates;
        this.secondary = options.secondary || false;
        this.supplementary = options.supplementary || false;
        this.mqThreshold = options.mqThreshold === undefined ? 0 : options.mqThreshold;

        if (options.readgroups) {
          this.readgroups = new Set(options.readgroups);
        }
      }

      pass(alignment) {
        if (this.vendorFailed && alignment.isFailsVendorQualityCheck()) return false;
        if (this.duplicates && alignment.isDuplicate()) return false;
        if (this.secondary && alignment.isSecondary()) return false;
        if (this.supplementary && alignment.isSupplementary()) return false;
        if (alignment.mq < this.mqThreshold) return false;

        if (this.readgroups) {
          var rg = alignment.tags()['RG'];
          return this.readgroups.has(rg);
        }

        return true;
      }

    }

    /**
     * This code is based on the Biodalliance BAM reader by Thomas Down,  2011
     *
     * https://github.com/dasmoth/dalliance/blob/master/js/bam.js
     */

    const SEQ_DECODER = ['=', 'A', 'C', 'x', 'G', 'x', 'x', 'x', 'T', 'x', 'x', 'x', 'x', 'x', 'x', 'N'];
    const CIGAR_DECODER = ['M', 'I', 'D', 'N', 'S', 'H', 'P', '=', 'X', '?', '?', '?', '?', '?', '?', '?'];
    const READ_STRAND_FLAG$1 = 0x10;
    const MATE_STRAND_FLAG$1 = 0x20;
    const BAM1_MAGIC_BYTES = new Uint8Array([0x42, 0x41, 0x4d, 0x01]); // BAM\1

    const BAM1_MAGIC_NUMBER = readInt(BAM1_MAGIC_BYTES, 0);
    const DEFAULT_ALLELE_FREQ_THRESHOLD = 0.2;
    const DEFAULT_SAMPLING_WINDOW_SIZE = 100;
    const DEFAULT_SAMPLING_DEPTH = 500;
    const MAXIMUM_SAMPLING_DEPTH = 10000;
    const BamUtils = {
      readHeader: function (url, options, genome) {
        return igvxhr.loadArrayBuffer(url, options).then(function (compressedBuffer) {
          var header, unc, uncba;
          unc = unbgzf(compressedBuffer);
          uncba = unc;
          header = BamUtils.decodeBamHeader(uncba, genome);
          return header;
        });
      },

      /**
       *
       * @param ba  bytes to decode as a UInt8Array
       * @param genome  optional igv genome object
       * @returns {{ magicNumer: number, size: number, chrNames: Array, chrToIndex: ({}|*), chrAliasTable: ({}|*) }}
       */
      decodeBamHeader: function (ba, genome) {
        var magic, samHeaderLen, samHeader, chrToIndex, chrNames, chrAliasTable, alias;
        magic = readInt(ba, 0);

        if (magic !== BAM1_MAGIC_NUMBER) {
          throw new Error('BAM header errror: bad magic number.  This could be caused by either a corrupt or missing file.');
        }

        samHeaderLen = readInt(ba, 4);
        samHeader = '';

        for (var i = 0; i < samHeaderLen; ++i) {
          samHeader += String.fromCharCode(ba[i + 8]);
        }

        var nRef = readInt(ba, samHeaderLen + 8);
        var p = samHeaderLen + 12;
        chrToIndex = {};
        chrNames = [];
        chrAliasTable = {};

        for (i = 0; i < nRef; ++i) {
          var lName = readInt(ba, p);
          var name = '';

          for (var j = 0; j < lName - 1; ++j) {
            name += String.fromCharCode(ba[p + 4 + j]);
          }

          readInt(ba, p + lName + 4); //dlog(name + ': ' + lRef);

          chrToIndex[name] = i;
          chrNames[i] = name;

          if (genome) {
            alias = genome.getChromosomeName(name);
            chrAliasTable[alias] = name;
          }

          p = p + 8 + lName;
        }

        return {
          magicNumber: magic,
          size: p,
          chrNames: chrNames,
          chrToIndex: chrToIndex,
          chrAliasTable: chrAliasTable
        };
      },
      bam_tag2cigar: function (ba, block_end, seq_offset, lseq, al, cigarArray) {
        function type2size(x) {
          if (x === 'C' || x === 'c' || x === 'A') return 1;else if (x === 'S' || x === 's') return 2;else if (x === 'I' || x === 'i' || x === 'f') return 4;else return 0;
        } // test if the real CIGAR is encoded in a CG:B,I tag


        if (cigarArray.length !== 1 || al.start < 0) return false;
        var p = seq_offset + (lseq + 1 >> 1) + lseq;

        while (p + 4 < block_end) {
          var tag = String.fromCharCode(ba[p]) + String.fromCharCode(ba[p + 1]);
          if (tag === 'CG') break;
          var type = String.fromCharCode(ba[p + 2]);

          if (type === 'B') {
            // the binary array type
            type = String.fromCharCode(ba[p + 3]);
            var size = type2size(type);
            var len = readInt(ba, p + 4);
            p += 8 + size * len;
          } else if (type === 'Z' || type === 'H') {
            // 0-terminated string
            p += 3;

            while (ba[p++] !== 0) {}
          } else {
            // other atomic types
            p += 3 + type2size(type);
          }
        }

        if (p >= block_end) return false; // no CG tag

        if (String.fromCharCode(ba[p + 2]) !== 'B' || String.fromCharCode(ba[p + 3]) !== 'I') return false; // not of type B,I
        // now we know the real CIGAR length and its offset in the binary array

        var cigar_len = readInt(ba, p + 4);
        var cigar_offset = p + 8; // 4 for "CGBI" and 4 for length

        if (cigar_offset + cigar_len * 4 > block_end) return false; // out of bound
        // decode CIGAR

        var cigar = '';
        var lengthOnRef = 0;
        cigarArray.length = 0; // empty the old array

        p = cigar_offset;

        for (var k = 0; k < cigar_len; ++k, p += 4) {
          var cigop = readInt(ba, p);
          var opLen = cigop >> 4;
          var opLtr = CIGAR_DECODER[cigop & 0xf];
          if (opLtr === 'M' || opLtr === 'EQ' || opLtr === 'X' || opLtr === 'D' || opLtr === 'N' || opLtr === '=') lengthOnRef += opLen;
          cigar = cigar + opLen + opLtr;
          cigarArray.push({
            len: opLen,
            ltr: opLtr
          });
        } // update alignment record. We are not updating bin, as apparently it is not used.


        al.cigar = cigar;
        al.lengthOnRef = lengthOnRef;
        return true;
      },

      /**
       *
       * @param ba                 bytes to decode as an UInt8Array
       * @param offset             offset position of ba array to start decoding
       * @param alignmentContainer container to receive the decoded alignments
       * @param min                minimum genomic position
       * @param max                maximum genomic position
       * @param chrIdx             chromosome index
       * @param chrNames            array of chromosome names
       * @param filter             a BamFilter object
       *
       * @return true if we have moved beyond the right end of the genomic range.
       */
      decodeBamRecords: function (ba, offset, alignmentContainer, chrNames, chrIdx, min, max, filter) {
        while (offset < ba.length) {
          const blockSize = readInt(ba, offset);
          const blockEnd = offset + blockSize + 4;
          const alignment = new BamAlignment();
          const refID = readInt(ba, offset + 4);
          const pos = readInt(ba, offset + 8);

          if (blockEnd > ba.length) {
            return;
          }

          if (refID < 0) {
            offset = blockEnd;
            continue; // unmapped read
          } else if (chrIdx !== undefined && (refID > chrIdx || pos > max)) {
            return true; // off right edge, we're done
          } else if (chrIdx !== undefined && refID < chrIdx) {
            offset = blockEnd;
            continue; // ref ID to left of start, not sure this is possible
          }

          const bin_mq_nl = readInt(ba, offset + 12);
          const mq = (bin_mq_nl & 0xff00) >> 8;
          const nl = bin_mq_nl & 0xff;
          const flag_nc = readInt(ba, offset + 16);
          const flag = (flag_nc & 0xffff0000) >> 16;
          const nc = flag_nc & 0xffff;
          const lseq = readInt(ba, offset + 20);
          const mateChrIdx = readInt(ba, offset + 24);
          const matePos = readInt(ba, offset + 28);
          const fragmentLength = readInt(ba, offset + 32);
          let readName = [];

          for (let j = 0; j < nl - 1; ++j) {
            readName.push(String.fromCharCode(ba[offset + 36 + j]));
          }

          readName = readName.join('');
          let lengthOnRef = 0;
          let cigar = '';
          let p = offset + 36 + nl;
          const cigarArray = []; // concatenate M,=,EQ,and X

          let lastCigRecord;

          for (let c = 0; c < nc; ++c) {
            var cigop = readInt(ba, p);
            var opLen = cigop >> 4;
            var opLtr = CIGAR_DECODER[cigop & 0xf];
            if (opLtr === 'M' || opLtr === 'EQ' || opLtr === 'X' || opLtr === 'D' || opLtr === 'N' || opLtr === '=') lengthOnRef += opLen;
            cigar = cigar + opLen + opLtr;
            p += 4; // if(mOperators.has(opLtr) && mOperators.has(lastCigRecord.ltr)) {
            //     lastCigRecord.len += opLen;
            //     lastCigRecord.ltr = 'M'
            // }
            // else {

            lastCigRecord = {
              len: opLen,
              ltr: opLtr
            };
            cigarArray.push(lastCigRecord); //}
          }

          alignment.chr = chrNames[refID];
          alignment.start = pos;
          alignment.flags = flag;
          alignment.strand = !(flag & READ_STRAND_FLAG$1);
          alignment.readName = readName;
          alignment.cigar = cigar;
          alignment.lengthOnRef = lengthOnRef;
          alignment.fragmentLength = fragmentLength;
          alignment.mq = mq;
          BamUtils.bam_tag2cigar(ba, blockEnd, p, lseq, alignment, cigarArray);
          alignment.end = alignment.start + alignment.lengthOnRef;

          if (alignment.end < min) {
            offset = blockEnd;
            continue;
          } // Record out-of-range "to the left", skip to next one


          let seq = [];
          const seqBytes = lseq + 1 >> 1;

          for (let j = 0; j < seqBytes; ++j) {
            var sb = ba[p + j];
            seq.push(SEQ_DECODER[(sb & 0xf0) >> 4]);
            seq.push(SEQ_DECODER[sb & 0x0f]);
          }

          seq = seq.slice(0, lseq).join(''); // seq might have one extra character (if lseq is an odd number)

          p += seqBytes;
          const qualArray = [];

          for (let j = 0; j < lseq; ++j) {
            qualArray.push(ba[p + j]);
          }

          p += lseq;

          if (mateChrIdx >= 0) {
            alignment.mate = {
              chr: chrNames[mateChrIdx],
              position: matePos,
              strand: !(flag & MATE_STRAND_FLAG$1)
            };
          }

          alignment.seq = seq;
          alignment.qual = qualArray;
          alignment.tagBA = new Uint8Array(ba.buffer.slice(p, blockEnd)); // decode these on demand

          this.setPairOrientation(alignment);

          if (undefined === filter || filter.pass(alignment)) {
            makeBlocks(alignment, cigarArray);
            alignmentContainer.push(alignment);
          }

          offset = blockEnd;
        }
      },
      decodeSamRecords: function (sam, alignmentContainer, chr, min, max, filter) {
        var lines, i, j, len, tokens, qualString, rnext, lengthOnRef, alignment, cigarArray, started;
        lines = splitLines$5(sam);
        len = lines.length;
        started = false;

        for (i = 0; i < len; i++) {
          tokens = lines[i].split('\t');
          alignment = new BamAlignment();
          alignment.chr = tokens[2];
          alignment.start = Number.parseInt(tokens[3]) - 1;
          alignment.flags = Number.parseInt(tokens[1]);
          alignment.readName = tokens[0];
          alignment.strand = !(alignment.flags & READ_STRAND_FLAG$1);
          alignment.mq = Number.parseInt(tokens[4]);
          alignment.cigar = tokens[5];
          alignment.fragmentLength = Number.parseInt(tokens[8]);
          alignment.seq = tokens[9];
          if (alignment.chr === '*' || !alignment.isMapped()) continue; // Unmapped

          if (alignment.chr !== chr) {
            if (started) break; // Off the right edge, we're done
            else continue; // Possibly to the left, skip but keep looping
          } else if (alignment.start > max) {
            break; // off right edge, we're done
          }

          lengthOnRef = 0;
          cigarArray = buildOperators(alignment.cigar);
          cigarArray.forEach(function (op) {
            var opLen = op.len;
            var opLtr = op.ltr;
            if (opLtr === 'M' || opLtr === 'EQ' || opLtr === 'X' || opLtr === 'D' || opLtr === 'N' || opLtr === '=') lengthOnRef += opLen;
          });
          alignment.lengthOnRef = lengthOnRef; // TODO for lh3: parse the CG:B,I tag in SAM here

          if (alignment.start + lengthOnRef < min) {
            continue; // To the left, skip and continue
          }

          qualString = tokens[10];
          alignment.qual = [];

          for (j = 0; j < qualString.length; j++) {
            alignment.qual[j] = qualString.charCodeAt(j) - 33;
          }

          alignment.tagDict = tokens.length < 11 ? {} : decodeSamTags(tokens.slice(11));

          if (alignment.isMateMapped()) {
            rnext = tokens[6];
            alignment.mate = {
              chr: rnext === '=' ? alignment.chr : rnext,
              position: Number.parseInt(tokens[7]),
              strand: !(alignment.flags & MATE_STRAND_FLAG$1)
            };
          }

          this.setPairOrientation(alignment);

          if (undefined === filter || filter.pass(alignment)) {
            makeBlocks(alignment, cigarArray);
            alignmentContainer.push(alignment);
          }
        }
      },
      setReaderDefaults: function (reader, config) {
        reader.filter = new BamFilter(config.filter);

        if (config.readgroup) {
          reader.filter.readgroups = new Set([config.readgroup]);
        }

        reader.alleleFreqThreshold = config.alleleFreqThreshold === undefined ? DEFAULT_ALLELE_FREQ_THRESHOLD : config.alleleFreqThreshold;
        reader.samplingWindowSize = config.samplingWindowSize === undefined ? DEFAULT_SAMPLING_WINDOW_SIZE : config.samplingWindowSize;
        reader.samplingDepth = config.samplingDepth === undefined ? DEFAULT_SAMPLING_DEPTH : config.samplingDepth;

        if (reader.samplingDepth > MAXIMUM_SAMPLING_DEPTH) {
          console.log("Warning: attempt to set sampling depth > maximum value of " + MAXIMUM_SAMPLING_DEPTH);
          reader.samplingDepth = MAXIMUM_SAMPLING_DEPTH;
        }

        if (config.viewAsPairs) {
          reader.pairsSupported = true;
        } else {
          reader.pairsSupported = config.pairsSupported === undefined ? true : config.pairsSupported;
        }
      },
      setPairOrientation: function (alignment) {
        if (alignment.isMapped() && alignment.mate && alignment.isMateMapped() && alignment.mate.chr === alignment.chr) {
          var s1 = alignment.strand ? 'F' : 'R';
          var mate = alignment.mate;
          var s2 = mate.strand ? 'F' : 'R';
          var o1 = ' ';
          var o2 = ' ';

          if (alignment.isFirstOfPair()) {
            o1 = '1';
            o2 = '2';
          } else if (alignment.isSecondOfPair()) {
            o1 = '2';
            o2 = '1';
          }

          var tmp = [];
          var isize = alignment.fragmentLength;
          var estReadLen = alignment.end - alignment.start;

          if (isize === 0) {
            //isize not recorded.  Need to estimate.  This calculation was validated against an Illumina
            // -> <- library bam.
            var estMateEnd = alignment.start < mate.position ? mate.position + estReadLen : mate.position - estReadLen;
            isize = estMateEnd - alignment.start;
          } //if (isize > estReadLen) {


          if (isize > 0) {
            tmp[0] = s1;
            tmp[1] = o1;
            tmp[2] = s2;
            tmp[3] = o2;
          } else {
            tmp[2] = s1;
            tmp[3] = o1;
            tmp[0] = s2;
            tmp[1] = o2;
          } // }


          alignment.pairOrientation = tmp.join('');
        }
      },
      computeLengthOnReference: function (cigarString) {
        let len = 0;
        let buf = '';

        for (let i = 0; i < cigarString.length; i++) {
          const c = cigarString.charCodeAt(i);

          if (c > 47 && c < 58) {
            buf += cigarString.charAt(i);
          } else {
            switch (c) {
              case 78: // N

              case 68: // D

              case 77: // M

              case 61: // =

              case 88:
                // X
                len += parseInt(buf.toString());
            }

            buf = '';
          }
        }

        return len;
      }
    };
    /**
     * Split the alignment record into blocks as specified in the cigarArray.  Each aligned block contains
     * its portion of the read sequence and base quality strings.  A read sequence or base quality string
     * of "*" indicates the value is not recorded.  In all other cases the length of the block sequence (block.seq)
     * and quality string (block.qual) must == the block length.
     *
     * @param alignment
     * @param cigarArray
     * @returns array of blocks
     */

    function makeBlocks(alignment, cigarArray) {
      const blocks = [];
      let insertions;
      let gaps;
      let seqOffset = 0;
      let pos = alignment.start;
      alignment.scStart = alignment.start;
      alignment.scLengthOnRef = alignment.lengthOnRef;

      for (let c of cigarArray) {
        let scPos;

        switch (c.ltr) {
          case 'H':
            break;
          // ignore hard clips

          case 'P':
            break;
          // ignore pads

          case 'S':
            scPos = pos;
            alignment.scLengthOnRef += c.len;

            if (blocks.length === 0) {
              alignment.scStart -= c.len;
              scPos -= c.len;
            }

            blocks.push(new AlignmentBlock({
              start: scPos,
              seqOffset: seqOffset,
              len: c.len,
              type: 'S'
            }));
            seqOffset += c.len;
            break;
          // soft clip read bases

          case 'N':
          case 'D':
            if (gaps === undefined) {
              gaps = [];
            }

            gaps.push({
              start: pos,
              len: c.len,
              type: c.ltr
            });
            pos += c.len;
            break;

          case 'I':
            if (insertions === undefined) {
              insertions = [];
            }

            insertions.push(new AlignmentBlock({
              start: pos,
              len: c.len,
              seqOffset: seqOffset,
              type: 'I'
            }));
            seqOffset += c.len;
            break;

          case 'M':
          case 'EQ':
          case '=':
          case 'X':
            blocks.push(new AlignmentBlock({
              start: pos,
              seqOffset: seqOffset,
              len: c.len,
              type: 'M'
            }));
            seqOffset += c.len;
            pos += c.len;
            break;

          default:
            console.log('Error processing cigar element: ' + c.len + c.ltr);
        }
      }

      alignment.blocks = blocks;
      alignment.insertions = insertions;
      alignment.gaps = gaps;
    }

    function readInt(ba, offset) {
      return ba[offset + 3] << 24 | ba[offset + 2] << 16 | ba[offset + 1] << 8 | ba[offset];
    }
    /**
     * Build a list of cigar operators from a cigarString.  Removes padding operators and concatenates consecutive
     * operators of the same type
     *
     * @param cigarString
     * @return
     */


    function buildOperators(cigarString) {
      var operators, buffer, i, len, prevOp, next, op, nBases;
      operators = [];
      buffer = []; // Create list of cigar operators

      prevOp = null;
      len = cigarString.length;

      for (i = 0; i < len; i++) {
        next = cigarString.charAt(i);

        if (isDigit(next)) {
          buffer.push(next);
        } else {
          op = next;
          nBases = Number.parseInt(buffer.join(''));
          buffer = [];

          if (prevOp !== null && prevOp.ltr === op) {
            prevOp.len += nBases;
          } else {
            prevOp = {
              len: nBases,
              ltr: op
            };
            operators.push(prevOp);
          }
        }
      }

      return operators;
    }

    function isDigit(a) {
      var charCode = a.charCodeAt(0);
      return charCode >= 48 && charCode <= 57; // 0-9
    }

    function decodeSamTags(tags) {
      var tagDict = {};
      tags.forEach(function (tag) {
        var tokens = tag.split(':');
        tagDict[tokens[0]] = tokens[2];
      });
      return tagDict;
    }

    /**
     * Class for reading a bam file
     *
     * @param config
     * @constructor
     */

    class BamReaderNonIndexed {
      constructor(config, genome) {
        this.config = config;
        this.genome = genome;
        this.bamPath = config.url;
        this.isDataUri = isDataURL(config.url);
        BamUtils.setReaderDefaults(this, config);
      } // Return an alignment container


      async readAlignments(chr, bpStart, bpEnd) {
        if (this.alignmentCache) {
          const header = this.header;
          const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
          const qAlignments = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
          const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);

          for (let a of qAlignments) {
            alignmentContainer.push(a);
          }

          alignmentContainer.finish();
          return alignmentContainer;
        } else {
          if (this.isDataUri) {
            const data = decodeDataURI(this.bamPath);
            const unc = unbgzf(data.buffer);
            this.parseAlignments(unc);
            return this.fetchAlignments(chr, bpStart, bpEnd);
          } else {
            const arrayBuffer = await igvxhr.loadArrayBuffer(this.bamPath, buildOptions(this.config));
            const unc = unbgzf(arrayBuffer);
            this.parseAlignments(unc);
            return this.fetchAlignments(chr, bpStart, bpEnd);
          }
        }
      }

      parseAlignments(data) {
        const alignments = [];
        this.header = BamUtils.decodeBamHeader(data);
        BamUtils.decodeBamRecords(data, this.header.size, alignments, this.header.chrNames);
        this.alignmentCache = new FeatureCache$1(alignments, this.genome);
      }

      fetchAlignments(chr, bpStart, bpEnd) {
        const queryChr = this.header.chrAliasTable.hasOwnProperty(chr) ? this.header.chrAliasTable[chr] : chr;
        const features = this.alignmentCache.queryFeatures(queryChr, bpStart, bpEnd);
        const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);

        for (let feature of features) {
          alignmentContainer.push(feature);
        }

        alignmentContainer.finish();
        return alignmentContainer;
      }

    }

    function decodeDataURI(dataURI) {
      const split = dataURI.split(',');
      const info = split[0].split(':')[1];
      let dataString = split[1];

      if (info.indexOf('base64') >= 0) {
        dataString = atob(dataString);
      } else {
        dataString = decodeURI(dataString);
      }

      const bytes = new Uint8Array(dataString.length);

      for (var i = 0; i < dataString.length; i++) {
        bytes[i] = dataString.charCodeAt(i);
      }

      return bytes;
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    // TODO -- big endian?
    class BinaryParser {
      constructor(dataView, littleEndian) {
        this.littleEndian = littleEndian !== undefined ? littleEndian : true;
        this.position = 0;
        this.view = dataView;
        this.length = dataView.byteLength;
      }

      available() {
        return this.length - this.position;
      }

      remLength() {
        return this.length - this.position;
      }

      hasNext() {
        return this.position < this.length - 1;
      }

      getByte() {
        var retValue = this.view.getUint8(this.position, this.littleEndian);
        this.position++;
        return retValue;
      }

      getShort() {
        var retValue = this.view.getInt16(this.position, this.littleEndian);
        this.position += 2;
        return retValue;
      }

      getUShort() {
        // var byte1 = this.getByte(),
        //     byte2 = this.getByte(),
        //     retValue = ((byte2 << 24 >>> 16) + (byte1 << 24 >>> 24));
        //     return retValue;
        //
        var retValue = this.view.getUint16(this.position, this.littleEndian);
        this.position += 2;
        return retValue;
      }

      getInt() {
        var retValue = this.view.getInt32(this.position, this.littleEndian);
        this.position += 4;
        return retValue;
      }

      getUInt() {
        var retValue = this.view.getUint32(this.position, this.littleEndian);
        this.position += 4;
        return retValue;
      }

      getLong() {
        // DataView doesn't support long. So we'll try manually
        var b = [];
        b[0] = this.view.getUint8(this.position);
        b[1] = this.view.getUint8(this.position + 1);
        b[2] = this.view.getUint8(this.position + 2);
        b[3] = this.view.getUint8(this.position + 3);
        b[4] = this.view.getUint8(this.position + 4);
        b[5] = this.view.getUint8(this.position + 5);
        b[6] = this.view.getUint8(this.position + 6);
        b[7] = this.view.getUint8(this.position + 7);
        var value = 0;

        if (this.littleEndian) {
          for (let i = b.length - 1; i >= 0; i--) {
            value = value * 256 + b[i];
          }
        } else {
          for (let i = 0; i < b.length; i++) {
            value = value * 256 + b[i];
          }
        }

        this.position += 8;
        return value;
      }

      getString(len) {
        var s = "";
        var c;

        while ((c = this.view.getUint8(this.position++)) !== 0) {
          s += String.fromCharCode(c);
          if (len && s.length === len) break;
        }

        return s;
      }

      getFixedLengthString(len) {
        var s = "";
        var i;
        var c;

        for (i = 0; i < len; i++) {
          c = this.view.getUint8(this.position++);

          if (c > 0) {
            s += String.fromCharCode(c);
          }
        }

        return s;
      }

      getFixedLengthTrimmedString(len) {
        var s = "";
        var i;
        var c;

        for (i = 0; i < len; i++) {
          c = this.view.getUint8(this.position++);

          if (c > 32) {
            s += String.fromCharCode(c);
          }
        }

        return s;
      }

      getFloat() {
        var retValue = this.view.getFloat32(this.position, this.littleEndian);
        this.position += 4;
        return retValue;
      }

      getDouble() {
        var retValue = this.view.getFloat64(this.position, this.littleEndian);
        this.position += 8;
        return retValue;
      }

      skip(n) {
        this.position += n;
        return this.position;
      }
      /**
       * Return a BGZip (bam and tabix) virtual pointer
       * TODO -- why isn't 8th byte used ?
       * @returns {*}
       */


      getVPointer() {
        var position = this.position,
            offset = this.view.getUint8(position + 1) << 8 | this.view.getUint8(position),
            byte6 = (this.view.getUint8(position + 6) & 0xff) * 0x100000000,
            byte5 = (this.view.getUint8(position + 5) & 0xff) * 0x1000000,
            byte4 = (this.view.getUint8(position + 4) & 0xff) * 0x10000,
            byte3 = (this.view.getUint8(position + 3) & 0xff) * 0x100,
            byte2 = this.view.getUint8(position + 2) & 0xff,
            block = byte6 + byte5 + byte4 + byte3 + byte2;
        this.position += 8; //       if (block == 0 && offset == 0) {
        //           return null;
        //       } else {

        return new VPointer(block, offset); //       }
      }

    }

    class VPointer {
      constructor(block, offset) {
        this.block = block;
        this.offset = offset;
      }

      isLessThan(vp) {
        return this.block < vp.block || this.block === vp.block && this.offset < vp.offset;
      }

      isGreaterThan(vp) {
        return this.block > vp.block || this.block === vp.block && this.offset > vp.offset;
      }

      print() {
        return "" + this.block + ":" + this.offset;
      }

    }

    // Represents a BAM index.
    const CSI1_MAGIC$1 = 21582659; // CSI\1

    const CSI2_MAGIC$1 = 38359875; // CSI\2

    async function parseCsiIndex(arrayBuffer, genome) {
      const idx = new CSIIndex();
      idx.parse(arrayBuffer, genome);
      return idx;
    }

    class CSIIndex {
      constructor(tabix) {
        this.tabix = true; // Means whatever is indexed is BGZipped
      }

      parse(arrayBuffer, genome) {
        const parser = new BinaryParser(new DataView(arrayBuffer));
        const magic = parser.getInt();

        if (magic !== CSI1_MAGIC$1) {
          if (magic === CSI2_MAGIC$1) {
            throw Error("CSI version 2 is not supported.  Please enter an issue at https://github.com/igvteam/igv.js");
          } else {
            throw Error("Not a CSI index");
          }
        }

        this.indices = [];
        this.blockMin = Number.MAX_SAFE_INTEGER;
        this.lastBlockPosition = [];
        this.sequenceIndexMap = {};
        this.minShift = parser.getInt();
        this.depth = parser.getInt();
        const lAux = parser.getInt();
        const seqNames = [];
        let bmax = 0;

        if (lAux >= 28) {
          // Tabix header parameters aren't used, but they must be read to advance the pointer
          parser.getInt();
          parser.getInt();
          parser.getInt();
          parser.getInt();
          parser.getInt();
          parser.getInt();
          const l_nm = parser.getInt();
          const nameEndPos = parser.position + l_nm;
          let i = 0;

          while (parser.position < nameEndPos) {
            let seq_name = parser.getString(); // Translate to "official" chr name.

            if (genome) {
              seq_name = genome.getChromosomeName(seq_name);
            }

            this.sequenceIndexMap[seq_name] = i;
            seqNames[i] = seq_name;
            i++;
          }
        }

        const MAX_BIN = this.bin_limit() + 1;
        const nref = parser.getInt();

        for (let ref = 0; ref < nref; ref++) {
          const binIndex = [];
          const loffset = [];
          const nbin = parser.getInt();

          for (let b = 0; b < nbin; b++) {
            const binNumber = parser.getInt();
            loffset[binNumber] = parser.getVPointer();

            if (binNumber > MAX_BIN) {
              // This is a psuedo bin, not used but we have to consume the bytes
              parser.getInt(); // # of chunks for this bin

              parser.getVPointer(); // unmapped beg

              parser.getVPointer(); // unmapped end

              parser.getLong();
              parser.getLong();
            } else {
              binIndex[binNumber] = [];
              const nchnk = parser.getInt(); // # of chunks for this bin

              for (let i = 0; i < nchnk; i++) {
                const cs = parser.getVPointer(); //chunk_beg

                const ce = parser.getVPointer(); //chunk_end

                if (cs && ce) {
                  if (cs.block < this.blockMin) {
                    this.blockMin = cs.block; // Block containing first alignment
                  }

                  if (ce.block > bmax) {
                    bmax = ce.block;
                  }

                  binIndex[binNumber].push([cs, ce]);
                }
              }
            }
          }

          if (nbin > 0) {
            this.indices[ref] = {
              binIndex: binIndex,
              loffset: loffset
            };
          }
        }

        this.lastBlockPosition = bmax;
      }

      get chromosomeNames() {
        return Object.keys(this.sequenceIndexMap);
      }
      /**
       * Fetch blocks for a particular genomic range.  This method is public so it can be unit-tested.
       *
       * @param refId  the sequence dictionary index of the chromosome
       * @param min  genomic start position
       * @param max  genomic end position
       * @param return an array of {minv: {filePointer, offset}, {maxv: {filePointer, offset}}
       */


      blocksForRange(refId, min, max) {
        const ba = this.indices[refId];

        if (!ba) {
          return [];
        } else {
          const overlappingBins = this.reg2bins(min, max); // List of bin #s that overlap min, max

          if (overlappingBins.length == 0) return [];
          const chunks = []; // Find chunks in overlapping bins.  Leaf bins (< 4681) are not pruned

          for (let binRange of overlappingBins) {
            for (let bin = binRange[0]; bin <= binRange[1]; bin++) {
              if (ba.binIndex[bin]) {
                const binChunks = ba.binIndex[bin];
                const nchnk = binChunks.length;

                for (let c = 0; c < nchnk; ++c) {
                  const cs = binChunks[c][0];
                  const ce = binChunks[c][1];
                  chunks.push({
                    minv: cs,
                    maxv: ce,
                    bin: bin
                  });
                }
              }
            }
          }

          const lowestOffset = ba.loffset[overlappingBins[0]];
          return optimizeChunks$1(chunks, lowestOffset);
        }
      } // reg2bins implementation adapted from GMOD/tabix-js  https://github.com/GMOD/tabix-js/blob/master/src/csi.ts


      reg2bins(beg, end) {
        beg -= 1; // < convert to 1-based closed

        if (beg < 1) beg = 1;
        if (end > 2 ** 50) end = 2 ** 34; // 17 GiB ought to be enough for anybody

        end -= 1;
        let l = 0;
        let t = 0;
        let s = this.minShift + this.depth * 3;
        const bins = [];

        for (; l <= this.depth; s -= 3, t += 1 << l * 3, l += 1) {
          const b = t + (beg >> s);
          const e = t + (end >> s);
          if (e - b + bins.length > this.maxBinNumber) throw new Error(`query ${beg}-${end} is too large for current binning scheme (shift ${this.minShift}, depth ${this.depth}), try a smaller query or a coarser index binning scheme`); //for (let i = b; i <= e; i += 1) bins.push(i)

          bins.push([b, e]);
        }

        return bins;
      } // function reg2bins(beg, end, min_shift, depth) {
      //     let l, t, n, s = min_shift + depth * 3;
      //     const bins = [];
      //     for (--end, l = n = t = 0; l <= depth; s -= 3, t += 1 << l * 3, ++l) {
      //         let b = t + (beg >> s), e = t + (end >> s), i;
      //         for (i = b; i <= e; ++i) bins[n++] = i;
      //     }
      //     return bins;
      // }


      bin_limit() {
        return ((1 << (this.depth + 1) * 3) - 1) / 7;
      }

    }

    function optimizeChunks$1(chunks, lowest) {
      const mergedChunks = [];
      let lastChunk = null;
      if (chunks.length === 0) return chunks;
      chunks.sort(function (c0, c1) {
        const dif = c0.minv.block - c1.minv.block;

        if (dif !== 0) {
          return dif;
        } else {
          return c0.minv.offset - c1.minv.offset;
        }
      });
      chunks.forEach(function (chunk) {
        if (!lowest || chunk.maxv.isGreaterThan(lowest)) {
          if (lastChunk === null) {
            mergedChunks.push(chunk);
            lastChunk = chunk;
          } else {
            if (canMerge$1(lastChunk, chunk)) {
              if (chunk.maxv.isGreaterThan(lastChunk.maxv)) {
                lastChunk.maxv = chunk.maxv;
              }
            } else {
              mergedChunks.push(chunk);
              lastChunk = chunk;
            }
          }
        }
      });
      return mergedChunks;
    }

    function canMerge$1(chunk1, chunk2) {
      return chunk2.minv.block - chunk1.maxv.block < 65000 && chunk2.maxv.block - chunk1.minv.block < 5000000; // lastChunk.minv.block === lastChunk.maxv.block &&
      // lastChunk.maxv.block === chunk.minv.block &&
      // chunk.minv.block === chunk.maxv.block
    }

    // Represents a BAM index.
    const BAI_MAGIC$1 = 21578050;
    const TABIX_MAGIC$1 = 21578324;
    const MB = 1000000;

    async function parseBamIndex(arrayBuffer, genome) {
      const index = new BamIndex();
      await index.parse(arrayBuffer, false, genome);
      return index;
    }

    async function parseTabixIndex(arrayBuffer, genome) {
      const index = new BamIndex();
      await index.parse(arrayBuffer, true, genome);
      return index;
    }

    class BamIndex {
      constructor() {}

      async parse(arrayBuffer, tabix, genome) {
        const indices = [];
        let blockMin = Number.MAX_SAFE_INTEGER;
        let blockMax = 0;
        const seqNames = [];
        const parser = new BinaryParser(new DataView(arrayBuffer));
        const magic = parser.getInt();
        const sequenceIndexMap = {};

        if (magic === BAI_MAGIC$1 || tabix && magic === TABIX_MAGIC$1) {
          const nref = parser.getInt();

          if (tabix) {
            // Tabix header parameters aren't used, but they must be read to advance the pointer
            parser.getInt();
            parser.getInt();
            parser.getInt();
            parser.getInt();
            parser.getInt();
            parser.getInt();
            parser.getInt();

            for (let i = 0; i < nref; i++) {
              let seq_name = parser.getString(); // Translate to "official" chr name.

              if (genome) {
                seq_name = genome.getChromosomeName(seq_name);
              }

              sequenceIndexMap[seq_name] = i;
              seqNames[i] = seq_name;
            }
          } // Loop through sequences


          for (let ref = 0; ref < nref; ref++) {
            const binIndex = {};
            const linearIndex = [];
            const nbin = parser.getInt();

            for (let b = 0; b < nbin; b++) {
              const binNumber = parser.getInt();

              if (binNumber === 37450) {
                // This is a psuedo bin, not used but we have to consume the bytes
                parser.getInt(); // # of chunks for this bin

                parser.getVPointer(); // unmapped beg

                parser.getVPointer(); // unmapped end

                parser.getLong();
                parser.getLong();
              } else {
                binIndex[binNumber] = [];
                const nchnk = parser.getInt(); // # of chunks for this bin

                for (let i = 0; i < nchnk; i++) {
                  const cs = parser.getVPointer(); //chunk_beg

                  const ce = parser.getVPointer(); //chunk_end

                  if (cs && ce) {
                    if (cs.block < blockMin) {
                      blockMin = cs.block; // Block containing first alignment
                    }

                    if (ce.block > blockMax) {
                      blockMax = ce.block;
                    }

                    binIndex[binNumber].push([cs, ce]);
                  }
                }
              }
            }

            const nintv = parser.getInt();

            for (let i = 0; i < nintv; i++) {
              const cs = parser.getVPointer();
              linearIndex.push(cs); // Might be null
            }

            if (nbin > 0) {
              indices[ref] = {
                binIndex: binIndex,
                linearIndex: linearIndex
              };
            }
          }

          this.firstBlockPosition = blockMin;
          this.lastBlockPosition = blockMax;
          this.indices = indices;
          this.sequenceIndexMap = sequenceIndexMap;
          this.tabix = tabix;
        } else {
          throw new Error(indexURL + " is not a " + (tabix ? "tabix" : "bai") + " file");
        }
      }

      get chromosomeNames() {
        return Object.keys(this.sequenceIndexMap);
      }
      /**
       * Fetch blocks for a particular genomic range.  This method is public so it can be unit-tested.
       *
       * @param refId  the sequence dictionary index of the chromosome
       * @param min  genomic start position
       * @param max  genomic end position
       * @param return an array of {minv: {filePointer, offset}, {maxv: {filePointer, offset}}
       */


      blocksForRange(refId, min, max) {
        const bam = this;
        const ba = bam.indices[refId];

        if (!ba) {
          return [];
        } else {
          const overlappingBins = reg2bins(min, max); // List of bin #s that overlap min, max

          const chunks = []; // Find chunks in overlapping bins.  Leaf bins (< 4681) are not pruned

          for (let binRange of overlappingBins) {
            for (let bin = binRange[0]; bin <= binRange[1]; bin++) {
              if (ba.binIndex[bin]) {
                const binChunks = ba.binIndex[bin],
                      nchnk = binChunks.length;

                for (let c = 0; c < nchnk; ++c) {
                  const cs = binChunks[c][0];
                  const ce = binChunks[c][1];
                  chunks.push({
                    minv: cs,
                    maxv: ce,
                    bin: bin
                  });
                }
              }
            }
          } // Use the linear index to find minimum file position of chunks that could contain alignments in the region


          const nintv = ba.linearIndex.length;
          let lowest = null;
          const minLin = Math.min(min >> 14, nintv - 1);
          const maxLin = Math.min(max >> 14, nintv - 1);

          for (let i = minLin; i <= maxLin; ++i) {
            const vp = ba.linearIndex[i];

            if (vp) {
              // todo -- I think, but am not sure, that the values in the linear index have to be in increasing order.  So the first non-null should be minimum
              if (!lowest || vp.isLessThan(lowest)) {
                lowest = vp;
              }
            }
          }

          return optimizeChunks(chunks, lowest);
        }
      }

    }

    function optimizeChunks(chunks, lowest) {
      const mergedChunks = [];
      let lastChunk = null;
      if (chunks.length === 0) return chunks;
      chunks.sort(function (c0, c1) {
        const dif = c0.minv.block - c1.minv.block;

        if (dif !== 0) {
          return dif;
        } else {
          return c0.minv.offset - c1.minv.offset;
        }
      });
      chunks.forEach(function (chunk) {
        if (!lowest || chunk.maxv.isGreaterThan(lowest)) {
          if (lastChunk === null) {
            mergedChunks.push(chunk);
            lastChunk = chunk;
          } else {
            if (canMerge(lastChunk, chunk)) {
              if (chunk.maxv.isGreaterThan(lastChunk.maxv)) {
                lastChunk.maxv = chunk.maxv;
              }
            } else {
              mergedChunks.push(chunk);
              lastChunk = chunk;
            }
          }
        }
      });
      return mergedChunks;
    }
    /**
     * Merge 2 blocks if the gap between them is < 1kb and the total resulting size < 100mb
     * @param chunk1
     * @param chunk2
     * @returns {boolean|boolean}
     */


    function canMerge(chunk1, chunk2) {
      const gap = chunk2.minv.block - chunk1.maxv.block;
      const total = chunk2.maxv.block - chunk1.minv.block;
      return gap < 30000 && total < 10 * MB;
    }
    /**
     * Calculate the list of bins that overlap with region [beg, end]
     *
     */


    function reg2bins(beg, end) {
      const list = [];
      if (end >= 1 << 29) end = 1 << 29;
      --end;
      list.push([0, 0]);
      list.push([1 + (beg >> 26), 1 + (end >> 26)]);
      list.push([9 + (beg >> 23), 9 + (end >> 23)]);
      list.push([73 + (beg >> 20), 73 + (end >> 20)]);
      list.push([585 + (beg >> 17), 585 + (end >> 17)]);
      list.push([4681 + (beg >> 14), 4681 + (end >> 14)]); // for (k = 1 + (beg >> 26); k <= 1 + (end >> 26); ++k) list.push(k);
      // for (k = 9 + (beg >> 23); k <= 9 + (end >> 23); ++k) list.push(k);
      // for (k = 73 + (beg >> 20); k <= 73 + (end >> 20); ++k) list.push(k);
      // for (k = 585 + (beg >> 17); k <= 585 + (end >> 17); ++k) list.push(k);
      // for (k = 4681 + (beg >> 14); k <= 4681 + (end >> 14); ++k) list.push(k);

      return list;
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    async function parseTribbleIndex(arrayBuffer, genome) {
      const index = new TribbleIndex();
      index.parse(arrayBuffer, genome);
      return index;
    }

    class TribbleIndex {
      constructor() {}

      async parse(arrayBuffer, genome) {
        let blockMax = 0;
        this.chrIndex = {};
        this.lastBlockPosition = [];
        const parser = new BinaryParser(new DataView(arrayBuffer));
        readHeader(parser);
        let nChrs = parser.getInt();

        while (nChrs-- > 0) {
          // todo -- support interval tree index, we're assuming its a linear index
          let chr = parser.getString();
          if (genome) chr = genome.getChromosomeName(chr); // Translate to canonical name

          const binWidth = parser.getInt();
          const nBins = parser.getInt();
          const longestFeature = parser.getInt();
          parser.getInt() > 0;
          parser.getInt(); // note the code below accounts for > 60% of the total time to read an index

          let pos = parser.getLong();
          const blocks = [];

          for (let binNumber = 0; binNumber < nBins; binNumber++) {
            const nextPos = parser.getLong();
            blocks.push({
              min: pos,
              max: nextPos
            }); //        {position: pos, size: size});

            pos = nextPos;

            if (nextPos > blockMax) {
              blockMax = nextPos;
            }
          }

          this.chrIndex[chr] = {
            chr: chr,
            blocks: blocks,
            longestFeature: longestFeature,
            binWidth: binWidth
          };
        }

        this.lastBlockPosition = blockMax;
        /**
         * Read the header .   Data here is not used in igv.js but we need to read it to advance the pointer.
         * @param parser
         */

        function readHeader(parser) {
          parser.getInt(); //   view._getInt32(offset += 32, true);

          parser.getInt();
          const version = parser.getInt();
          parser.getString();
          parser.getLong();
          parser.getLong();
          parser.getString();
          parser.getInt();

          if (version >= 3) {
            let nProperties = parser.getInt();

            while (nProperties-- > 0) {
              parser.getString();
              parser.getString();
            }
          }
        }
      }

      get chromosomeNames() {
        return Object.keys(this.chrIndex);
      }
      /**
       * Fetch blocks for a particular genomic range.
       *
       * @param queryChr the sequence dictionary index of the chromosome
       * @param min  genomic start position
       * @param max  genomic end position
       */


      blocksForRange(queryChr, min, max) {
        const chrIdx = this.chrIndex[queryChr];

        if (chrIdx) {
          const blocks = chrIdx.blocks;
          const longestFeature = chrIdx.longestFeature;
          const binWidth = chrIdx.binWidth;
          const adjustedPosition = Math.max(min - longestFeature, 0);
          const startBinNumber = Math.floor(adjustedPosition / binWidth);
          if (startBinNumber >= blocks.length) // are we off the end of the bin list, so return nothing
            return [];else {
            const endBinNumber = Math.min(Math.floor((max - 1) / binWidth), blocks.length - 1); // By definition blocks are adjacent in the file for the liner index.  Combine them into one merged block

            const startPos = blocks[startBinNumber].min;
            const endPos = blocks[endBinNumber].max;
            const size = endPos - startPos;

            if (size === 0) {
              return [];
            } else {
              const mergedBlock = {
                minv: {
                  block: startPos,
                  offset: 0
                },
                maxv: {
                  block: endPos,
                  offset: 0
                }
              };
              return [mergedBlock];
            }
          }
        } else {
          return undefined;
        }
      }

    }

    const CSI1_MAGIC = 21582659; // CSI\1

    const CSI2_MAGIC = 38359875; // CSI\2

    const BAI_MAGIC = 21578050;
    const TABIX_MAGIC = 21578324;
    const TRIBBLE_MAGIC = 1480870228; //  byte[]{'T', 'I', 'D', 'X'};

    /**
     * @param indexURL
     * @param config
     * @param tabix
     *
     */

    async function loadIndex(indexURL, config, genome) {
      let arrayBuffer = await igvxhr.loadArrayBuffer(indexURL, buildOptions(config));
      let dv = new DataView(arrayBuffer); // Some indexs are bgzipped, specifically tabix, and csi.  Bam (bai) are not.  Tribble is usually not.
      // Check first 2 bytes of file for gzip magic number, and inflate if neccessary

      if (dv.getUint8(0) === 0x1f && dv.getUint8(1) === 0x8b) {
        // gzipped
        const inflate = unbgzf(arrayBuffer);
        arrayBuffer = inflate.buffer;
        dv = new DataView(arrayBuffer);
      }

      const magic = dv.getInt32(0, true);

      switch (magic) {
        case BAI_MAGIC:
          return parseBamIndex(arrayBuffer, genome);

        case TABIX_MAGIC:
          return parseTabixIndex(arrayBuffer, genome);

        case CSI1_MAGIC:
          return parseCsiIndex(arrayBuffer, genome);

        case TRIBBLE_MAGIC:
          return parseTribbleIndex(arrayBuffer, genome);

        case CSI2_MAGIC:
          throw Error("CSI version 2 is not supported.");

        default:
          throw Error(`Unrecognized index type: ${indexURL}`);
      }
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    /**
     * Class for reading a bam file
     *
     * @param config
     * @constructor
     */

    class BamReader {
      constructor(config, genome) {
        this.config = config;
        this.genome = genome;
        this.bamPath = config.url;
        this.baiPath = config.indexURL;
        BamUtils.setReaderDefaults(this, config);
      }

      async readAlignments(chr, bpStart, bpEnd) {
        const chrToIndex = await this.getChrIndex();
        const queryChr = this.chrAliasTable.hasOwnProperty(chr) ? this.chrAliasTable[chr] : chr;
        const chrId = chrToIndex[queryChr];
        const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);

        if (chrId === undefined) {
          return alignmentContainer;
        } else {
          const bamIndex = await this.getIndex();
          const chunks = bamIndex.blocksForRange(chrId, bpStart, bpEnd);

          if (!chunks || chunks.length === 0) {
            return alignmentContainer;
          }

          for (let c of chunks) {
            let lastBlockSize;

            if (c.maxv.offset === 0) {
              lastBlockSize = 0; // Don't need to read the last block.
            } else {
              const bsizeOptions = buildOptions(this.config, {
                range: {
                  start: c.maxv.block,
                  size: 26
                }
              });
              const abuffer = await igvxhr.loadArrayBuffer(this.bamPath, bsizeOptions);
              lastBlockSize = bgzBlockSize(abuffer);
            }

            const fetchMin = c.minv.block;
            const fetchMax = c.maxv.block + lastBlockSize;
            const range = {
              start: fetchMin,
              size: fetchMax - fetchMin + 1
            };
            const compressed = await igvxhr.loadArrayBuffer(this.bamPath, buildOptions(this.config, {
              range: range
            }));
            var ba = unbgzf(compressed); //new Uint8Array(BGZip.unbgzf(compressed)); //, c.maxv.block - c.minv.block + 1));

            const done = BamUtils.decodeBamRecords(ba, c.minv.offset, alignmentContainer, this.indexToChr, chrId, bpStart, bpEnd, this.filter);

            if (done) {
              //    console.log(`Loaded ${counter} chunks out of  ${chunks.length}`);
              break;
            }
          }

          alignmentContainer.finish();
          return alignmentContainer;
        }
      }

      async getHeader() {
        if (!this.header) {
          const genome = this.genome;
          const index = await this.getIndex();
          let len;

          if (index.firstBlockPosition) {
            const bsizeOptions = buildOptions(this.config, {
              range: {
                start: index.firstBlockPosition,
                size: 26
              }
            });
            const abuffer = await igvxhr.loadArrayBuffer(this.bamPath, bsizeOptions);
            const bsize = bgzBlockSize(abuffer);
            len = index.firstBlockPosition + bsize; // Insure we get the complete compressed block containing the header
          } else {
            len = 64000;
          }

          const options = buildOptions(this.config, {
            range: {
              start: 0,
              size: len
            }
          });
          this.header = await BamUtils.readHeader(this.bamPath, options, genome);
        }

        return this.header;
      }

      async getIndex() {
        const genome = this.genome;

        if (!this.index) {
          this.index = await loadIndex(this.baiPath, this.config, genome);
        }

        return this.index;
      }

      async getChrIndex() {
        if (this.chrToIndex) {
          return this.chrToIndex;
        } else {
          const header = await this.getHeader();
          this.chrToIndex = header.chrToIndex;
          this.indexToChr = header.chrNames;
          this.chrAliasTable = header.chrAliasTable;
          return this.chrToIndex;
        }
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class ShardedBamReader {
      constructor(config, genome) {
        this.config = config;
        this.genome = genome;
        const bamReaders = {};
        config.sources.sequences.forEach(function (chr) {
          const queryChr = genome ? genome.getChromosomeName(chr) : chr;
          bamReaders[queryChr] = getReader(config, genome, chr);
        });
        this.bamReaders = bamReaders;
        BamUtils.setReaderDefaults(this, config);
      }

      async readAlignments(chr, start, end) {
        if (!this.bamReaders.hasOwnProperty(chr)) {
          return new AlignmentContainer(chr, start, end, this.config);
        } else {
          let reader = this.bamReaders[chr];
          const a = await reader.readAlignments(chr, start, end);
          return a;
        }
      }

    }

    function getReader(config, genome, chr) {
      const tmp = {
        url: config.sources.url.replace("$CHR", chr)
      };

      if (config.sources.indexURL) {
        tmp.indexURL = config.sources.indexURL.replace("$CHR", chr);
      }

      const bamConfig = Object.assign(config, tmp); // TODO -- support non-indexed, htsget, etc

      return new BamReader(bamConfig, genome);
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    /**
     * Class for reading bam records from an igv.js-flask server backed by pysam.  Deprecated.
     *
     * @param config
     * @constructor
     */

    const BamWebserviceReader = function (config, genome) {
      this.config = config;
      this.genome = genome;
      BamUtils.setReaderDefaults(this, config);
    }; // Example http://localhost:5000/alignments/?reference=/Users/jrobinso/hg19mini.fa&file=/Users/jrobinso/cram_with_crai_index.cram&region=1:100-2000


    BamWebserviceReader.prototype.readAlignments = function (chr, bpStart, bpEnd) {
      var self = this;
      return getHeader.call(self).then(function (header) {
        var queryChr, url;
        queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
        url = self.config.url + "?reference=" + self.config.referenceFile + "&file=" + self.config.alignmentFile + "" + "&region=" + queryChr + ":" + bpStart + "-" + bpEnd;
        return igvxhr.loadString(url, buildOptions(self.config)).then(function (sam) {
          var alignmentContainer;
          header.chrToIndex[queryChr];
          alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, self.config);
          BamUtils.decodeSamRecords(sam, alignmentContainer, queryChr, bpStart, bpEnd, self.filter);
          return alignmentContainer;
        });
      });
    }; // Example  http://localhost:5000/alignments/?reference=/Users/jrobinso/hg19mini.fa&file=/Users/jrobinso/cram_with_crai_index.cram&options=-b%20-H


    function getHeader() {
      const self = this;
      const genome = this.genome;

      if (this.header) {
        return Promise.resolve(this.header);
      } else {
        const url = this.config.url + "?file=" + this.config.alignmentFile + "&options=-b,-H";
        const options = buildOptions(this.config);
        return BamUtils.readHeader(url, options, genome).then(function (header) {
          self.header = header;
          return header;
        });
      }
    }

    class HtsgetReader {
      constructor(config, genome) {
        this.config = config;
        this.genome = genome;
        this.format = config.format ? config.format.toUpperCase() : "BAM"; // Backward compatibility

        if (!(this.format === "BAM" || this.format === "VCF")) {
          throw Error(`htsget format ${config.format} is not supported`);
        }
      }

      async readHeaderData() {
        const url = `${getUrl(this.config)}?class=header&format=${this.format}`;
        const ticket = await igvxhr.loadJson(url, buildOptions(this.config));
        return await this.loadUrls(ticket.htsget.urls);
      }

      async readData(chr, start, end) {
        const url = `${getUrl(this.config)}?format=${this.format}&referenceName=${chr}&start=${Math.floor(start)}&end=${Math.ceil(end)}`;
        const ticket = await igvxhr.loadJson(url, buildOptions(this.config));
        return this.loadUrls(ticket.htsget.urls);
      }

      async loadUrls(urls) {
        const promiseArray = [];

        for (let urlData of urls) {
          if (urlData.url.startsWith('data:')) {
            // this is a data-uri
            promiseArray.push(Promise.resolve(dataUriToBytes(urlData.url)));
          } else {
            const options = buildOptions(this.config || {});

            if (urlData.headers) {
              options.headers = Object.assign(options.headers || {}, urlData.headers);
            }

            promiseArray.push(igvxhr.loadArrayBuffer(urlData.url, options));
          }
        }

        const arrayBuffers = await Promise.all(promiseArray);
        return concatArrays(arrayBuffers);
      }

      static async inferFormat(config) {
        try {
          const url = getUrl(config);
          const headerURL = `${url}${url.includes("?") ? "&" : "?"}class=header`;
          const ticket = await igvxhr.loadJson(headerURL, buildOptions(config));

          if (ticket.htsget) {
            const format = ticket.htsget.format;

            if (!(format === "BAM" || format === "VCF")) {
              throw Error(`htsget format ${format} is not supported`);
            }

            config.format = format.toLowerCase();
            config.sourceType = "htsget";

            if (!config.name) {
              config.name = await getFilename(config.url);
            }
          }
        } catch (e) {// Errors => this is not an htsget source, not an application error.  Ignore
        }
      }

    }
    /**
     * Extract the full url from the config.  Striving for backward compatibility, "endpoint" and "id" are deprecated.
     *
     * @param config
     */


    function getUrl(config) {
      if (config.url && config.endpoint && config.id) {
        return config.url + config.endpoint + config.id; // Deprecated
      } else if (config.endpoint && config.id) {
        return config.endpoint + config.id; // Deprecated
      } else if (config.url) {
        if (config.url.startsWith("htsget://")) {
          return config.url.replace("htsget://", "https://"); // htsget -> http not supported
        } else {
          return config.url;
        }
      } else {
        throw Error("Must specify either 'url', or 'endpoint' and 'id");
      }
    }
    /**
     * Concatenate a list of array buffers, returning an UInt8Array
     * @param arrayBuffers
     */


    function concatArrays(arrayBuffers) {
      let len = 0;

      for (let a of arrayBuffers) {
        len += a.byteLength;
      }

      let offset = 0;
      const newArray = new Uint8Array(len);

      for (let buf of arrayBuffers) {
        const a = new Uint8Array(buf);
        newArray.set(a, offset);
        offset += a.length;
      }

      return newArray;
    }

    function dataUriToBytes(dataUri) {
      const split = dataUri.split(',');
      const info = split[0].split(':')[1];
      let dataString = split[1];

      if (info.indexOf('base64') >= 0) {
        dataString = atob(dataString);
      } else {
        dataString = decodeURI(dataString);
      }

      const bytes = new Uint8Array(dataString.length);

      for (var i = 0; i < dataString.length; i++) {
        bytes[i] = dataString.charCodeAt(i);
      }

      return bytes;
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class HtsgetBamReader extends HtsgetReader {
      constructor(config, genome) {
        super(config, genome);
        BamUtils.setReaderDefaults(this, config);
      }

      async readAlignments(chr, start, end) {
        if (!this.header) {
          const compressedData = await this.readHeaderData();
          const ba = unbgzf(compressedData.buffer);
          this.header = BamUtils.decodeBamHeader(ba, this.genome);
          this.chrAliasTable = new Map();

          for (let key of Object.keys(this.header.chrAliasTable)) {
            this.chrAliasTable.set(key, this.header.chrAliasTable[key]);
          }
        }

        let queryChr = this.chrAliasTable.has(chr) ? this.chrAliasTable.get(chr) : chr;
        const compressedData = await this.readData(queryChr, start, end); // BAM decoding

        const ba = unbgzf(compressedData.buffer);
        const chrIdx = this.header.chrToIndex[chr];
        const alignmentContainer = new AlignmentContainer(chr, start, end, this.config);
        BamUtils.decodeBamRecords(ba, this.header.size, alignmentContainer, this.header.chrNames, chrIdx, start, end);
        alignmentContainer.finish();
        return alignmentContainer;
      }

    }

    var FunctionPrototype = Function.prototype;
    var apply = FunctionPrototype.apply;
    var call = FunctionPrototype.call; // eslint-disable-next-line es-x/no-reflect -- safe

    var functionApply = typeof Reflect == 'object' && Reflect.apply || (functionBindNative ? call.bind(apply) : function () {
      return call.apply(apply, arguments);
    });

    var bind = functionUncurryThis(functionUncurryThis.bind); // optional / simple context binding

    var functionBindContext = function (fn, that) {
      aCallable(fn);
      return that === undefined ? fn : functionBindNative ? bind(fn, that) : function
        /* ...args */
      () {
        return fn.apply(that, arguments);
      };
    };

    var arraySlice = functionUncurryThis([].slice);

    var TypeError$1 = global$1.TypeError;

    var validateArgumentsLength = function (passed, required) {
      if (passed < required) throw TypeError$1('Not enough arguments');
      return passed;
    };

    var engineIsIos = /(?:ipad|iphone|ipod).*applewebkit/i.test(engineUserAgent);

    var engineIsNode = classofRaw(global$1.process) == 'process';

    var set = global$1.setImmediate;
    var clear = global$1.clearImmediate;
    var process$1 = global$1.process;
    var Dispatch = global$1.Dispatch;
    var Function$1 = global$1.Function;
    var MessageChannel$1 = global$1.MessageChannel;
    var String$1 = global$1.String;
    var counter = 0;
    var queue = {};
    var ONREADYSTATECHANGE = 'onreadystatechange';
    var location, defer, channel, port;

    try {
      // Deno throws a ReferenceError on `location` access without `--location` flag
      location = global$1.location;
    } catch (error) {
      /* empty */
    }

    var run = function (id) {
      if (hasOwnProperty_1(queue, id)) {
        var fn = queue[id];
        delete queue[id];
        fn();
      }
    };

    var runner = function (id) {
      return function () {
        run(id);
      };
    };

    var listener = function (event) {
      run(event.data);
    };

    var post = function (id) {
      // old engines have not location.origin
      global$1.postMessage(String$1(id), location.protocol + '//' + location.host);
    }; // Node.js 0.9+ & IE10+ has setImmediate, otherwise:


    if (!set || !clear) {
      set = function setImmediate(handler) {
        validateArgumentsLength(arguments.length, 1);
        var fn = isCallable(handler) ? handler : Function$1(handler);
        var args = arraySlice(arguments, 1);

        queue[++counter] = function () {
          functionApply(fn, undefined, args);
        };

        defer(counter);
        return counter;
      };

      clear = function clearImmediate(id) {
        delete queue[id];
      }; // Node.js 0.8-


      if (engineIsNode) {
        defer = function (id) {
          process$1.nextTick(runner(id));
        }; // Sphere (JS game engine) Dispatch API

      } else if (Dispatch && Dispatch.now) {
        defer = function (id) {
          Dispatch.now(runner(id));
        }; // Browsers with MessageChannel, includes WebWorkers
        // except iOS - https://github.com/zloirock/core-js/issues/624

      } else if (MessageChannel$1 && !engineIsIos) {
        channel = new MessageChannel$1();
        port = channel.port2;
        channel.port1.onmessage = listener;
        defer = functionBindContext(port.postMessage, port); // Browsers with postMessage, skip WebWorkers
        // IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
      } else if (global$1.addEventListener && isCallable(global$1.postMessage) && !global$1.importScripts && location && location.protocol !== 'file:' && !fails(post)) {
        defer = post;
        global$1.addEventListener('message', listener, false); // IE8-
      } else if (ONREADYSTATECHANGE in documentCreateElement('script')) {
        defer = function (id) {
          html.appendChild(documentCreateElement('script'))[ONREADYSTATECHANGE] = function () {
            html.removeChild(this);
            run(id);
          };
        }; // Rest old browsers

      } else {
        defer = function (id) {
          setTimeout(runner(id), 0);
        };
      }
    }

    var task = {
      set: set,
      clear: clear
    };

    var clearImmediate = task.clear; // `clearImmediate` method
    // http://w3c.github.io/setImmediate/#si-clearImmediate

    _export({
      global: true,
      bind: true,
      enumerable: true,
      forced: global$1.clearImmediate !== clearImmediate
    }, {
      clearImmediate: clearImmediate
    });

    var setImmediate = task.set; // `setImmediate` method
    // http://w3c.github.io/setImmediate/#si-setImmediate

    _export({
      global: true,
      bind: true,
      enumerable: true,
      forced: global$1.setImmediate !== setImmediate
    }, {
      setImmediate: setImmediate
    });

    const eval2 = eval;

    const gmodCRAM = function (t) {
      var e = {};

      function r(n) {
        if (e[n]) return e[n].exports;
        var i = e[n] = {
          i: n,
          l: !1,
          exports: {}
        };
        return t[n].call(i.exports, i, i.exports, r), i.l = !0, i.exports;
      }

      return r.m = t, r.c = e, r.d = function (t, e, n) {
        r.o(t, e) || Object.defineProperty(t, e, {
          enumerable: !0,
          get: n
        });
      }, r.r = function (t) {
        "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(t, Symbol.toStringTag, {
          value: "Module"
        }), Object.defineProperty(t, "__esModule", {
          value: !0
        });
      }, r.t = function (t, e) {
        if (1 & e && (t = r(t)), 8 & e) return t;
        if (4 & e && "object" == typeof t && t && t.__esModule) return t;
        var n = Object.create(null);
        if (r.r(n), Object.defineProperty(n, "default", {
          enumerable: !0,
          value: t
        }), 2 & e && "string" != typeof t) for (var i in t) r.d(n, i, function (e) {
          return t[e];
        }.bind(null, i));
        return n;
      }, r.n = function (t) {
        var e = t && t.__esModule ? function () {
          return t.default;
        } : function () {
          return t;
        };
        return r.d(e, "a", e), e;
      }, r.o = function (t, e) {
        return Object.prototype.hasOwnProperty.call(t, e);
      }, r.p = "", r(r.s = 163);
    }([function (t, e, r) {

      var n = r(13),
          i = r(81).f,
          o = r(107),
          a = r(3),
          s = r(15),
          u = r(20),
          f = r(24),
          c = function (t) {
        var e = function (e, r, n) {
          if (this instanceof t) {
            switch (arguments.length) {
              case 0:
                return new t();

              case 1:
                return new t(e);

              case 2:
                return new t(e, r);
            }

            return new t(e, r, n);
          }

          return t.apply(this, arguments);
        };

        return e.prototype = t.prototype, e;
      };

      t.exports = function (t, e) {
        var r,
            l,
            h,
            d,
            p,
            g,
            v,
            y,
            m = t.target,
            b = t.global,
            _ = t.stat,
            w = t.proto,
            x = b ? n : _ ? n[m] : (n[m] || {}).prototype,
            E = b ? a : a[m] || (a[m] = {}),
            S = E.prototype;

        for (h in e) r = !o(b ? h : m + (_ ? "." : "#") + h, t.forced) && x && f(x, h), p = E[h], r && (g = t.noTargetGet ? (y = i(x, h)) && y.value : x[h]), d = r && g ? g : e[h], r && typeof p == typeof d || (v = t.bind && r ? s(d, n) : t.wrap && r ? c(d) : w && "function" == typeof d ? s(Function.call, d) : d, (t.sham || d && d.sham || p && p.sham) && u(v, "sham", !0), E[h] = v, w && (f(a, l = m + "Prototype") || u(a, l, {}), a[l][h] = d, t.real && S && !S[h] && u(S, h, d)));
      };
    }, function (t, e) {
      t.exports = function (t) {
        return t && t.__esModule ? t : {
          default: t
        };
      };
    }, function (t, e, r) {
      var n = r(14);

      t.exports = function (t) {
        if (!n(t)) throw TypeError(String(t) + " is not an object");
        return t;
      };
    }, function (t, e) {
      t.exports = {};
    }, function (t, e) {
      t.exports = !0;
    }, function (t, e) {
      t.exports = function (t) {
        if ("function" != typeof t) throw TypeError(String(t) + " is not a function");
        return t;
      };
    }, function (t, e, r) {
      var n = r(13),
          i = r(61),
          o = r(64),
          a = r(110),
          s = n.Symbol,
          u = i("wks");

      t.exports = function (t) {
        return u[t] || (u[t] = a && s[t] || (a ? s : o)("Symbol." + t));
      };
    }, function (t, e) {
      t.exports = function (t, e) {
        if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function");
      };
    }, function (t, e, r) {
      var n = r(2),
          i = r(119),
          o = r(29),
          a = r(15),
          s = r(90),
          u = r(120),
          f = function (t, e) {
        this.stopped = t, this.result = e;
      };

      (t.exports = function (t, e, r, c, l) {
        var h,
            d,
            p,
            g,
            v,
            y,
            m = a(e, r, c ? 2 : 1);
        if (l) h = t;else {
          if ("function" != typeof (d = s(t))) throw TypeError("Target is not iterable");

          if (i(d)) {
            for (p = 0, g = o(t.length); g > p; p++) if ((v = c ? m(n(y = t[p])[0], y[1]) : m(t[p])) && v instanceof f) return v;

            return new f(!1);
          }

          h = d.call(t);
        }

        for (; !(y = h.next()).done;) if ((v = u(h, m, y.value, c)) && v instanceof f) return v;

        return new f(!1);
      }).stop = function (t) {
        return new f(!0, t);
      };
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(7)),
          o = n(r(32)),
          a = n(r(27)),
          s = n(r(33)),
          u = n(r(265)),
          f = function (t) {
        function e() {
          return (0, i.default)(this, e), (0, o.default)(this, (0, a.default)(e).apply(this, arguments));
        }

        return (0, s.default)(e, t), e;
      }((0, u.default)(Error)),
          c = function (t) {
        function e() {
          return (0, i.default)(this, e), (0, o.default)(this, (0, a.default)(e).apply(this, arguments));
        }

        return (0, s.default)(e, t), e;
      }((0, u.default)(Error)),
          l = function (t) {
        function e() {
          return (0, i.default)(this, e), (0, o.default)(this, (0, a.default)(e).apply(this, arguments));
        }

        return (0, s.default)(e, t), e;
      }(f),
          h = function (t) {
        function e() {
          return (0, i.default)(this, e), (0, o.default)(this, (0, a.default)(e).apply(this, arguments));
        }

        return (0, s.default)(e, t), e;
      }(l),
          d = function (t) {
        function e() {
          return (0, i.default)(this, e), (0, o.default)(this, (0, a.default)(e).apply(this, arguments));
        }

        return (0, s.default)(e, t), e;
      }(f),
          p = function (t) {
        function e() {
          return (0, i.default)(this, e), (0, o.default)(this, (0, a.default)(e).apply(this, arguments));
        }

        return (0, s.default)(e, t), e;
      }(f);

      t.exports = {
        CramBufferOverrunError: h,
        CramMalformedError: l,
        CramUnimplementedError: c,
        CramSizeLimitError: d,
        CramArgumentError: p
      };
    }, function (t, e, r) {
      var n = r(196);

      function i(t, e) {
        for (var r = 0; r < e.length; r++) {
          var i = e[r];
          i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), n(t, i.key, i);
        }
      }

      t.exports = function (t, e, r) {
        return e && i(t.prototype, e), r && i(t, r), t;
      };
    }, function (t, e, r) {
      var n = r(3),
          i = r(24),
          o = r(95),
          a = r(21).f;

      t.exports = function (t) {
        var e = n.Symbol || (n.Symbol = {});
        i(e, t) || a(e, t, {
          value: o.f(t)
        });
      };
    }, function (t, e) {
      t.exports = function (t) {
        try {
          return !!t();
        } catch (t) {
          return !0;
        }
      };
    }, function (t, e, r) {
      (function (e) {
        var r = "object",
            n = function (t) {
          return t && t.Math == Math && t;
        };

        t.exports = n(typeof globalThis == r && globalThis) || n(typeof window == r && window) || n(typeof self == r && self) || n(typeof e == r && e) || Function("return this")();
      }).call(this, r(23));
    }, function (t, e) {
      t.exports = function (t) {
        return "object" == typeof t ? null !== t : "function" == typeof t;
      };
    }, function (t, e, r) {
      var n = r(5);

      t.exports = function (t, e, r) {
        if (n(t), void 0 === e) return t;

        switch (r) {
          case 0:
            return function () {
              return t.call(e);
            };

          case 1:
            return function (r) {
              return t.call(e, r);
            };

          case 2:
            return function (r, n) {
              return t.call(e, r, n);
            };

          case 3:
            return function (r, n, i) {
              return t.call(e, r, n, i);
            };
        }

        return function () {
          return t.apply(e, arguments);
        };
      };
    }, function (t, e, r) {
      t.exports = r(167);
    }, function (t, e, r) {
      var n = r(12);
      t.exports = !n(function () {
        return 7 != Object.defineProperty({}, "a", {
          get: function () {
            return 7;
          }
        }).a;
      });
    }, function (t, e, r) {
      var n = r(3);

      t.exports = function (t) {
        return n[t + "Prototype"];
      };
    }, function (t, e, r) {

      (function (t) {
        /*!
         * The buffer module from node.js, for the browser.
         *
         * @author   Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
         * @license  MIT
         */
        var n = r(165),
            i = r(166),
            o = r(105);

        function a() {
          return u.TYPED_ARRAY_SUPPORT ? 2147483647 : 1073741823;
        }

        function s(t, e) {
          if (a() < e) throw new RangeError("Invalid typed array length");
          return u.TYPED_ARRAY_SUPPORT ? (t = new Uint8Array(e)).__proto__ = u.prototype : (null === t && (t = new u(e)), t.length = e), t;
        }

        function u(t, e, r) {
          if (!(u.TYPED_ARRAY_SUPPORT || this instanceof u)) return new u(t, e, r);

          if ("number" == typeof t) {
            if ("string" == typeof e) throw new Error("If encoding is specified then the first argument must be a string");
            return l(this, t);
          }

          return f(this, t, e, r);
        }

        function f(t, e, r, n) {
          if ("number" == typeof e) throw new TypeError('"value" argument must not be a number');
          return "undefined" != typeof ArrayBuffer && e instanceof ArrayBuffer ? function (t, e, r, n) {
            if (e.byteLength, r < 0 || e.byteLength < r) throw new RangeError("'offset' is out of bounds");
            if (e.byteLength < r + (n || 0)) throw new RangeError("'length' is out of bounds");
            e = void 0 === r && void 0 === n ? new Uint8Array(e) : void 0 === n ? new Uint8Array(e, r) : new Uint8Array(e, r, n);
            u.TYPED_ARRAY_SUPPORT ? (t = e).__proto__ = u.prototype : t = h(t, e);
            return t;
          }(t, e, r, n) : "string" == typeof e ? function (t, e, r) {
            "string" == typeof r && "" !== r || (r = "utf8");
            if (!u.isEncoding(r)) throw new TypeError('"encoding" must be a valid string encoding');
            var n = 0 | p(e, r),
                i = (t = s(t, n)).write(e, r);
            i !== n && (t = t.slice(0, i));
            return t;
          }(t, e, r) : function (t, e) {
            if (u.isBuffer(e)) {
              var r = 0 | d(e.length);
              return 0 === (t = s(t, r)).length ? t : (e.copy(t, 0, 0, r), t);
            }

            if (e) {
              if ("undefined" != typeof ArrayBuffer && e.buffer instanceof ArrayBuffer || "length" in e) return "number" != typeof e.length || (n = e.length) != n ? s(t, 0) : h(t, e);
              if ("Buffer" === e.type && o(e.data)) return h(t, e.data);
            }

            var n;
            throw new TypeError("First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.");
          }(t, e);
        }

        function c(t) {
          if ("number" != typeof t) throw new TypeError('"size" argument must be a number');
          if (t < 0) throw new RangeError('"size" argument must not be negative');
        }

        function l(t, e) {
          if (c(e), t = s(t, e < 0 ? 0 : 0 | d(e)), !u.TYPED_ARRAY_SUPPORT) for (var r = 0; r < e; ++r) t[r] = 0;
          return t;
        }

        function h(t, e) {
          var r = e.length < 0 ? 0 : 0 | d(e.length);
          t = s(t, r);

          for (var n = 0; n < r; n += 1) t[n] = 255 & e[n];

          return t;
        }

        function d(t) {
          if (t >= a()) throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x" + a().toString(16) + " bytes");
          return 0 | t;
        }

        function p(t, e) {
          if (u.isBuffer(t)) return t.length;
          if ("undefined" != typeof ArrayBuffer && "function" == typeof ArrayBuffer.isView && (ArrayBuffer.isView(t) || t instanceof ArrayBuffer)) return t.byteLength;
          "string" != typeof t && (t = "" + t);
          var r = t.length;
          if (0 === r) return 0;

          for (var n = !1;;) switch (e) {
            case "ascii":
            case "latin1":
            case "binary":
              return r;

            case "utf8":
            case "utf-8":
            case void 0:
              return D(t).length;

            case "ucs2":
            case "ucs-2":
            case "utf16le":
            case "utf-16le":
              return 2 * r;

            case "hex":
              return r >>> 1;

            case "base64":
              return q(t).length;

            default:
              if (n) return D(t).length;
              e = ("" + e).toLowerCase(), n = !0;
          }
        }

        function g(t, e, r) {
          var n = !1;
          if ((void 0 === e || e < 0) && (e = 0), e > this.length) return "";
          if ((void 0 === r || r > this.length) && (r = this.length), r <= 0) return "";
          if ((r >>>= 0) <= (e >>>= 0)) return "";

          for (t || (t = "utf8");;) switch (t) {
            case "hex":
              return O(this, e, r);

            case "utf8":
            case "utf-8":
              return A(this, e, r);

            case "ascii":
              return T(this, e, r);

            case "latin1":
            case "binary":
              return R(this, e, r);

            case "base64":
              return k(this, e, r);

            case "ucs2":
            case "ucs-2":
            case "utf16le":
            case "utf-16le":
              return I(this, e, r);

            default:
              if (n) throw new TypeError("Unknown encoding: " + t);
              t = (t + "").toLowerCase(), n = !0;
          }
        }

        function v(t, e, r) {
          var n = t[e];
          t[e] = t[r], t[r] = n;
        }

        function y(t, e, r, n, i) {
          if (0 === t.length) return -1;

          if ("string" == typeof r ? (n = r, r = 0) : r > 2147483647 ? r = 2147483647 : r < -2147483648 && (r = -2147483648), r = +r, isNaN(r) && (r = i ? 0 : t.length - 1), r < 0 && (r = t.length + r), r >= t.length) {
            if (i) return -1;
            r = t.length - 1;
          } else if (r < 0) {
            if (!i) return -1;
            r = 0;
          }

          if ("string" == typeof e && (e = u.from(e, n)), u.isBuffer(e)) return 0 === e.length ? -1 : m(t, e, r, n, i);
          if ("number" == typeof e) return e &= 255, u.TYPED_ARRAY_SUPPORT && "function" == typeof Uint8Array.prototype.indexOf ? i ? Uint8Array.prototype.indexOf.call(t, e, r) : Uint8Array.prototype.lastIndexOf.call(t, e, r) : m(t, [e], r, n, i);
          throw new TypeError("val must be string, number or Buffer");
        }

        function m(t, e, r, n, i) {
          var o,
              a = 1,
              s = t.length,
              u = e.length;

          if (void 0 !== n && ("ucs2" === (n = String(n).toLowerCase()) || "ucs-2" === n || "utf16le" === n || "utf-16le" === n)) {
            if (t.length < 2 || e.length < 2) return -1;
            a = 2, s /= 2, u /= 2, r /= 2;
          }

          function f(t, e) {
            return 1 === a ? t[e] : t.readUInt16BE(e * a);
          }

          if (i) {
            var c = -1;

            for (o = r; o < s; o++) if (f(t, o) === f(e, -1 === c ? 0 : o - c)) {
              if (-1 === c && (c = o), o - c + 1 === u) return c * a;
            } else -1 !== c && (o -= o - c), c = -1;
          } else for (r + u > s && (r = s - u), o = r; o >= 0; o--) {
            for (var l = !0, h = 0; h < u; h++) if (f(t, o + h) !== f(e, h)) {
              l = !1;
              break;
            }

            if (l) return o;
          }

          return -1;
        }

        function b(t, e, r, n) {
          r = Number(r) || 0;
          var i = t.length - r;
          n ? (n = Number(n)) > i && (n = i) : n = i;
          var o = e.length;
          if (o % 2 != 0) throw new TypeError("Invalid hex string");
          n > o / 2 && (n = o / 2);

          for (var a = 0; a < n; ++a) {
            var s = parseInt(e.substr(2 * a, 2), 16);
            if (isNaN(s)) return a;
            t[r + a] = s;
          }

          return a;
        }

        function _(t, e, r, n) {
          return Z(D(e, t.length - r), t, r, n);
        }

        function w(t, e, r, n) {
          return Z(function (t) {
            for (var e = [], r = 0; r < t.length; ++r) e.push(255 & t.charCodeAt(r));

            return e;
          }(e), t, r, n);
        }

        function x(t, e, r, n) {
          return w(t, e, r, n);
        }

        function E(t, e, r, n) {
          return Z(q(e), t, r, n);
        }

        function S(t, e, r, n) {
          return Z(function (t, e) {
            for (var r, n, i, o = [], a = 0; a < t.length && !((e -= 2) < 0); ++a) r = t.charCodeAt(a), n = r >> 8, i = r % 256, o.push(i), o.push(n);

            return o;
          }(e, t.length - r), t, r, n);
        }

        function k(t, e, r) {
          return 0 === e && r === t.length ? n.fromByteArray(t) : n.fromByteArray(t.slice(e, r));
        }

        function A(t, e, r) {
          r = Math.min(t.length, r);

          for (var n = [], i = e; i < r;) {
            var o,
                a,
                s,
                u,
                f = t[i],
                c = null,
                l = f > 239 ? 4 : f > 223 ? 3 : f > 191 ? 2 : 1;
            if (i + l <= r) switch (l) {
              case 1:
                f < 128 && (c = f);
                break;

              case 2:
                128 == (192 & (o = t[i + 1])) && (u = (31 & f) << 6 | 63 & o) > 127 && (c = u);
                break;

              case 3:
                o = t[i + 1], a = t[i + 2], 128 == (192 & o) && 128 == (192 & a) && (u = (15 & f) << 12 | (63 & o) << 6 | 63 & a) > 2047 && (u < 55296 || u > 57343) && (c = u);
                break;

              case 4:
                o = t[i + 1], a = t[i + 2], s = t[i + 3], 128 == (192 & o) && 128 == (192 & a) && 128 == (192 & s) && (u = (15 & f) << 18 | (63 & o) << 12 | (63 & a) << 6 | 63 & s) > 65535 && u < 1114112 && (c = u);
            }
            null === c ? (c = 65533, l = 1) : c > 65535 && (c -= 65536, n.push(c >>> 10 & 1023 | 55296), c = 56320 | 1023 & c), n.push(c), i += l;
          }

          return function (t) {
            var e = t.length;
            if (e <= C) return String.fromCharCode.apply(String, t);
            var r = "",
                n = 0;

            for (; n < e;) r += String.fromCharCode.apply(String, t.slice(n, n += C));

            return r;
          }(n);
        }

        e.Buffer = u, e.SlowBuffer = function (t) {
          +t != t && (t = 0);
          return u.alloc(+t);
        }, e.INSPECT_MAX_BYTES = 50, u.TYPED_ARRAY_SUPPORT = void 0 !== t.TYPED_ARRAY_SUPPORT ? t.TYPED_ARRAY_SUPPORT : function () {
          try {
            var t = new Uint8Array(1);
            return t.__proto__ = {
              __proto__: Uint8Array.prototype,
              foo: function () {
                return 42;
              }
            }, 42 === t.foo() && "function" == typeof t.subarray && 0 === t.subarray(1, 1).byteLength;
          } catch (t) {
            return !1;
          }
        }(), e.kMaxLength = a(), u.poolSize = 8192, u._augment = function (t) {
          return t.__proto__ = u.prototype, t;
        }, u.from = function (t, e, r) {
          return f(null, t, e, r);
        }, u.TYPED_ARRAY_SUPPORT && (u.prototype.__proto__ = Uint8Array.prototype, u.__proto__ = Uint8Array, "undefined" != typeof Symbol && Symbol.species && u[Symbol.species] === u && Object.defineProperty(u, Symbol.species, {
          value: null,
          configurable: !0
        })), u.alloc = function (t, e, r) {
          return function (t, e, r, n) {
            return c(e), e <= 0 ? s(t, e) : void 0 !== r ? "string" == typeof n ? s(t, e).fill(r, n) : s(t, e).fill(r) : s(t, e);
          }(null, t, e, r);
        }, u.allocUnsafe = function (t) {
          return l(null, t);
        }, u.allocUnsafeSlow = function (t) {
          return l(null, t);
        }, u.isBuffer = function (t) {
          return !(null == t || !t._isBuffer);
        }, u.compare = function (t, e) {
          if (!u.isBuffer(t) || !u.isBuffer(e)) throw new TypeError("Arguments must be Buffers");
          if (t === e) return 0;

          for (var r = t.length, n = e.length, i = 0, o = Math.min(r, n); i < o; ++i) if (t[i] !== e[i]) {
            r = t[i], n = e[i];
            break;
          }

          return r < n ? -1 : n < r ? 1 : 0;
        }, u.isEncoding = function (t) {
          switch (String(t).toLowerCase()) {
            case "hex":
            case "utf8":
            case "utf-8":
            case "ascii":
            case "latin1":
            case "binary":
            case "base64":
            case "ucs2":
            case "ucs-2":
            case "utf16le":
            case "utf-16le":
              return !0;

            default:
              return !1;
          }
        }, u.concat = function (t, e) {
          if (!o(t)) throw new TypeError('"list" argument must be an Array of Buffers');
          if (0 === t.length) return u.alloc(0);
          var r;
          if (void 0 === e) for (e = 0, r = 0; r < t.length; ++r) e += t[r].length;
          var n = u.allocUnsafe(e),
              i = 0;

          for (r = 0; r < t.length; ++r) {
            var a = t[r];
            if (!u.isBuffer(a)) throw new TypeError('"list" argument must be an Array of Buffers');
            a.copy(n, i), i += a.length;
          }

          return n;
        }, u.byteLength = p, u.prototype._isBuffer = !0, u.prototype.swap16 = function () {
          var t = this.length;
          if (t % 2 != 0) throw new RangeError("Buffer size must be a multiple of 16-bits");

          for (var e = 0; e < t; e += 2) v(this, e, e + 1);

          return this;
        }, u.prototype.swap32 = function () {
          var t = this.length;
          if (t % 4 != 0) throw new RangeError("Buffer size must be a multiple of 32-bits");

          for (var e = 0; e < t; e += 4) v(this, e, e + 3), v(this, e + 1, e + 2);

          return this;
        }, u.prototype.swap64 = function () {
          var t = this.length;
          if (t % 8 != 0) throw new RangeError("Buffer size must be a multiple of 64-bits");

          for (var e = 0; e < t; e += 8) v(this, e, e + 7), v(this, e + 1, e + 6), v(this, e + 2, e + 5), v(this, e + 3, e + 4);

          return this;
        }, u.prototype.toString = function () {
          var t = 0 | this.length;
          return 0 === t ? "" : 0 === arguments.length ? A(this, 0, t) : g.apply(this, arguments);
        }, u.prototype.equals = function (t) {
          if (!u.isBuffer(t)) throw new TypeError("Argument must be a Buffer");
          return this === t || 0 === u.compare(this, t);
        }, u.prototype.inspect = function () {
          var t = "",
              r = e.INSPECT_MAX_BYTES;
          return this.length > 0 && (t = this.toString("hex", 0, r).match(/.{2}/g).join(" "), this.length > r && (t += " ... ")), "<Buffer " + t + ">";
        }, u.prototype.compare = function (t, e, r, n, i) {
          if (!u.isBuffer(t)) throw new TypeError("Argument must be a Buffer");
          if (void 0 === e && (e = 0), void 0 === r && (r = t ? t.length : 0), void 0 === n && (n = 0), void 0 === i && (i = this.length), e < 0 || r > t.length || n < 0 || i > this.length) throw new RangeError("out of range index");
          if (n >= i && e >= r) return 0;
          if (n >= i) return -1;
          if (e >= r) return 1;
          if (this === t) return 0;

          for (var o = (i >>>= 0) - (n >>>= 0), a = (r >>>= 0) - (e >>>= 0), s = Math.min(o, a), f = this.slice(n, i), c = t.slice(e, r), l = 0; l < s; ++l) if (f[l] !== c[l]) {
            o = f[l], a = c[l];
            break;
          }

          return o < a ? -1 : a < o ? 1 : 0;
        }, u.prototype.includes = function (t, e, r) {
          return -1 !== this.indexOf(t, e, r);
        }, u.prototype.indexOf = function (t, e, r) {
          return y(this, t, e, r, !0);
        }, u.prototype.lastIndexOf = function (t, e, r) {
          return y(this, t, e, r, !1);
        }, u.prototype.write = function (t, e, r, n) {
          if (void 0 === e) n = "utf8", r = this.length, e = 0;else if (void 0 === r && "string" == typeof e) n = e, r = this.length, e = 0;else {
            if (!isFinite(e)) throw new Error("Buffer.write(string, encoding, offset[, length]) is no longer supported");
            e |= 0, isFinite(r) ? (r |= 0, void 0 === n && (n = "utf8")) : (n = r, r = void 0);
          }
          var i = this.length - e;
          if ((void 0 === r || r > i) && (r = i), t.length > 0 && (r < 0 || e < 0) || e > this.length) throw new RangeError("Attempt to write outside buffer bounds");
          n || (n = "utf8");

          for (var o = !1;;) switch (n) {
            case "hex":
              return b(this, t, e, r);

            case "utf8":
            case "utf-8":
              return _(this, t, e, r);

            case "ascii":
              return w(this, t, e, r);

            case "latin1":
            case "binary":
              return x(this, t, e, r);

            case "base64":
              return E(this, t, e, r);

            case "ucs2":
            case "ucs-2":
            case "utf16le":
            case "utf-16le":
              return S(this, t, e, r);

            default:
              if (o) throw new TypeError("Unknown encoding: " + n);
              n = ("" + n).toLowerCase(), o = !0;
          }
        }, u.prototype.toJSON = function () {
          return {
            type: "Buffer",
            data: Array.prototype.slice.call(this._arr || this, 0)
          };
        };
        var C = 4096;

        function T(t, e, r) {
          var n = "";
          r = Math.min(t.length, r);

          for (var i = e; i < r; ++i) n += String.fromCharCode(127 & t[i]);

          return n;
        }

        function R(t, e, r) {
          var n = "";
          r = Math.min(t.length, r);

          for (var i = e; i < r; ++i) n += String.fromCharCode(t[i]);

          return n;
        }

        function O(t, e, r) {
          var n = t.length;
          (!e || e < 0) && (e = 0), (!r || r < 0 || r > n) && (r = n);

          for (var i = "", o = e; o < r; ++o) i += U(t[o]);

          return i;
        }

        function I(t, e, r) {
          for (var n = t.slice(e, r), i = "", o = 0; o < n.length; o += 2) i += String.fromCharCode(n[o] + 256 * n[o + 1]);

          return i;
        }

        function L(t, e, r) {
          if (t % 1 != 0 || t < 0) throw new RangeError("offset is not uint");
          if (t + e > r) throw new RangeError("Trying to access beyond buffer length");
        }

        function B(t, e, r, n, i, o) {
          if (!u.isBuffer(t)) throw new TypeError('"buffer" argument must be a Buffer instance');
          if (e > i || e < o) throw new RangeError('"value" argument is out of bounds');
          if (r + n > t.length) throw new RangeError("Index out of range");
        }

        function P(t, e, r, n) {
          e < 0 && (e = 65535 + e + 1);

          for (var i = 0, o = Math.min(t.length - r, 2); i < o; ++i) t[r + i] = (e & 255 << 8 * (n ? i : 1 - i)) >>> 8 * (n ? i : 1 - i);
        }

        function F(t, e, r, n) {
          e < 0 && (e = 4294967295 + e + 1);

          for (var i = 0, o = Math.min(t.length - r, 4); i < o; ++i) t[r + i] = e >>> 8 * (n ? i : 3 - i) & 255;
        }

        function N(t, e, r, n, i, o) {
          if (r + n > t.length) throw new RangeError("Index out of range");
          if (r < 0) throw new RangeError("Index out of range");
        }

        function M(t, e, r, n, o) {
          return o || N(t, 0, r, 4), i.write(t, e, r, n, 23, 4), r + 4;
        }

        function j(t, e, r, n, o) {
          return o || N(t, 0, r, 8), i.write(t, e, r, n, 52, 8), r + 8;
        }

        u.prototype.slice = function (t, e) {
          var r,
              n = this.length;
          if ((t = ~~t) < 0 ? (t += n) < 0 && (t = 0) : t > n && (t = n), (e = void 0 === e ? n : ~~e) < 0 ? (e += n) < 0 && (e = 0) : e > n && (e = n), e < t && (e = t), u.TYPED_ARRAY_SUPPORT) (r = this.subarray(t, e)).__proto__ = u.prototype;else {
            var i = e - t;
            r = new u(i, void 0);

            for (var o = 0; o < i; ++o) r[o] = this[o + t];
          }
          return r;
        }, u.prototype.readUIntLE = function (t, e, r) {
          t |= 0, e |= 0, r || L(t, e, this.length);

          for (var n = this[t], i = 1, o = 0; ++o < e && (i *= 256);) n += this[t + o] * i;

          return n;
        }, u.prototype.readUIntBE = function (t, e, r) {
          t |= 0, e |= 0, r || L(t, e, this.length);

          for (var n = this[t + --e], i = 1; e > 0 && (i *= 256);) n += this[t + --e] * i;

          return n;
        }, u.prototype.readUInt8 = function (t, e) {
          return e || L(t, 1, this.length), this[t];
        }, u.prototype.readUInt16LE = function (t, e) {
          return e || L(t, 2, this.length), this[t] | this[t + 1] << 8;
        }, u.prototype.readUInt16BE = function (t, e) {
          return e || L(t, 2, this.length), this[t] << 8 | this[t + 1];
        }, u.prototype.readUInt32LE = function (t, e) {
          return e || L(t, 4, this.length), (this[t] | this[t + 1] << 8 | this[t + 2] << 16) + 16777216 * this[t + 3];
        }, u.prototype.readUInt32BE = function (t, e) {
          return e || L(t, 4, this.length), 16777216 * this[t] + (this[t + 1] << 16 | this[t + 2] << 8 | this[t + 3]);
        }, u.prototype.readIntLE = function (t, e, r) {
          t |= 0, e |= 0, r || L(t, e, this.length);

          for (var n = this[t], i = 1, o = 0; ++o < e && (i *= 256);) n += this[t + o] * i;

          return n >= (i *= 128) && (n -= Math.pow(2, 8 * e)), n;
        }, u.prototype.readIntBE = function (t, e, r) {
          t |= 0, e |= 0, r || L(t, e, this.length);

          for (var n = e, i = 1, o = this[t + --n]; n > 0 && (i *= 256);) o += this[t + --n] * i;

          return o >= (i *= 128) && (o -= Math.pow(2, 8 * e)), o;
        }, u.prototype.readInt8 = function (t, e) {
          return e || L(t, 1, this.length), 128 & this[t] ? -1 * (255 - this[t] + 1) : this[t];
        }, u.prototype.readInt16LE = function (t, e) {
          e || L(t, 2, this.length);
          var r = this[t] | this[t + 1] << 8;
          return 32768 & r ? 4294901760 | r : r;
        }, u.prototype.readInt16BE = function (t, e) {
          e || L(t, 2, this.length);
          var r = this[t + 1] | this[t] << 8;
          return 32768 & r ? 4294901760 | r : r;
        }, u.prototype.readInt32LE = function (t, e) {
          return e || L(t, 4, this.length), this[t] | this[t + 1] << 8 | this[t + 2] << 16 | this[t + 3] << 24;
        }, u.prototype.readInt32BE = function (t, e) {
          return e || L(t, 4, this.length), this[t] << 24 | this[t + 1] << 16 | this[t + 2] << 8 | this[t + 3];
        }, u.prototype.readFloatLE = function (t, e) {
          return e || L(t, 4, this.length), i.read(this, t, !0, 23, 4);
        }, u.prototype.readFloatBE = function (t, e) {
          return e || L(t, 4, this.length), i.read(this, t, !1, 23, 4);
        }, u.prototype.readDoubleLE = function (t, e) {
          return e || L(t, 8, this.length), i.read(this, t, !0, 52, 8);
        }, u.prototype.readDoubleBE = function (t, e) {
          return e || L(t, 8, this.length), i.read(this, t, !1, 52, 8);
        }, u.prototype.writeUIntLE = function (t, e, r, n) {
          (t = +t, e |= 0, r |= 0, n) || B(this, t, e, r, Math.pow(2, 8 * r) - 1, 0);
          var i = 1,
              o = 0;

          for (this[e] = 255 & t; ++o < r && (i *= 256);) this[e + o] = t / i & 255;

          return e + r;
        }, u.prototype.writeUIntBE = function (t, e, r, n) {
          (t = +t, e |= 0, r |= 0, n) || B(this, t, e, r, Math.pow(2, 8 * r) - 1, 0);
          var i = r - 1,
              o = 1;

          for (this[e + i] = 255 & t; --i >= 0 && (o *= 256);) this[e + i] = t / o & 255;

          return e + r;
        }, u.prototype.writeUInt8 = function (t, e, r) {
          return t = +t, e |= 0, r || B(this, t, e, 1, 255, 0), u.TYPED_ARRAY_SUPPORT || (t = Math.floor(t)), this[e] = 255 & t, e + 1;
        }, u.prototype.writeUInt16LE = function (t, e, r) {
          return t = +t, e |= 0, r || B(this, t, e, 2, 65535, 0), u.TYPED_ARRAY_SUPPORT ? (this[e] = 255 & t, this[e + 1] = t >>> 8) : P(this, t, e, !0), e + 2;
        }, u.prototype.writeUInt16BE = function (t, e, r) {
          return t = +t, e |= 0, r || B(this, t, e, 2, 65535, 0), u.TYPED_ARRAY_SUPPORT ? (this[e] = t >>> 8, this[e + 1] = 255 & t) : P(this, t, e, !1), e + 2;
        }, u.prototype.writeUInt32LE = function (t, e, r) {
          return t = +t, e |= 0, r || B(this, t, e, 4, 4294967295, 0), u.TYPED_ARRAY_SUPPORT ? (this[e + 3] = t >>> 24, this[e + 2] = t >>> 16, this[e + 1] = t >>> 8, this[e] = 255 & t) : F(this, t, e, !0), e + 4;
        }, u.prototype.writeUInt32BE = function (t, e, r) {
          return t = +t, e |= 0, r || B(this, t, e, 4, 4294967295, 0), u.TYPED_ARRAY_SUPPORT ? (this[e] = t >>> 24, this[e + 1] = t >>> 16, this[e + 2] = t >>> 8, this[e + 3] = 255 & t) : F(this, t, e, !1), e + 4;
        }, u.prototype.writeIntLE = function (t, e, r, n) {
          if (t = +t, e |= 0, !n) {
            var i = Math.pow(2, 8 * r - 1);
            B(this, t, e, r, i - 1, -i);
          }

          var o = 0,
              a = 1,
              s = 0;

          for (this[e] = 255 & t; ++o < r && (a *= 256);) t < 0 && 0 === s && 0 !== this[e + o - 1] && (s = 1), this[e + o] = (t / a >> 0) - s & 255;

          return e + r;
        }, u.prototype.writeIntBE = function (t, e, r, n) {
          if (t = +t, e |= 0, !n) {
            var i = Math.pow(2, 8 * r - 1);
            B(this, t, e, r, i - 1, -i);
          }

          var o = r - 1,
              a = 1,
              s = 0;

          for (this[e + o] = 255 & t; --o >= 0 && (a *= 256);) t < 0 && 0 === s && 0 !== this[e + o + 1] && (s = 1), this[e + o] = (t / a >> 0) - s & 255;

          return e + r;
        }, u.prototype.writeInt8 = function (t, e, r) {
          return t = +t, e |= 0, r || B(this, t, e, 1, 127, -128), u.TYPED_ARRAY_SUPPORT || (t = Math.floor(t)), t < 0 && (t = 255 + t + 1), this[e] = 255 & t, e + 1;
        }, u.prototype.writeInt16LE = function (t, e, r) {
          return t = +t, e |= 0, r || B(this, t, e, 2, 32767, -32768), u.TYPED_ARRAY_SUPPORT ? (this[e] = 255 & t, this[e + 1] = t >>> 8) : P(this, t, e, !0), e + 2;
        }, u.prototype.writeInt16BE = function (t, e, r) {
          return t = +t, e |= 0, r || B(this, t, e, 2, 32767, -32768), u.TYPED_ARRAY_SUPPORT ? (this[e] = t >>> 8, this[e + 1] = 255 & t) : P(this, t, e, !1), e + 2;
        }, u.prototype.writeInt32LE = function (t, e, r) {
          return t = +t, e |= 0, r || B(this, t, e, 4, 2147483647, -2147483648), u.TYPED_ARRAY_SUPPORT ? (this[e] = 255 & t, this[e + 1] = t >>> 8, this[e + 2] = t >>> 16, this[e + 3] = t >>> 24) : F(this, t, e, !0), e + 4;
        }, u.prototype.writeInt32BE = function (t, e, r) {
          return t = +t, e |= 0, r || B(this, t, e, 4, 2147483647, -2147483648), t < 0 && (t = 4294967295 + t + 1), u.TYPED_ARRAY_SUPPORT ? (this[e] = t >>> 24, this[e + 1] = t >>> 16, this[e + 2] = t >>> 8, this[e + 3] = 255 & t) : F(this, t, e, !1), e + 4;
        }, u.prototype.writeFloatLE = function (t, e, r) {
          return M(this, t, e, !0, r);
        }, u.prototype.writeFloatBE = function (t, e, r) {
          return M(this, t, e, !1, r);
        }, u.prototype.writeDoubleLE = function (t, e, r) {
          return j(this, t, e, !0, r);
        }, u.prototype.writeDoubleBE = function (t, e, r) {
          return j(this, t, e, !1, r);
        }, u.prototype.copy = function (t, e, r, n) {
          if (r || (r = 0), n || 0 === n || (n = this.length), e >= t.length && (e = t.length), e || (e = 0), n > 0 && n < r && (n = r), n === r) return 0;
          if (0 === t.length || 0 === this.length) return 0;
          if (e < 0) throw new RangeError("targetStart out of bounds");
          if (r < 0 || r >= this.length) throw new RangeError("sourceStart out of bounds");
          if (n < 0) throw new RangeError("sourceEnd out of bounds");
          n > this.length && (n = this.length), t.length - e < n - r && (n = t.length - e + r);
          var i,
              o = n - r;
          if (this === t && r < e && e < n) for (i = o - 1; i >= 0; --i) t[i + e] = this[i + r];else if (o < 1e3 || !u.TYPED_ARRAY_SUPPORT) for (i = 0; i < o; ++i) t[i + e] = this[i + r];else Uint8Array.prototype.set.call(t, this.subarray(r, r + o), e);
          return o;
        }, u.prototype.fill = function (t, e, r, n) {
          if ("string" == typeof t) {
            if ("string" == typeof e ? (n = e, e = 0, r = this.length) : "string" == typeof r && (n = r, r = this.length), 1 === t.length) {
              var i = t.charCodeAt(0);
              i < 256 && (t = i);
            }

            if (void 0 !== n && "string" != typeof n) throw new TypeError("encoding must be a string");
            if ("string" == typeof n && !u.isEncoding(n)) throw new TypeError("Unknown encoding: " + n);
          } else "number" == typeof t && (t &= 255);

          if (e < 0 || this.length < e || this.length < r) throw new RangeError("Out of range index");
          if (r <= e) return this;
          var o;
          if (e >>>= 0, r = void 0 === r ? this.length : r >>> 0, t || (t = 0), "number" == typeof t) for (o = e; o < r; ++o) this[o] = t;else {
            var a = u.isBuffer(t) ? t : D(new u(t, n).toString()),
                s = a.length;

            for (o = 0; o < r - e; ++o) this[o + e] = a[o % s];
          }
          return this;
        };
        var z = /[^+\/0-9A-Za-z-_]/g;

        function U(t) {
          return t < 16 ? "0" + t.toString(16) : t.toString(16);
        }

        function D(t, e) {
          var r;
          e = e || 1 / 0;

          for (var n = t.length, i = null, o = [], a = 0; a < n; ++a) {
            if ((r = t.charCodeAt(a)) > 55295 && r < 57344) {
              if (!i) {
                if (r > 56319) {
                  (e -= 3) > -1 && o.push(239, 191, 189);
                  continue;
                }

                if (a + 1 === n) {
                  (e -= 3) > -1 && o.push(239, 191, 189);
                  continue;
                }

                i = r;
                continue;
              }

              if (r < 56320) {
                (e -= 3) > -1 && o.push(239, 191, 189), i = r;
                continue;
              }

              r = 65536 + (i - 55296 << 10 | r - 56320);
            } else i && (e -= 3) > -1 && o.push(239, 191, 189);

            if (i = null, r < 128) {
              if ((e -= 1) < 0) break;
              o.push(r);
            } else if (r < 2048) {
              if ((e -= 2) < 0) break;
              o.push(r >> 6 | 192, 63 & r | 128);
            } else if (r < 65536) {
              if ((e -= 3) < 0) break;
              o.push(r >> 12 | 224, r >> 6 & 63 | 128, 63 & r | 128);
            } else {
              if (!(r < 1114112)) throw new Error("Invalid code point");
              if ((e -= 4) < 0) break;
              o.push(r >> 18 | 240, r >> 12 & 63 | 128, r >> 6 & 63 | 128, 63 & r | 128);
            }
          }

          return o;
        }

        function q(t) {
          return n.toByteArray(function (t) {
            if ((t = function (t) {
              return t.trim ? t.trim() : t.replace(/^\s+|\s+$/g, "");
            }(t).replace(z, "")).length < 2) return "";

            for (; t.length % 4 != 0;) t += "=";

            return t;
          }(t));
        }

        function Z(t, e, r, n) {
          for (var i = 0; i < n && !(i + r >= e.length || i >= t.length); ++i) e[i + r] = t[i];

          return i;
        }
      }).call(this, r(23));
    }, function (t, e, r) {
      var n = r(17),
          i = r(21),
          o = r(43);
      t.exports = n ? function (t, e, r) {
        return i.f(t, e, o(1, r));
      } : function (t, e, r) {
        return t[e] = r, t;
      };
    }, function (t, e, r) {
      var n = r(17),
          i = r(106),
          o = r(2),
          a = r(62),
          s = Object.defineProperty;
      e.f = n ? s : function (t, e, r) {
        if (o(t), e = a(e, !0), o(r), i) try {
          return s(t, e, r);
        } catch (t) {}
        if ("get" in r || "set" in r) throw TypeError("Accessors not supported");
        return "value" in r && (t[e] = r.value), t;
      };
    }, function (t, e, r) {
      var n = r(49);

      t.exports = function (t) {
        return Object(n(t));
      };
    }, function (t, e) {
      var r;

      r = function () {
        return this;
      }();

      try {
        r = r || new Function("return this")();
      } catch (t) {
        "object" == typeof window && (r = window);
      }

      t.exports = r;
    }, function (t, e) {
      var r = {}.hasOwnProperty;

      t.exports = function (t, e) {
        return r.call(t, e);
      };
    }, function (t, e, r) {
      var n = r(4),
          i = r(142);
      t.exports = n ? i : function (t) {
        return Map.prototype.entries.call(t);
      };
    }, function (t, e, r) {
      var n = r(3),
          i = r(13),
          o = function (t) {
        return "function" == typeof t ? t : void 0;
      };

      t.exports = function (t, e) {
        return arguments.length < 2 ? o(n[t]) || o(i[t]) : n[t] && n[t][e] || i[t] && i[t][e];
      };
    }, function (t, e, r) {
      var n = r(255),
          i = r(139);

      function o(e) {
        return t.exports = o = i ? n : function (t) {
          return t.__proto__ || n(t);
        }, o(e);
      }

      t.exports = o;
    }, function (t, e, r) {
      var n = r(60),
          i = r(49);

      t.exports = function (t) {
        return n(i(t));
      };
    }, function (t, e, r) {
      var n = r(83),
          i = Math.min;

      t.exports = function (t) {
        return t > 0 ? i(n(t), 9007199254740991) : 0;
      };
    }, function (t, e, r) {
      t.exports = r(184);
    }, function (t, e, r) {
      var n = r(185);

      function i(t, e, r, i, o, a, s) {
        try {
          var u = t[a](s),
              f = u.value;
        } catch (t) {
          return void r(t);
        }

        u.done ? e(f) : n.resolve(f).then(i, o);
      }

      t.exports = function (t) {
        return function () {
          var e = this,
              r = arguments;
          return new n(function (n, o) {
            var a = t.apply(e, r);

            function s(t) {
              i(a, n, o, s, u, "next", t);
            }

            function u(t) {
              i(a, n, o, s, u, "throw", t);
            }

            s(void 0);
          });
        };
      };
    }, function (t, e, r) {
      var n = r(225),
          i = r(254);

      t.exports = function (t, e) {
        return !e || "object" !== n(e) && "function" != typeof e ? i(t) : e;
      };
    }, function (t, e, r) {
      var n = r(140),
          i = r(96);

      t.exports = function (t, e) {
        if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function");
        t.prototype = n(e && e.prototype, {
          constructor: {
            value: t,
            writable: !0,
            configurable: !0
          }
        }), e && i(t, e);
      };
    }, function (t, e) {
      var r = {}.toString;

      t.exports = function (t) {
        return r.call(t).slice(8, -1);
      };
    }, function (t, e) {
      t.exports = {};
    }, function (t, e, r) {
      var n = r(21).f,
          i = r(20),
          o = r(24),
          a = r(174),
          s = r(6)("toStringTag"),
          u = a !== {}.toString;

      t.exports = function (t, e, r, f) {
        if (t) {
          var c = r ? t : t.prototype;
          o(c, s) || n(c, s, {
            configurable: !0,
            value: e
          }), f && u && i(c, "toString", a);
        }
      };
    }, function (t, e, r) {
      t.exports = r(181);
    }, function (t, e) {
      var r,
          n,
          i = t.exports = {};

      function o() {
        throw new Error("setTimeout has not been defined");
      }

      function a() {
        throw new Error("clearTimeout has not been defined");
      }

      function s(t) {
        if (r === setTimeout) return setTimeout(t, 0);
        if ((r === o || !r) && setTimeout) return r = setTimeout, setTimeout(t, 0);

        try {
          return r(t, 0);
        } catch (e) {
          try {
            return r.call(null, t, 0);
          } catch (e) {
            return r.call(this, t, 0);
          }
        }
      }

      !function () {
        try {
          r = "function" == typeof setTimeout ? setTimeout : o;
        } catch (t) {
          r = o;
        }

        try {
          n = "function" == typeof clearTimeout ? clearTimeout : a;
        } catch (t) {
          n = a;
        }
      }();
      var u,
          f = [],
          c = !1,
          l = -1;

      function h() {
        c && u && (c = !1, u.length ? f = u.concat(f) : l = -1, f.length && d());
      }

      function d() {
        if (!c) {
          var t = s(h);
          c = !0;

          for (var e = f.length; e;) {
            for (u = f, f = []; ++l < e;) u && u[l].run();

            l = -1, e = f.length;
          }

          u = null, c = !1, function (t) {
            if (n === clearTimeout) return clearTimeout(t);
            if ((n === a || !n) && clearTimeout) return n = clearTimeout, clearTimeout(t);

            try {
              n(t);
            } catch (e) {
              try {
                return n.call(null, t);
              } catch (e) {
                return n.call(this, t);
              }
            }
          }(t);
        }
      }

      function p(t, e) {
        this.fun = t, this.array = e;
      }

      function g() {}

      i.nextTick = function (t) {
        var e = new Array(arguments.length - 1);
        if (arguments.length > 1) for (var r = 1; r < arguments.length; r++) e[r - 1] = arguments[r];
        f.push(new p(t, e)), 1 !== f.length || c || s(d);
      }, p.prototype.run = function () {
        this.fun.apply(null, this.array);
      }, i.title = "browser", i.browser = !0, i.env = {}, i.argv = [], i.version = "", i.versions = {}, i.on = g, i.addListener = g, i.once = g, i.off = g, i.removeListener = g, i.removeAllListeners = g, i.emit = g, i.prependListener = g, i.prependOnceListener = g, i.listeners = function (t) {
        return [];
      }, i.binding = function (t) {
        throw new Error("process.binding is not supported");
      }, i.cwd = function () {
        return "/";
      }, i.chdir = function (t) {
        throw new Error("process.chdir is not supported");
      }, i.umask = function () {
        return 0;
      };
    }, function (t, e, r) {

      var n = r(70),
          i = Object.keys || function (t) {
        var e = [];

        for (var r in t) e.push(r);

        return e;
      };

      t.exports = l;
      var o = r(57);
      o.inherits = r(47);
      var a = r(127),
          s = r(93);
      o.inherits(l, a);

      for (var u = i(s.prototype), f = 0; f < u.length; f++) {
        var c = u[f];
        l.prototype[c] || (l.prototype[c] = s.prototype[c]);
      }

      function l(t) {
        if (!(this instanceof l)) return new l(t);
        a.call(this, t), s.call(this, t), t && !1 === t.readable && (this.readable = !1), t && !1 === t.writable && (this.writable = !1), this.allowHalfOpen = !0, t && !1 === t.allowHalfOpen && (this.allowHalfOpen = !1), this.once("end", h);
      }

      function h() {
        this.allowHalfOpen || this._writableState.ended || n.nextTick(d, this);
      }

      function d(t) {
        t.end();
      }

      Object.defineProperty(l.prototype, "writableHighWaterMark", {
        enumerable: !1,
        get: function () {
          return this._writableState.highWaterMark;
        }
      }), Object.defineProperty(l.prototype, "destroyed", {
        get: function () {
          return void 0 !== this._readableState && void 0 !== this._writableState && this._readableState.destroyed && this._writableState.destroyed;
        },
        set: function (t) {
          void 0 !== this._readableState && void 0 !== this._writableState && (this._readableState.destroyed = t, this._writableState.destroyed = t);
        }
      }), l.prototype._destroy = function (t, e) {
        this.push(null), this.end(), n.nextTick(e, t);
      };
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(7)),
          o = n(r(10)),
          a = r(9).CramBufferOverrunError,
          s = {
        int: !0,
        byte: !0,
        long: !0,
        byteArray: !0,
        byteArrayBlock: !0
      },
          u = function () {
        function t() {
          var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
              r = arguments.length > 1 ? arguments[1] : void 0;
          if ((0, i.default)(this, t), this.parameters = e, this.dataType = r, !r) throw new TypeError("must provide a data type to codec constructor");
          if (!s[r]) throw new TypeError("invalid data type ".concat(r));
        }

        return (0, o.default)(t, [{
          key: "_getBits",
          value: function (t, e, r) {
            var n = 0;
            if (e.bytePosition + (7 - e.bitPosition + r) / 8 > t.length) throw new a("read error during decoding. the file seems to be truncated.");

            for (var i = r; i; i -= 1) n <<= 1, n |= t[e.bytePosition] >> e.bitPosition & 1, e.bitPosition -= 1, e.bitPosition < 0 && (e.bytePosition += 1), e.bitPosition &= 7;

            return n;
          }
        }]), t;
      }();

      t.exports = u;
    }, function (t, e, r) {
      r(168);
      var n = r(176),
          i = r(13),
          o = r(20),
          a = r(35),
          s = r(6)("toStringTag");

      for (var u in n) {
        var f = i[u],
            c = f && f.prototype;
        c && !c[s] && o(c, s, u), a[u] = a.Array;
      }
    }, function (t, e, r) {
      var n,
          i,
          o,
          a = r(169),
          s = r(13),
          u = r(14),
          f = r(20),
          c = r(24),
          l = r(63),
          h = r(50),
          d = s.WeakMap;

      if (a) {
        var p = new d(),
            g = p.get,
            v = p.has,
            y = p.set;
        n = function (t, e) {
          return y.call(p, t, e), e;
        }, i = function (t) {
          return g.call(p, t) || {};
        }, o = function (t) {
          return v.call(p, t);
        };
      } else {
        var m = l("state");
        h[m] = !0, n = function (t, e) {
          return f(t, m, e), e;
        }, i = function (t) {
          return c(t, m) ? t[m] : {};
        }, o = function (t) {
          return c(t, m);
        };
      }

      t.exports = {
        set: n,
        get: i,
        has: o,
        enforce: function (t) {
          return o(t) ? i(t) : n(t, {});
        },
        getterFor: function (t) {
          return function (e) {
            var r;
            if (!u(e) || (r = i(e)).type !== t) throw TypeError("Incompatible receiver, " + t + " required");
            return r;
          };
        }
      };
    }, function (t, e) {
      t.exports = function (t, e) {
        return {
          enumerable: !(1 & t),
          configurable: !(2 & t),
          writable: !(4 & t),
          value: e
        };
      };
    }, function (t, e, r) {
      var n = r(2),
          i = r(173),
          o = r(85),
          a = r(50),
          s = r(112),
          u = r(79),
          f = r(63)("IE_PROTO"),
          c = function () {},
          l = function () {
        var t,
            e = u("iframe"),
            r = o.length;

        for (e.style.display = "none", s.appendChild(e), e.src = String("javascript:"), (t = e.contentWindow.document).open(), t.write("<script>document.F=Object<\/script>"), t.close(), l = t.F; r--;) delete l.prototype[o[r]];

        return l();
      };

      t.exports = Object.create || function (t, e) {
        var r;
        return null !== t ? (c.prototype = n(t), r = new c(), c.prototype = null, r[f] = t) : r = l(), void 0 === e ? r : i(r, e);
      }, a[f] = !0;
    }, function (t, e, r) {
      var n = r(15),
          i = r(60),
          o = r(22),
          a = r(29),
          s = r(113),
          u = [].push,
          f = function (t) {
        var e = 1 == t,
            r = 2 == t,
            f = 3 == t,
            c = 4 == t,
            l = 6 == t,
            h = 5 == t || l;
        return function (d, p, g, v) {
          for (var y, m, b = o(d), _ = i(b), w = n(p, g, 3), x = a(_.length), E = 0, S = v || s, k = e ? S(d, x) : r ? S(d, 0) : void 0; x > E; E++) if ((h || E in _) && (m = w(y = _[E], E, b), t)) if (e) k[E] = m;else if (m) switch (t) {
            case 3:
              return !0;

            case 5:
              return y;

            case 6:
              return E;

            case 2:
              u.call(k, y);
          } else if (c) return !1;

          return l ? -1 : f || c ? c : k;
        };
      };

      t.exports = {
        forEach: f(0),
        map: f(1),
        filter: f(2),
        some: f(3),
        every: f(4),
        find: f(5),
        findIndex: f(6)
      };
    }, function (t, e, r) {

      var n = r(187).charAt,
          i = r(42),
          o = r(80),
          a = i.set,
          s = i.getterFor("String Iterator");
      o(String, "String", function (t) {
        a(this, {
          type: "String Iterator",
          string: String(t),
          index: 0
        });
      }, function () {
        var t,
            e = s(this),
            r = e.string,
            i = e.index;
        return i >= r.length ? {
          value: void 0,
          done: !0
        } : (t = n(r, i), e.index += t.length, {
          value: t,
          done: !1
        });
      });
    }, function (t, e) {
      "function" == typeof Object.create ? t.exports = function (t, e) {
        e && (t.super_ = e, t.prototype = Object.create(e.prototype, {
          constructor: {
            value: t,
            enumerable: !1,
            writable: !0,
            configurable: !0
          }
        }));
      } : t.exports = function (t, e) {
        if (e) {
          t.super_ = e;

          var r = function () {};

          r.prototype = e.prototype, t.prototype = new r(), t.prototype.constructor = t;
        }
      };
    }, function (t, e, r) {
      var n = r(157),
          i = r(384),
          o = r(158);

      t.exports = function (t, e) {
        return n(t) || i(t, e) || o();
      };
    }, function (t, e) {
      t.exports = function (t) {
        if (null == t) throw TypeError("Can't call method on " + t);
        return t;
      };
    }, function (t, e) {
      t.exports = {};
    }, function (t, e, r) {
      var n = r(111),
          i = r(85);

      t.exports = Object.keys || function (t) {
        return n(t, i);
      };
    }, function (t, e, r) {
      var n = r(20);

      t.exports = function (t, e, r, i) {
        i && i.enumerable ? t[e] = r : n(t, e, r);
      };
    }, function (t, e, r) {
      var n = r(34);

      t.exports = Array.isArray || function (t) {
        return "Array" == n(t);
      };
    }, function (t, e, r) {

      var n = r(12);

      t.exports = function (t, e) {
        var r = [][t];
        return !r || !n(function () {
          r.call(null, e || function () {
            throw 1;
          }, 1);
        });
      };
    }, function (t, e, r) {
      var n = r(2),
          i = r(5),
          o = r(6)("species");

      t.exports = function (t, e) {
        var r,
            a = n(t).constructor;
        return void 0 === a || null == (r = n(a)[o]) ? e : i(r);
      };
    }, function (t, e, r) {

      var n = r(5),
          i = function (t) {
        var e, r;
        this.promise = new t(function (t, n) {
          if (void 0 !== e || void 0 !== r) throw TypeError("Bad Promise constructor");
          e = t, r = n;
        }), this.resolve = n(e), this.reject = n(r);
      };

      t.exports.f = function (t) {
        return new i(t);
      };
    }, function (t, e, r) {
      (function (t) {
        function r(t) {
          return Object.prototype.toString.call(t);
        }

        e.isArray = function (t) {
          return Array.isArray ? Array.isArray(t) : "[object Array]" === r(t);
        }, e.isBoolean = function (t) {
          return "boolean" == typeof t;
        }, e.isNull = function (t) {
          return null === t;
        }, e.isNullOrUndefined = function (t) {
          return null == t;
        }, e.isNumber = function (t) {
          return "number" == typeof t;
        }, e.isString = function (t) {
          return "string" == typeof t;
        }, e.isSymbol = function (t) {
          return "symbol" == typeof t;
        }, e.isUndefined = function (t) {
          return void 0 === t;
        }, e.isRegExp = function (t) {
          return "[object RegExp]" === r(t);
        }, e.isObject = function (t) {
          return "object" == typeof t && null !== t;
        }, e.isDate = function (t) {
          return "[object Date]" === r(t);
        }, e.isError = function (t) {
          return "[object Error]" === r(t) || t instanceof Error;
        }, e.isFunction = function (t) {
          return "function" == typeof t;
        }, e.isPrimitive = function (t) {
          return null === t || "boolean" == typeof t || "number" == typeof t || "string" == typeof t || "symbol" == typeof t || void 0 === t;
        }, e.isBuffer = t.isBuffer;
      }).call(this, r(19).Buffer);
    }, function (t, e, r) {

      var n = r(1)(r(76)),
          i = r(331),
          o = r(9).CramBufferOverrunError;
      t.exports = {
        itf8Size: function (t) {
          return -128 & t ? -16384 & t ? -2097152 & t ? -268435456 & t ? 5 : 4 : 3 : 2 : 1;
        },
        parseItf8: function (t, e) {
          var r,
              n = e,
              i = t[n];
          if (i < 128 ? (r = i, n += 1) : i < 192 ? (r = 16383 & (i << 8 | t[n + 1]), n += 2) : i < 224 ? (r = 2097151 & (i << 16 | t[n + 1] << 8 | t[n + 2]), n += 3) : i < 240 ? (r = 268435455 & (i << 24 | t[n + 1] << 16 | t[n + 2] << 8 | t[n + 3]), n += 4) : (r = (15 & i) << 28 | t[n + 1] << 20 | t[n + 2] << 12 | t[n + 3] << 4 | 15 & t[n + 4], n += 5), n > t.length) throw new o("Attempted to read beyond end of buffer; this file seems truncated.");
          return [r, n - e];
        },
        parseItem: function (t, e) {
          var r = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : 0,
              n = arguments.length > 3 && void 0 !== arguments[3] ? arguments[3] : 0,
              i = e.parse(t),
              o = i.offset,
              a = i.result;
          return a._endPosition = o + n, a._size = o - r, a;
        },
        tinyMemoize: function (t, e) {
          var r = t.prototype[e],
              i = "_memo_".concat(e);

          t.prototype[e] = function () {
            var t = this;

            if (!(i in this)) {
              var e = r.call(this);
              this[i] = e, n.default.resolve(e).catch(function () {
                delete t[i];
              });
            }

            return this[i];
          };
        },
        sequenceMD5: function (t) {
          return i(t.toUpperCase().replace(/[^\x21-\x7e]/g, ""));
        }
      };
    }, function (t, e, r) {
      var n = r(351),
          i = r(152),
          o = r(362);

      t.exports = function (t) {
        return n(t) || i(t) || o();
      };
    }, function (t, e, r) {
      var n = r(12),
          i = r(34),
          o = "".split;
      t.exports = n(function () {
        return !Object("z").propertyIsEnumerable(0);
      }) ? function (t) {
        return "String" == i(t) ? o.call(t, "") : Object(t);
      } : Object;
    }, function (t, e, r) {
      var n = r(13),
          i = r(171),
          o = r(4),
          a = n["__core-js_shared__"] || i("__core-js_shared__", {});
      (t.exports = function (t, e) {
        return a[t] || (a[t] = void 0 !== e ? e : {});
      })("versions", []).push({
        version: "3.2.1",
        mode: o ? "pure" : "global",
        copyright: "© 2019 Denis Pushkarev (zloirock.ru)"
      });
    }, function (t, e, r) {
      var n = r(14);

      t.exports = function (t, e) {
        if (!n(t)) return t;
        var r, i;
        if (e && "function" == typeof (r = t.toString) && !n(i = r.call(t))) return i;
        if ("function" == typeof (r = t.valueOf) && !n(i = r.call(t))) return i;
        if (!e && "function" == typeof (r = t.toString) && !n(i = r.call(t))) return i;
        throw TypeError("Can't convert object to primitive value");
      };
    }, function (t, e, r) {
      var n = r(61),
          i = r(64),
          o = n("keys");

      t.exports = function (t) {
        return o[t] || (o[t] = i(t));
      };
    }, function (t, e) {
      var r = 0,
          n = Math.random();

      t.exports = function (t) {
        return "Symbol(" + String(void 0 === t ? "" : t) + ")_" + (++r + n).toString(36);
      };
    }, function (t, e, r) {

      var n = {}.propertyIsEnumerable,
          i = Object.getOwnPropertyDescriptor,
          o = i && !n.call({
        1: 2
      }, 1);
      e.f = o ? function (t) {
        var e = i(this, t);
        return !!e && e.enumerable;
      } : n;
    }, function (t, e, r) {
      var n = r(24),
          i = r(22),
          o = r(63),
          a = r(109),
          s = o("IE_PROTO"),
          u = Object.prototype;
      t.exports = a ? Object.getPrototypeOf : function (t) {
        return t = i(t), n(t, s) ? t[s] : "function" == typeof t.constructor && t instanceof t.constructor ? t.constructor.prototype : t instanceof Object ? u : null;
      };
    }, function (t, e, r) {
      var n = r(34),
          i = r(6)("toStringTag"),
          o = "Arguments" == n(function () {
        return arguments;
      }());

      t.exports = function (t) {
        var e, r, a;
        return void 0 === t ? "Undefined" : null === t ? "Null" : "string" == typeof (r = function (t, e) {
          try {
            return t[e];
          } catch (t) {}
        }(e = Object(t), i)) ? r : o ? n(e) : "Object" == (a = n(e)) && "function" == typeof e.callee ? "Arguments" : a;
      };
    }, function (t, e, r) {
      var n = r(12),
          i = r(6)("species");

      t.exports = function (t) {
        return !n(function () {
          var e = [];
          return (e.constructor = {})[i] = function () {
            return {
              foo: 1
            };
          }, 1 !== e[t](Boolean).foo;
        });
      };
    }, function (t, e) {
      t.exports = function (t) {
        try {
          return {
            error: !1,
            value: t()
          };
        } catch (t) {
          return {
            error: !0,
            value: t
          };
        }
      };
    }, function (t, e, r) {

      (function (e) {
        void 0 === e || !e.version || 0 === e.version.indexOf("v0.") || 0 === e.version.indexOf("v1.") && 0 !== e.version.indexOf("v1.8.") ? t.exports = {
          nextTick: function (t, r, n, i) {
            if ("function" != typeof t) throw new TypeError('"callback" argument must be a function');
            var o,
                a,
                s = arguments.length;

            switch (s) {
              case 0:
              case 1:
                return e.nextTick(t);

              case 2:
                return e.nextTick(function () {
                  t.call(null, r);
                });

              case 3:
                return e.nextTick(function () {
                  t.call(null, r, n);
                });

              case 4:
                return e.nextTick(function () {
                  t.call(null, r, n, i);
                });

              default:
                for (o = new Array(s - 1), a = 0; a < o.length;) o[a++] = arguments[a];

                return e.nextTick(function () {
                  t.apply(null, o);
                });
            }
          }
        } : t.exports = e;
      }).call(this, r(38));
    }, function (t, e, r) {
      var n = r(19),
          i = n.Buffer;

      function o(t, e) {
        for (var r in t) e[r] = t[r];
      }

      function a(t, e, r) {
        return i(t, e, r);
      }

      i.from && i.alloc && i.allocUnsafe && i.allocUnsafeSlow ? t.exports = n : (o(n, e), e.Buffer = a), o(i, a), a.from = function (t, e, r) {
        if ("number" == typeof t) throw new TypeError("Argument must not be a number");
        return i(t, e, r);
      }, a.alloc = function (t, e, r) {
        if ("number" != typeof t) throw new TypeError("Argument must be a number");
        var n = i(t);
        return void 0 !== e ? "string" == typeof r ? n.fill(e, r) : n.fill(e) : n.fill(0), n;
      }, a.allocUnsafe = function (t) {
        if ("number" != typeof t) throw new TypeError("Argument must be a number");
        return i(t);
      }, a.allocUnsafeSlow = function (t) {
        if ("number" != typeof t) throw new TypeError("Argument must be a number");
        return n.SlowBuffer(t);
      };
    }, function (t, e, r) {

      var n = "undefined" != typeof Uint8Array && "undefined" != typeof Uint16Array && "undefined" != typeof Int32Array;

      function i(t, e) {
        return Object.prototype.hasOwnProperty.call(t, e);
      }

      e.assign = function (t) {
        for (var e = Array.prototype.slice.call(arguments, 1); e.length;) {
          var r = e.shift();

          if (r) {
            if ("object" != typeof r) throw new TypeError(r + "must be non-object");

            for (var n in r) i(r, n) && (t[n] = r[n]);
          }
        }

        return t;
      }, e.shrinkBuf = function (t, e) {
        return t.length === e ? t : t.subarray ? t.subarray(0, e) : (t.length = e, t);
      };
      var o = {
        arraySet: function (t, e, r, n, i) {
          if (e.subarray && t.subarray) t.set(e.subarray(r, r + n), i);else for (var o = 0; o < n; o++) t[i + o] = e[r + o];
        },
        flattenChunks: function (t) {
          var e, r, n, i, o, a;

          for (n = 0, e = 0, r = t.length; e < r; e++) n += t[e].length;

          for (a = new Uint8Array(n), i = 0, e = 0, r = t.length; e < r; e++) o = t[e], a.set(o, i), i += o.length;

          return a;
        }
      },
          a = {
        arraySet: function (t, e, r, n, i) {
          for (var o = 0; o < n; o++) t[i + o] = e[r + o];
        },
        flattenChunks: function (t) {
          return [].concat.apply([], t);
        }
      };
      e.setTyped = function (t) {
        t ? (e.Buf8 = Uint8Array, e.Buf16 = Uint16Array, e.Buf32 = Int32Array, e.assign(e, o)) : (e.Buf8 = Array, e.Buf16 = Array, e.Buf32 = Array, e.assign(e, a));
      }, e.setTyped(n);
    }, function (t, e, r) {

      var n = r(1)(r(7)),
          i = r(9).CramMalformedError,
          o = r(74),
          a = function t() {
        (0, n.default)(this, t), this.F = void 0, this.C = void 0;
      };

      function s(t, e, r, n) {
        return r * (t >> n) + (t & (1 << n) - 1) - e;
      }

      var u = {
        FC: a,
        AriDecoder: function t() {
          (0, n.default)(this, t), this.fc = new Array(256);

          for (var e = 0; e < this.fc.length; e += 1) this.fc[e] = new a();

          this.R = null;
        },
        Symbol: function t() {
          (0, n.default)(this, t), this.start = void 0, this.freq = void 0;
        },
        symbolInit: function (t, e, r) {
          if (!(e <= 65536)) throw new i("assertion failed: start <= 1<<16");
          if (!(r <= 65536 - e)) throw new i("assertion failed: freq <= 1<<16");
          t.start = e, t.freq = r;
        },
        advanceStep: s,
        advanceSymbolStep: function (t, e, r) {
          return s(t, e.start, e.freq, r);
        },
        get: function (t, e) {
          return t & (1 << e) - 1;
        },
        advanceSymbol: function (t, e, r, n) {
          return function (t, e, r, n, i) {
            if ((t = n * (t >> i) + (t & (1 << i) - 1) - r) < o.RANS_BYTE_L) do {
              t = t << 8 | 255 & e.get();
            } while (t < o.RANS_BYTE_L);
            return t;
          }(t, e, r.start, r.freq, n);
        },
        renormalize: function (t, e) {
          if (t < o.RANS_BYTE_L) do {
            t = t << 8 | 255 & e.get();
          } while (t < o.RANS_BYTE_L);
          return t;
        }
      };
      t.exports = u;
    }, function (t, e, r) {

      t.exports = {
        TF_SHIFT: 12,
        TOTFREQ: 4096,
        RANS_BYTE_L: 1 << 23
      };
    }, function (t, e, r) {
      t.exports = r(322);
    }, function (t, e, r) {
      t.exports = r(330);
    }, function (t, e, r) {
      t.exports = r(347);
    }, function (t, e) {
      t.exports = function () {};
    }, function (t, e, r) {
      var n = r(13),
          i = r(14),
          o = n.document,
          a = i(o) && i(o.createElement);

      t.exports = function (t) {
        return a ? o.createElement(t) : {};
      };
    }, function (t, e, r) {

      var n = r(0),
          i = r(172),
          o = r(66),
          a = r(86),
          s = r(36),
          u = r(20),
          f = r(52),
          c = r(6),
          l = r(4),
          h = r(35),
          d = r(108),
          p = d.IteratorPrototype,
          g = d.BUGGY_SAFARI_ITERATORS,
          v = c("iterator"),
          y = function () {
        return this;
      };

      t.exports = function (t, e, r, c, d, m, b) {
        i(r, e, c);

        var _,
            w,
            x,
            E = function (t) {
          if (t === d && T) return T;
          if (!g && t in A) return A[t];

          switch (t) {
            case "keys":
            case "values":
            case "entries":
              return function () {
                return new r(this, t);
              };
          }

          return function () {
            return new r(this);
          };
        },
            S = e + " Iterator",
            k = !1,
            A = t.prototype,
            C = A[v] || A["@@iterator"] || d && A[d],
            T = !g && C || E(d),
            R = "Array" == e && A.entries || C;

        if (R && (_ = o(R.call(new t())), p !== Object.prototype && _.next && (l || o(_) === p || (a ? a(_, p) : "function" != typeof _[v] && u(_, v, y)), s(_, S, !0, !0), l && (h[S] = y))), "values" == d && C && "values" !== C.name && (k = !0, T = function () {
          return C.call(this);
        }), l && !b || A[v] === T || u(A, v, T), h[e] = T, d) if (w = {
          values: E("values"),
          keys: m ? T : E("keys"),
          entries: E("entries")
        }, b) for (x in w) !g && !k && x in A || f(A, x, w[x]);else n({
          target: e,
          proto: !0,
          forced: g || k
        }, w);
        return w;
      };
    }, function (t, e, r) {
      var n = r(17),
          i = r(65),
          o = r(43),
          a = r(28),
          s = r(62),
          u = r(24),
          f = r(106),
          c = Object.getOwnPropertyDescriptor;
      e.f = n ? c : function (t, e) {
        if (t = a(t), e = s(e, !0), f) try {
          return c(t, e);
        } catch (t) {}
        if (u(t, e)) return o(!i.f.call(t, e), t[e]);
      };
    }, function (t, e, r) {
      var n = r(28),
          i = r(29),
          o = r(84),
          a = function (t) {
        return function (e, r, a) {
          var s,
              u = n(e),
              f = i(u.length),
              c = o(a, f);

          if (t && r != r) {
            for (; f > c;) if ((s = u[c++]) != s) return !0;
          } else for (; f > c; c++) if ((t || c in u) && u[c] === r) return t || c || 0;

          return !t && -1;
        };
      };

      t.exports = {
        includes: a(!0),
        indexOf: a(!1)
      };
    }, function (t, e) {
      var r = Math.ceil,
          n = Math.floor;

      t.exports = function (t) {
        return isNaN(t = +t) ? 0 : (t > 0 ? n : r)(t);
      };
    }, function (t, e, r) {
      var n = r(83),
          i = Math.max,
          o = Math.min;

      t.exports = function (t, e) {
        var r = n(t);
        return r < 0 ? i(r + e, 0) : o(r, e);
      };
    }, function (t, e) {
      t.exports = ["constructor", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString", "toString", "valueOf"];
    }, function (t, e, r) {
      var n = r(2),
          i = r(175);
      t.exports = Object.setPrototypeOf || ("__proto__" in {} ? function () {
        var t,
            e = !1,
            r = {};

        try {
          (t = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__").set).call(r, []), e = r instanceof Array;
        } catch (t) {}

        return function (r, o) {
          return n(r), i(o), e ? t.call(r, o) : r.__proto__ = o, r;
        };
      }() : void 0);
    }, function (t, e, r) {

      var n = r(62),
          i = r(21),
          o = r(43);

      t.exports = function (t, e, r) {
        var a = n(e);
        a in t ? i.f(t, a, o(0, r)) : t[a] = r;
      };
    }, function (t, e) {}, function (t, e) {
      t.exports = function (t, e, r) {
        if (!(t instanceof e)) throw TypeError("Incorrect " + (r ? r + " " : "") + "invocation");
        return t;
      };
    }, function (t, e, r) {
      var n = r(67),
          i = r(35),
          o = r(6)("iterator");

      t.exports = function (t) {
        if (null != t) return t[o] || t["@@iterator"] || i[n(t)];
      };
    }, function (t, e, r) {

      var n,
          i = "object" == typeof Reflect ? Reflect : null,
          o = i && "function" == typeof i.apply ? i.apply : function (t, e, r) {
        return Function.prototype.apply.call(t, e, r);
      };
      n = i && "function" == typeof i.ownKeys ? i.ownKeys : Object.getOwnPropertySymbols ? function (t) {
        return Object.getOwnPropertyNames(t).concat(Object.getOwnPropertySymbols(t));
      } : function (t) {
        return Object.getOwnPropertyNames(t);
      };

      var a = Number.isNaN || function (t) {
        return t != t;
      };

      function s() {
        s.init.call(this);
      }

      t.exports = s, s.EventEmitter = s, s.prototype._events = void 0, s.prototype._eventsCount = 0, s.prototype._maxListeners = void 0;
      var u = 10;

      function f(t) {
        return void 0 === t._maxListeners ? s.defaultMaxListeners : t._maxListeners;
      }

      function c(t, e, r, n) {
        var i, o, a, s;
        if ("function" != typeof r) throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof r);
        if (void 0 === (o = t._events) ? (o = t._events = Object.create(null), t._eventsCount = 0) : (void 0 !== o.newListener && (t.emit("newListener", e, r.listener ? r.listener : r), o = t._events), a = o[e]), void 0 === a) a = o[e] = r, ++t._eventsCount;else if ("function" == typeof a ? a = o[e] = n ? [r, a] : [a, r] : n ? a.unshift(r) : a.push(r), (i = f(t)) > 0 && a.length > i && !a.warned) {
          a.warned = !0;
          var u = new Error("Possible EventEmitter memory leak detected. " + a.length + " " + String(e) + " listeners added. Use emitter.setMaxListeners() to increase limit");
          u.name = "MaxListenersExceededWarning", u.emitter = t, u.type = e, u.count = a.length, s = u, console && console.warn && console.warn(s);
        }
        return t;
      }

      function l() {
        for (var t = [], e = 0; e < arguments.length; e++) t.push(arguments[e]);

        this.fired || (this.target.removeListener(this.type, this.wrapFn), this.fired = !0, o(this.listener, this.target, t));
      }

      function h(t, e, r) {
        var n = {
          fired: !1,
          wrapFn: void 0,
          target: t,
          type: e,
          listener: r
        },
            i = l.bind(n);
        return i.listener = r, n.wrapFn = i, i;
      }

      function d(t, e, r) {
        var n = t._events;
        if (void 0 === n) return [];
        var i = n[e];
        return void 0 === i ? [] : "function" == typeof i ? r ? [i.listener || i] : [i] : r ? function (t) {
          for (var e = new Array(t.length), r = 0; r < e.length; ++r) e[r] = t[r].listener || t[r];

          return e;
        }(i) : g(i, i.length);
      }

      function p(t) {
        var e = this._events;

        if (void 0 !== e) {
          var r = e[t];
          if ("function" == typeof r) return 1;
          if (void 0 !== r) return r.length;
        }

        return 0;
      }

      function g(t, e) {
        for (var r = new Array(e), n = 0; n < e; ++n) r[n] = t[n];

        return r;
      }

      Object.defineProperty(s, "defaultMaxListeners", {
        enumerable: !0,
        get: function () {
          return u;
        },
        set: function (t) {
          if ("number" != typeof t || t < 0 || a(t)) throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + t + ".");
          u = t;
        }
      }), s.init = function () {
        void 0 !== this._events && this._events !== Object.getPrototypeOf(this)._events || (this._events = Object.create(null), this._eventsCount = 0), this._maxListeners = this._maxListeners || void 0;
      }, s.prototype.setMaxListeners = function (t) {
        if ("number" != typeof t || t < 0 || a(t)) throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + t + ".");
        return this._maxListeners = t, this;
      }, s.prototype.getMaxListeners = function () {
        return f(this);
      }, s.prototype.emit = function (t) {
        for (var e = [], r = 1; r < arguments.length; r++) e.push(arguments[r]);

        var n = "error" === t,
            i = this._events;
        if (void 0 !== i) n = n && void 0 === i.error;else if (!n) return !1;

        if (n) {
          var a;
          if (e.length > 0 && (a = e[0]), a instanceof Error) throw a;
          var s = new Error("Unhandled error." + (a ? " (" + a.message + ")" : ""));
          throw s.context = a, s;
        }

        var u = i[t];
        if (void 0 === u) return !1;
        if ("function" == typeof u) o(u, this, e);else {
          var f = u.length,
              c = g(u, f);

          for (r = 0; r < f; ++r) o(c[r], this, e);
        }
        return !0;
      }, s.prototype.addListener = function (t, e) {
        return c(this, t, e, !1);
      }, s.prototype.on = s.prototype.addListener, s.prototype.prependListener = function (t, e) {
        return c(this, t, e, !0);
      }, s.prototype.once = function (t, e) {
        if ("function" != typeof e) throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof e);
        return this.on(t, h(this, t, e)), this;
      }, s.prototype.prependOnceListener = function (t, e) {
        if ("function" != typeof e) throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof e);
        return this.prependListener(t, h(this, t, e)), this;
      }, s.prototype.removeListener = function (t, e) {
        var r, n, i, o, a;
        if ("function" != typeof e) throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof e);
        if (void 0 === (n = this._events)) return this;
        if (void 0 === (r = n[t])) return this;
        if (r === e || r.listener === e) 0 == --this._eventsCount ? this._events = Object.create(null) : (delete n[t], n.removeListener && this.emit("removeListener", t, r.listener || e));else if ("function" != typeof r) {
          for (i = -1, o = r.length - 1; o >= 0; o--) if (r[o] === e || r[o].listener === e) {
            a = r[o].listener, i = o;
            break;
          }

          if (i < 0) return this;
          0 === i ? r.shift() : function (t, e) {
            for (; e + 1 < t.length; e++) t[e] = t[e + 1];

            t.pop();
          }(r, i), 1 === r.length && (n[t] = r[0]), void 0 !== n.removeListener && this.emit("removeListener", t, a || e);
        }
        return this;
      }, s.prototype.off = s.prototype.removeListener, s.prototype.removeAllListeners = function (t) {
        var e, r, n;
        if (void 0 === (r = this._events)) return this;
        if (void 0 === r.removeListener) return 0 === arguments.length ? (this._events = Object.create(null), this._eventsCount = 0) : void 0 !== r[t] && (0 == --this._eventsCount ? this._events = Object.create(null) : delete r[t]), this;

        if (0 === arguments.length) {
          var i,
              o = Object.keys(r);

          for (n = 0; n < o.length; ++n) "removeListener" !== (i = o[n]) && this.removeAllListeners(i);

          return this.removeAllListeners("removeListener"), this._events = Object.create(null), this._eventsCount = 0, this;
        }

        if ("function" == typeof (e = r[t])) this.removeListener(t, e);else if (void 0 !== e) for (n = e.length - 1; n >= 0; n--) this.removeListener(t, e[n]);
        return this;
      }, s.prototype.listeners = function (t) {
        return d(this, t, !0);
      }, s.prototype.rawListeners = function (t) {
        return d(this, t, !1);
      }, s.listenerCount = function (t, e) {
        return "function" == typeof t.listenerCount ? t.listenerCount(e) : p.call(t, e);
      }, s.prototype.listenerCount = p, s.prototype.eventNames = function () {
        return this._eventsCount > 0 ? n(this._events) : [];
      };
    }, function (t, e, r) {
      (e = t.exports = r(127)).Stream = e, e.Readable = e, e.Writable = r(93), e.Duplex = r(39), e.Transform = r(131), e.PassThrough = r(207);
    }, function (t, e, r) {

      (function (e, n, i) {
        var o = r(70);

        function a(t) {
          var e = this;
          this.next = null, this.entry = null, this.finish = function () {
            !function (t, e, r) {
              var n = t.entry;
              t.entry = null;

              for (; n;) {
                var i = n.callback;
                e.pendingcb--, i(r), n = n.next;
              }

              e.corkedRequestsFree ? e.corkedRequestsFree.next = t : e.corkedRequestsFree = t;
            }(e, t);
          };
        }

        t.exports = m;
        var s,
            u = !e.browser && ["v0.10", "v0.9."].indexOf(e.version.slice(0, 5)) > -1 ? n : o.nextTick;
        m.WritableState = y;
        var f = r(57);
        f.inherits = r(47);

        var c = {
          deprecate: r(206)
        },
            l = r(128),
            h = r(71).Buffer,
            d = i.Uint8Array || function () {};

        var p,
            g = r(129);

        function v() {}

        function y(t, e) {
          s = s || r(39), t = t || {};
          var n = e instanceof s;
          this.objectMode = !!t.objectMode, n && (this.objectMode = this.objectMode || !!t.writableObjectMode);
          var i = t.highWaterMark,
              f = t.writableHighWaterMark,
              c = this.objectMode ? 16 : 16384;
          this.highWaterMark = i || 0 === i ? i : n && (f || 0 === f) ? f : c, this.highWaterMark = Math.floor(this.highWaterMark), this.finalCalled = !1, this.needDrain = !1, this.ending = !1, this.ended = !1, this.finished = !1, this.destroyed = !1;
          var l = !1 === t.decodeStrings;
          this.decodeStrings = !l, this.defaultEncoding = t.defaultEncoding || "utf8", this.length = 0, this.writing = !1, this.corked = 0, this.sync = !0, this.bufferProcessing = !1, this.onwrite = function (t) {
            !function (t, e) {
              var r = t._writableState,
                  n = r.sync,
                  i = r.writecb;
              if (function (t) {
                t.writing = !1, t.writecb = null, t.length -= t.writelen, t.writelen = 0;
              }(r), e) !function (t, e, r, n, i) {
                --e.pendingcb, r ? (o.nextTick(i, n), o.nextTick(S, t, e), t._writableState.errorEmitted = !0, t.emit("error", n)) : (i(n), t._writableState.errorEmitted = !0, t.emit("error", n), S(t, e));
              }(t, r, n, e, i);else {
                var a = x(r);
                a || r.corked || r.bufferProcessing || !r.bufferedRequest || w(t, r), n ? u(_, t, r, a, i) : _(t, r, a, i);
              }
            }(e, t);
          }, this.writecb = null, this.writelen = 0, this.bufferedRequest = null, this.lastBufferedRequest = null, this.pendingcb = 0, this.prefinished = !1, this.errorEmitted = !1, this.bufferedRequestCount = 0, this.corkedRequestsFree = new a(this);
        }

        function m(t) {
          if (s = s || r(39), !(p.call(m, this) || this instanceof s)) return new m(t);
          this._writableState = new y(t, this), this.writable = !0, t && ("function" == typeof t.write && (this._write = t.write), "function" == typeof t.writev && (this._writev = t.writev), "function" == typeof t.destroy && (this._destroy = t.destroy), "function" == typeof t.final && (this._final = t.final)), l.call(this);
        }

        function b(t, e, r, n, i, o, a) {
          e.writelen = n, e.writecb = a, e.writing = !0, e.sync = !0, r ? t._writev(i, e.onwrite) : t._write(i, o, e.onwrite), e.sync = !1;
        }

        function _(t, e, r, n) {
          r || function (t, e) {
            0 === e.length && e.needDrain && (e.needDrain = !1, t.emit("drain"));
          }(t, e), e.pendingcb--, n(), S(t, e);
        }

        function w(t, e) {
          e.bufferProcessing = !0;
          var r = e.bufferedRequest;

          if (t._writev && r && r.next) {
            var n = e.bufferedRequestCount,
                i = new Array(n),
                o = e.corkedRequestsFree;
            o.entry = r;

            for (var s = 0, u = !0; r;) i[s] = r, r.isBuf || (u = !1), r = r.next, s += 1;

            i.allBuffers = u, b(t, e, !0, e.length, i, "", o.finish), e.pendingcb++, e.lastBufferedRequest = null, o.next ? (e.corkedRequestsFree = o.next, o.next = null) : e.corkedRequestsFree = new a(e), e.bufferedRequestCount = 0;
          } else {
            for (; r;) {
              var f = r.chunk,
                  c = r.encoding,
                  l = r.callback;
              if (b(t, e, !1, e.objectMode ? 1 : f.length, f, c, l), r = r.next, e.bufferedRequestCount--, e.writing) break;
            }

            null === r && (e.lastBufferedRequest = null);
          }

          e.bufferedRequest = r, e.bufferProcessing = !1;
        }

        function x(t) {
          return t.ending && 0 === t.length && null === t.bufferedRequest && !t.finished && !t.writing;
        }

        function E(t, e) {
          t._final(function (r) {
            e.pendingcb--, r && t.emit("error", r), e.prefinished = !0, t.emit("prefinish"), S(t, e);
          });
        }

        function S(t, e) {
          var r = x(e);
          return r && (!function (t, e) {
            e.prefinished || e.finalCalled || ("function" == typeof t._final ? (e.pendingcb++, e.finalCalled = !0, o.nextTick(E, t, e)) : (e.prefinished = !0, t.emit("prefinish")));
          }(t, e), 0 === e.pendingcb && (e.finished = !0, t.emit("finish"))), r;
        }

        f.inherits(m, l), y.prototype.getBuffer = function () {
          for (var t = this.bufferedRequest, e = []; t;) e.push(t), t = t.next;

          return e;
        }, function () {
          try {
            Object.defineProperty(y.prototype, "buffer", {
              get: c.deprecate(function () {
                return this.getBuffer();
              }, "_writableState.buffer is deprecated. Use _writableState.getBuffer instead.", "DEP0003")
            });
          } catch (t) {}
        }(), "function" == typeof Symbol && Symbol.hasInstance && "function" == typeof Function.prototype[Symbol.hasInstance] ? (p = Function.prototype[Symbol.hasInstance], Object.defineProperty(m, Symbol.hasInstance, {
          value: function (t) {
            return !!p.call(this, t) || this === m && t && t._writableState instanceof y;
          }
        })) : p = function (t) {
          return t instanceof this;
        }, m.prototype.pipe = function () {
          this.emit("error", new Error("Cannot pipe, not readable"));
        }, m.prototype.write = function (t, e, r) {
          var n,
              i = this._writableState,
              a = !1,
              s = !i.objectMode && (n = t, h.isBuffer(n) || n instanceof d);
          return s && !h.isBuffer(t) && (t = function (t) {
            return h.from(t);
          }(t)), "function" == typeof e && (r = e, e = null), s ? e = "buffer" : e || (e = i.defaultEncoding), "function" != typeof r && (r = v), i.ended ? function (t, e) {
            var r = new Error("write after end");
            t.emit("error", r), o.nextTick(e, r);
          }(this, r) : (s || function (t, e, r, n) {
            var i = !0,
                a = !1;
            return null === r ? a = new TypeError("May not write null values to stream") : "string" == typeof r || void 0 === r || e.objectMode || (a = new TypeError("Invalid non-string/buffer chunk")), a && (t.emit("error", a), o.nextTick(n, a), i = !1), i;
          }(this, i, t, r)) && (i.pendingcb++, a = function (t, e, r, n, i, o) {
            if (!r) {
              var a = function (t, e, r) {
                t.objectMode || !1 === t.decodeStrings || "string" != typeof e || (e = h.from(e, r));
                return e;
              }(e, n, i);

              n !== a && (r = !0, i = "buffer", n = a);
            }

            var s = e.objectMode ? 1 : n.length;
            e.length += s;
            var u = e.length < e.highWaterMark;
            u || (e.needDrain = !0);

            if (e.writing || e.corked) {
              var f = e.lastBufferedRequest;
              e.lastBufferedRequest = {
                chunk: n,
                encoding: i,
                isBuf: r,
                callback: o,
                next: null
              }, f ? f.next = e.lastBufferedRequest : e.bufferedRequest = e.lastBufferedRequest, e.bufferedRequestCount += 1;
            } else b(t, e, !1, s, n, i, o);

            return u;
          }(this, i, s, t, e, r)), a;
        }, m.prototype.cork = function () {
          this._writableState.corked++;
        }, m.prototype.uncork = function () {
          var t = this._writableState;
          t.corked && (t.corked--, t.writing || t.corked || t.finished || t.bufferProcessing || !t.bufferedRequest || w(this, t));
        }, m.prototype.setDefaultEncoding = function (t) {
          if ("string" == typeof t && (t = t.toLowerCase()), !(["hex", "utf8", "utf-8", "ascii", "binary", "base64", "ucs2", "ucs-2", "utf16le", "utf-16le", "raw"].indexOf((t + "").toLowerCase()) > -1)) throw new TypeError("Unknown encoding: " + t);
          return this._writableState.defaultEncoding = t, this;
        }, Object.defineProperty(m.prototype, "writableHighWaterMark", {
          enumerable: !1,
          get: function () {
            return this._writableState.highWaterMark;
          }
        }), m.prototype._write = function (t, e, r) {
          r(new Error("_write() is not implemented"));
        }, m.prototype._writev = null, m.prototype.end = function (t, e, r) {
          var n = this._writableState;
          "function" == typeof t ? (r = t, t = null, e = null) : "function" == typeof e && (r = e, e = null), null != t && this.write(t, e), n.corked && (n.corked = 1, this.uncork()), n.ending || n.finished || function (t, e, r) {
            e.ending = !0, S(t, e), r && (e.finished ? o.nextTick(r) : t.once("finish", r));
            e.ended = !0, t.writable = !1;
          }(this, n, r);
        }, Object.defineProperty(m.prototype, "destroyed", {
          get: function () {
            return void 0 !== this._writableState && this._writableState.destroyed;
          },
          set: function (t) {
            this._writableState && (this._writableState.destroyed = t);
          }
        }), m.prototype.destroy = g.destroy, m.prototype._undestroy = g.undestroy, m.prototype._destroy = function (t, e) {
          this.end(), e(t);
        };
      }).call(this, r(38), r(204).setImmediate, r(23));
    }, function (t, e, r) {

      t.exports = class {
        constructor() {
          let t = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
          if (!(t.maxSize && t.maxSize > 0)) throw new TypeError("`maxSize` must be a number greater than 0");
          this.maxSize = t.maxSize, this.cache = new Map(), this.oldCache = new Map(), this._size = 0;
        }

        _set(t, e) {
          this.cache.set(t, e), this._size++, this._size >= this.maxSize && (this._size = 0, this.oldCache = this.cache, this.cache = new Map());
        }

        get(t) {
          if (this.cache.has(t)) return this.cache.get(t);

          if (this.oldCache.has(t)) {
            const e = this.oldCache.get(t);
            return this._set(t, e), e;
          }
        }

        set(t, e) {
          return this.cache.has(t) ? this.cache.set(t, e) : this._set(t, e), this;
        }

        has(t) {
          return this.cache.has(t) || this.oldCache.has(t);
        }

        peek(t) {
          return this.cache.has(t) ? this.cache.get(t) : this.oldCache.has(t) ? this.oldCache.get(t) : void 0;
        }

        delete(t) {
          const e = this.cache.delete(t);
          return e && this._size--, this.oldCache.delete(t) || e;
        }

        clear() {
          this.cache.clear(), this.oldCache.clear(), this._size = 0;
        }

        *keys() {
          for (const [t] of this) yield t;
        }

        *values() {
          for (const [, t] of this) yield t;
        }

        *[Symbol.iterator]() {
          for (const t of this.cache) yield t;

          for (const t of this.oldCache) {
            const [e] = t;
            this.cache.has(e) || (yield t);
          }
        }

        get size() {
          let t = 0;

          for (const e of this.oldCache.keys()) this.cache.has(e) || t++;

          return this._size + t;
        }

      };
    }, function (t, e, r) {
      e.f = r(6);
    }, function (t, e, r) {
      var n = r(139);

      function i(e, r) {
        return t.exports = i = n || function (t, e) {
          return t.__proto__ = e, t;
        }, i(e, r);
      }

      t.exports = i;
    }, function (t, e, r) {
      t.exports = r(310);
    }, function (t, e, r) {
      t.exports = r(319);
    }, function (t, e, r) {
      t.exports = r(342);
    }, function (t, e, r) {

      t.exports = {
        CRAM_FLAG_PRESERVE_QUAL_SCORES: 1,
        CRAM_FLAG_DETACHED: 2,
        CRAM_FLAG_MATE_DOWNSTREAM: 4,
        CRAM_FLAG_NO_SEQ: 8,
        CRAM_FLAG_MASK: 15,
        CRAM_M_REVERSE: 1,
        CRAM_M_UNMAP: 2,
        BAM_FPAIRED: 1,
        BAM_FPROPER_PAIR: 2,
        BAM_FUNMAP: 4,
        BAM_FMUNMAP: 8,
        BAM_FREVERSE: 16,
        BAM_FMREVERSE: 32,
        BAM_FREAD1: 64,
        BAM_FREAD2: 128,
        BAM_FSECONDARY: 256,
        BAM_FQCFAIL: 512,
        BAM_FDUP: 1024,
        BAM_FSUPPLEMENTARY: 2048,
        BAM_CMATCH: 0,
        BAM_CINS: 1,
        BAM_CDEL: 2,
        BAM_CREF_SKIP: 3,
        BAM_CSOFT_CLIP: 4,
        BAM_CHARD_CLIP: 5,
        BAM_CPAD: 6,
        BAM_CEQUAL: 7,
        BAM_CDIFF: 8,
        BAM_CBACK: 9,
        BAM_CIGAR_STR: "MIDNSHP:XB",
        BAM_CIGAR_SHIFT: 4,
        BAM_CIGAR_MASK: 15,
        BAM_CIGAR_TYPE: 246183
      };
    }, function (t, e, r) {
      t.exports = r(368);
    }, function (t, e, r) {
      t.exports = r(396);
    }, function (t, e, r) {
      t.exports = r(399);
    }, function (t, e, r) {

      var n = r(164);
      t.exports = n;
    }, function (t, e) {
      var r = {}.toString;

      t.exports = Array.isArray || function (t) {
        return "[object Array]" == r.call(t);
      };
    }, function (t, e, r) {
      var n = r(17),
          i = r(12),
          o = r(79);
      t.exports = !n && !i(function () {
        return 7 != Object.defineProperty(o("div"), "a", {
          get: function () {
            return 7;
          }
        }).a;
      });
    }, function (t, e, r) {
      var n = r(12),
          i = /#|\.prototype\./,
          o = function (t, e) {
        var r = s[a(t)];
        return r == f || r != u && ("function" == typeof e ? n(e) : !!e);
      },
          a = o.normalize = function (t) {
        return String(t).replace(i, ".").toLowerCase();
      },
          s = o.data = {},
          u = o.NATIVE = "N",
          f = o.POLYFILL = "P";

      t.exports = o;
    }, function (t, e, r) {

      var n,
          i,
          o,
          a = r(66),
          s = r(20),
          u = r(24),
          f = r(6),
          c = r(4),
          l = f("iterator"),
          h = !1;
      [].keys && ("next" in (o = [].keys()) ? (i = a(a(o))) !== Object.prototype && (n = i) : h = !0), null == n && (n = {}), c || u(n, l) || s(n, l, function () {
        return this;
      }), t.exports = {
        IteratorPrototype: n,
        BUGGY_SAFARI_ITERATORS: h
      };
    }, function (t, e, r) {
      var n = r(12);
      t.exports = !n(function () {
        function t() {}

        return t.prototype.constructor = null, Object.getPrototypeOf(new t()) !== t.prototype;
      });
    }, function (t, e, r) {
      var n = r(12);
      t.exports = !!Object.getOwnPropertySymbols && !n(function () {
        return !String(Symbol());
      });
    }, function (t, e, r) {
      var n = r(24),
          i = r(28),
          o = r(82).indexOf,
          a = r(50);

      t.exports = function (t, e) {
        var r,
            s = i(t),
            u = 0,
            f = [];

        for (r in s) !n(a, r) && n(s, r) && f.push(r);

        for (; e.length > u;) n(s, r = e[u++]) && (~o(f, r) || f.push(r));

        return f;
      };
    }, function (t, e, r) {
      var n = r(26);
      t.exports = n("document", "documentElement");
    }, function (t, e, r) {
      var n = r(14),
          i = r(53),
          o = r(6)("species");

      t.exports = function (t, e) {
        var r;
        return i(t) && ("function" != typeof (r = t.constructor) || r !== Array && !i(r.prototype) ? n(r) && null === (r = r[o]) && (r = void 0) : r = void 0), new (void 0 === r ? Array : r)(0 === e ? 0 : e);
      };
    }, function (t, e, r) {

      var n = r(0),
          i = r(12),
          o = r(53),
          a = r(14),
          s = r(22),
          u = r(29),
          f = r(87),
          c = r(113),
          l = r(68),
          h = r(6)("isConcatSpreadable"),
          d = !i(function () {
        var t = [];
        return t[h] = !1, t.concat()[0] !== t;
      }),
          p = l("concat"),
          g = function (t) {
        if (!a(t)) return !1;
        var e = t[h];
        return void 0 !== e ? !!e : o(t);
      };

      n({
        target: "Array",
        proto: !0,
        forced: !d || !p
      }, {
        concat: function (t) {
          var e,
              r,
              n,
              i,
              o,
              a = s(this),
              l = c(a, 0),
              h = 0;

          for (e = -1, n = arguments.length; e < n; e++) if (o = -1 === e ? a : arguments[e], g(o)) {
            if (h + (i = u(o.length)) > 9007199254740991) throw TypeError("Maximum allowed index exceeded");

            for (r = 0; r < i; r++, h++) r in o && f(l, h, o[r]);
          } else {
            if (h >= 9007199254740991) throw TypeError("Maximum allowed index exceeded");
            f(l, h++, o);
          }

          return l.length = h, l;
        }
      });
    }, function (t, e, r) {
      r(88), r(46), r(41), r(188), r(125), r(191);
      var n = r(3);
      t.exports = n.Promise;
    }, function (t, e, r) {
      var n = r(13);
      t.exports = n.Promise;
    }, function (t, e, r) {
      var n = r(52);

      t.exports = function (t, e, r) {
        for (var i in e) r && r.unsafe && t[i] ? t[i] = e[i] : n(t, i, e[i], r);

        return t;
      };
    }, function (t, e, r) {

      var n = r(26),
          i = r(21),
          o = r(6),
          a = r(17),
          s = o("species");

      t.exports = function (t) {
        var e = n(t),
            r = i.f;
        a && e && !e[s] && r(e, s, {
          configurable: !0,
          get: function () {
            return this;
          }
        });
      };
    }, function (t, e, r) {
      var n = r(6),
          i = r(35),
          o = n("iterator"),
          a = Array.prototype;

      t.exports = function (t) {
        return void 0 !== t && (i.Array === t || a[o] === t);
      };
    }, function (t, e, r) {
      var n = r(2);

      t.exports = function (t, e, r, i) {
        try {
          return i ? e(n(r)[0], r[1]) : e(r);
        } catch (e) {
          var o = t.return;
          throw void 0 !== o && n(o.call(t)), e;
        }
      };
    }, function (t, e, r) {
      var n = r(6)("iterator"),
          i = !1;

      try {
        var o = 0,
            a = {
          next: function () {
            return {
              done: !!o++
            };
          },
          return: function () {
            i = !0;
          }
        };
        a[n] = function () {
          return this;
        }, Array.from(a, function () {
          throw 2;
        });
      } catch (t) {}

      t.exports = function (t, e) {
        if (!e && !i) return !1;
        var r = !1;

        try {
          var o = {};
          o[n] = function () {
            return {
              next: function () {
                return {
                  done: r = !0
                };
              }
            };
          }, t(o);
        } catch (t) {}

        return r;
      };
    }, function (t, e, r) {
      var n,
          i,
          o,
          a = r(13),
          s = r(12),
          u = r(34),
          f = r(15),
          c = r(112),
          l = r(79),
          h = a.location,
          d = a.setImmediate,
          p = a.clearImmediate,
          g = a.process,
          v = a.MessageChannel,
          y = a.Dispatch,
          m = 0,
          b = {},
          _ = function (t) {
        if (b.hasOwnProperty(t)) {
          var e = b[t];
          delete b[t], e();
        }
      },
          w = function (t) {
        return function () {
          _(t);
        };
      },
          x = function (t) {
        _(t.data);
      },
          E = function (t) {
        a.postMessage(t + "", h.protocol + "//" + h.host);
      };

      d && p || (d = function (t) {
        for (var e = [], r = 1; arguments.length > r;) e.push(arguments[r++]);

        return b[++m] = function () {
          ("function" == typeof t ? t : Function(t)).apply(void 0, e);
        }, n(m), m;
      }, p = function (t) {
        delete b[t];
      }, "process" == u(g) ? n = function (t) {
        g.nextTick(w(t));
      } : y && y.now ? n = function (t) {
        y.now(w(t));
      } : v ? (o = (i = new v()).port2, i.port1.onmessage = x, n = f(o.postMessage, o, 1)) : !a.addEventListener || "function" != typeof postMessage || a.importScripts || s(E) ? n = "onreadystatechange" in l("script") ? function (t) {
        c.appendChild(l("script")).onreadystatechange = function () {
          c.removeChild(this), _(t);
        };
      } : function (t) {
        setTimeout(w(t), 0);
      } : (n = E, a.addEventListener("message", x, !1))), t.exports = {
        set: d,
        clear: p
      };
    }, function (t, e, r) {
      var n = r(26);
      t.exports = n("navigator", "userAgent") || "";
    }, function (t, e, r) {
      var n = r(2),
          i = r(14),
          o = r(56);

      t.exports = function (t, e) {
        if (n(t), i(e) && e.constructor === t) return e;
        var r = o.f(t);
        return (0, r.resolve)(e), r.promise;
      };
    }, function (t, e, r) {

      var n = r(0),
          i = r(5),
          o = r(56),
          a = r(69),
          s = r(8);
      n({
        target: "Promise",
        stat: !0
      }, {
        allSettled: function (t) {
          var e = this,
              r = o.f(e),
              n = r.resolve,
              u = r.reject,
              f = a(function () {
            var r = i(e.resolve),
                o = [],
                a = 0,
                u = 1;
            s(t, function (t) {
              var i = a++,
                  s = !1;
              o.push(void 0), u++, r.call(e, t).then(function (t) {
                s || (s = !0, o[i] = {
                  status: "fulfilled",
                  value: t
                }, --u || n(o));
              }, function (t) {
                s || (s = !0, o[i] = {
                  status: "rejected",
                  reason: t
                }, --u || n(o));
              });
            }), --u || n(o);
          });
          return f.error && u(f.value), r.promise;
        }
      });
    }, function (t, e, r) {

      (function (t) {
        var n = r(19).Buffer,
            i = r(200).Transform,
            o = r(212),
            a = r(133),
            s = r(132).ok,
            u = r(19).kMaxLength,
            f = "Cannot create final Buffer. It would be larger than 0x" + u.toString(16) + " bytes";
        o.Z_MIN_WINDOWBITS = 8, o.Z_MAX_WINDOWBITS = 15, o.Z_DEFAULT_WINDOWBITS = 15, o.Z_MIN_CHUNK = 64, o.Z_MAX_CHUNK = 1 / 0, o.Z_DEFAULT_CHUNK = 16384, o.Z_MIN_MEMLEVEL = 1, o.Z_MAX_MEMLEVEL = 9, o.Z_DEFAULT_MEMLEVEL = 8, o.Z_MIN_LEVEL = -1, o.Z_MAX_LEVEL = 9, o.Z_DEFAULT_LEVEL = o.Z_DEFAULT_COMPRESSION;

        for (var c = Object.keys(o), l = 0; l < c.length; l++) {
          var h = c[l];
          h.match(/^Z/) && Object.defineProperty(e, h, {
            enumerable: !0,
            value: o[h],
            writable: !1
          });
        }

        for (var d = {
          Z_OK: o.Z_OK,
          Z_STREAM_END: o.Z_STREAM_END,
          Z_NEED_DICT: o.Z_NEED_DICT,
          Z_ERRNO: o.Z_ERRNO,
          Z_STREAM_ERROR: o.Z_STREAM_ERROR,
          Z_DATA_ERROR: o.Z_DATA_ERROR,
          Z_MEM_ERROR: o.Z_MEM_ERROR,
          Z_BUF_ERROR: o.Z_BUF_ERROR,
          Z_VERSION_ERROR: o.Z_VERSION_ERROR
        }, p = Object.keys(d), g = 0; g < p.length; g++) {
          var v = p[g];
          d[d[v]] = v;
        }

        function y(t, e, r) {
          var i = [],
              o = 0;

          function a() {
            for (var e; null !== (e = t.read());) i.push(e), o += e.length;

            t.once("readable", a);
          }

          function s() {
            var e,
                a = null;
            o >= u ? a = new RangeError(f) : e = n.concat(i, o), i = [], t.close(), r(a, e);
          }

          t.on("error", function (e) {
            t.removeListener("end", s), t.removeListener("readable", a), r(e);
          }), t.on("end", s), t.end(e), a();
        }

        function m(t, e) {
          if ("string" == typeof e && (e = n.from(e)), !n.isBuffer(e)) throw new TypeError("Not a string or buffer");
          var r = t._finishFlushFlag;
          return t._processChunk(e, r);
        }

        function b(t) {
          if (!(this instanceof b)) return new b(t);
          C.call(this, t, o.DEFLATE);
        }

        function _(t) {
          if (!(this instanceof _)) return new _(t);
          C.call(this, t, o.INFLATE);
        }

        function w(t) {
          if (!(this instanceof w)) return new w(t);
          C.call(this, t, o.GZIP);
        }

        function x(t) {
          if (!(this instanceof x)) return new x(t);
          C.call(this, t, o.GUNZIP);
        }

        function E(t) {
          if (!(this instanceof E)) return new E(t);
          C.call(this, t, o.DEFLATERAW);
        }

        function S(t) {
          if (!(this instanceof S)) return new S(t);
          C.call(this, t, o.INFLATERAW);
        }

        function k(t) {
          if (!(this instanceof k)) return new k(t);
          C.call(this, t, o.UNZIP);
        }

        function A(t) {
          return t === o.Z_NO_FLUSH || t === o.Z_PARTIAL_FLUSH || t === o.Z_SYNC_FLUSH || t === o.Z_FULL_FLUSH || t === o.Z_FINISH || t === o.Z_BLOCK;
        }

        function C(t, r) {
          var a = this;
          if (this._opts = t = t || {}, this._chunkSize = t.chunkSize || e.Z_DEFAULT_CHUNK, i.call(this, t), t.flush && !A(t.flush)) throw new Error("Invalid flush flag: " + t.flush);
          if (t.finishFlush && !A(t.finishFlush)) throw new Error("Invalid flush flag: " + t.finishFlush);
          if (this._flushFlag = t.flush || o.Z_NO_FLUSH, this._finishFlushFlag = void 0 !== t.finishFlush ? t.finishFlush : o.Z_FINISH, t.chunkSize && (t.chunkSize < e.Z_MIN_CHUNK || t.chunkSize > e.Z_MAX_CHUNK)) throw new Error("Invalid chunk size: " + t.chunkSize);
          if (t.windowBits && (t.windowBits < e.Z_MIN_WINDOWBITS || t.windowBits > e.Z_MAX_WINDOWBITS)) throw new Error("Invalid windowBits: " + t.windowBits);
          if (t.level && (t.level < e.Z_MIN_LEVEL || t.level > e.Z_MAX_LEVEL)) throw new Error("Invalid compression level: " + t.level);
          if (t.memLevel && (t.memLevel < e.Z_MIN_MEMLEVEL || t.memLevel > e.Z_MAX_MEMLEVEL)) throw new Error("Invalid memLevel: " + t.memLevel);
          if (t.strategy && t.strategy != e.Z_FILTERED && t.strategy != e.Z_HUFFMAN_ONLY && t.strategy != e.Z_RLE && t.strategy != e.Z_FIXED && t.strategy != e.Z_DEFAULT_STRATEGY) throw new Error("Invalid strategy: " + t.strategy);
          if (t.dictionary && !n.isBuffer(t.dictionary)) throw new Error("Invalid dictionary: it should be a Buffer instance");
          this._handle = new o.Zlib(r);
          var s = this;
          this._hadError = !1, this._handle.onerror = function (t, r) {
            T(s), s._hadError = !0;
            var n = new Error(t);
            n.errno = r, n.code = e.codes[r], s.emit("error", n);
          };
          var u = e.Z_DEFAULT_COMPRESSION;
          "number" == typeof t.level && (u = t.level);
          var f = e.Z_DEFAULT_STRATEGY;
          "number" == typeof t.strategy && (f = t.strategy), this._handle.init(t.windowBits || e.Z_DEFAULT_WINDOWBITS, u, t.memLevel || e.Z_DEFAULT_MEMLEVEL, f, t.dictionary), this._buffer = n.allocUnsafe(this._chunkSize), this._offset = 0, this._level = u, this._strategy = f, this.once("end", this.close), Object.defineProperty(this, "_closed", {
            get: function () {
              return !a._handle;
            },
            configurable: !0,
            enumerable: !0
          });
        }

        function T(e, r) {
          r && t.nextTick(r), e._handle && (e._handle.close(), e._handle = null);
        }

        function R(t) {
          t.emit("close");
        }

        Object.defineProperty(e, "codes", {
          enumerable: !0,
          value: Object.freeze(d),
          writable: !1
        }), e.Deflate = b, e.Inflate = _, e.Gzip = w, e.Gunzip = x, e.DeflateRaw = E, e.InflateRaw = S, e.Unzip = k, e.createDeflate = function (t) {
          return new b(t);
        }, e.createInflate = function (t) {
          return new _(t);
        }, e.createDeflateRaw = function (t) {
          return new E(t);
        }, e.createInflateRaw = function (t) {
          return new S(t);
        }, e.createGzip = function (t) {
          return new w(t);
        }, e.createGunzip = function (t) {
          return new x(t);
        }, e.createUnzip = function (t) {
          return new k(t);
        }, e.deflate = function (t, e, r) {
          return "function" == typeof e && (r = e, e = {}), y(new b(e), t, r);
        }, e.deflateSync = function (t, e) {
          return m(new b(e), t);
        }, e.gzip = function (t, e, r) {
          return "function" == typeof e && (r = e, e = {}), y(new w(e), t, r);
        }, e.gzipSync = function (t, e) {
          return m(new w(e), t);
        }, e.deflateRaw = function (t, e, r) {
          return "function" == typeof e && (r = e, e = {}), y(new E(e), t, r);
        }, e.deflateRawSync = function (t, e) {
          return m(new E(e), t);
        }, e.unzip = function (t, e, r) {
          return "function" == typeof e && (r = e, e = {}), y(new k(e), t, r);
        }, e.unzipSync = function (t, e) {
          return m(new k(e), t);
        }, e.inflate = function (t, e, r) {
          return "function" == typeof e && (r = e, e = {}), y(new _(e), t, r);
        }, e.inflateSync = function (t, e) {
          return m(new _(e), t);
        }, e.gunzip = function (t, e, r) {
          return "function" == typeof e && (r = e, e = {}), y(new x(e), t, r);
        }, e.gunzipSync = function (t, e) {
          return m(new x(e), t);
        }, e.inflateRaw = function (t, e, r) {
          return "function" == typeof e && (r = e, e = {}), y(new S(e), t, r);
        }, e.inflateRawSync = function (t, e) {
          return m(new S(e), t);
        }, a.inherits(C, i), C.prototype.params = function (r, n, i) {
          if (r < e.Z_MIN_LEVEL || r > e.Z_MAX_LEVEL) throw new RangeError("Invalid compression level: " + r);
          if (n != e.Z_FILTERED && n != e.Z_HUFFMAN_ONLY && n != e.Z_RLE && n != e.Z_FIXED && n != e.Z_DEFAULT_STRATEGY) throw new TypeError("Invalid strategy: " + n);

          if (this._level !== r || this._strategy !== n) {
            var a = this;
            this.flush(o.Z_SYNC_FLUSH, function () {
              s(a._handle, "zlib binding closed"), a._handle.params(r, n), a._hadError || (a._level = r, a._strategy = n, i && i());
            });
          } else t.nextTick(i);
        }, C.prototype.reset = function () {
          return s(this._handle, "zlib binding closed"), this._handle.reset();
        }, C.prototype._flush = function (t) {
          this._transform(n.alloc(0), "", t);
        }, C.prototype.flush = function (e, r) {
          var i = this,
              a = this._writableState;
          ("function" == typeof e || void 0 === e && !r) && (r = e, e = o.Z_FULL_FLUSH), a.ended ? r && t.nextTick(r) : a.ending ? r && this.once("end", r) : a.needDrain ? r && this.once("drain", function () {
            return i.flush(e, r);
          }) : (this._flushFlag = e, this.write(n.alloc(0), "", r));
        }, C.prototype.close = function (e) {
          T(this, e), t.nextTick(R, this);
        }, C.prototype._transform = function (t, e, r) {
          var i,
              a = this._writableState,
              s = (a.ending || a.ended) && (!t || a.length === t.length);
          return null === t || n.isBuffer(t) ? this._handle ? (s ? i = this._finishFlushFlag : (i = this._flushFlag, t.length >= a.length && (this._flushFlag = this._opts.flush || o.Z_NO_FLUSH)), void this._processChunk(t, i, r)) : r(new Error("zlib binding closed")) : r(new Error("invalid input"));
        }, C.prototype._processChunk = function (t, e, r) {
          var i = t && t.length,
              o = this._chunkSize - this._offset,
              a = 0,
              c = this,
              l = "function" == typeof r;

          if (!l) {
            var h,
                d = [],
                p = 0;
            this.on("error", function (t) {
              h = t;
            }), s(this._handle, "zlib binding closed");

            do {
              var g = this._handle.writeSync(e, t, a, i, this._buffer, this._offset, o);
            } while (!this._hadError && m(g[0], g[1]));

            if (this._hadError) throw h;
            if (p >= u) throw T(this), new RangeError(f);
            var v = n.concat(d, p);
            return T(this), v;
          }

          s(this._handle, "zlib binding closed");

          var y = this._handle.write(e, t, a, i, this._buffer, this._offset, o);

          function m(u, f) {
            if (this && (this.buffer = null, this.callback = null), !c._hadError) {
              var h = o - f;

              if (s(h >= 0, "have should not go down"), h > 0) {
                var g = c._buffer.slice(c._offset, c._offset + h);

                c._offset += h, l ? c.push(g) : (d.push(g), p += g.length);
              }

              if ((0 === f || c._offset >= c._chunkSize) && (o = c._chunkSize, c._offset = 0, c._buffer = n.allocUnsafe(c._chunkSize)), 0 === f) {
                if (a += i - u, i = u, !l) return !0;

                var v = c._handle.write(e, t, a, i, c._buffer, c._offset, c._chunkSize);

                return v.callback = m, void (v.buffer = t);
              }

              if (!l) return !1;
              r();
            }
          }

          y.buffer = t, y.callback = m;
        }, a.inherits(b, C), a.inherits(_, C), a.inherits(w, C), a.inherits(x, C), a.inherits(E, C), a.inherits(S, C), a.inherits(k, C);
      }).call(this, r(38));
    }, function (t, e, r) {

      (function (e, n) {
        var i = r(70);
        t.exports = b;
        var o,
            a = r(105);
        b.ReadableState = m;
        r(91).EventEmitter;

        var s = function (t, e) {
          return t.listeners(e).length;
        },
            u = r(128),
            f = r(71).Buffer,
            c = e.Uint8Array || function () {};

        var l = r(57);
        l.inherits = r(47);
        var h = r(201),
            d = void 0;
        d = h && h.debuglog ? h.debuglog("stream") : function () {};
        var p,
            g = r(202),
            v = r(129);
        l.inherits(b, u);
        var y = ["error", "close", "destroy", "pause", "resume"];

        function m(t, e) {
          t = t || {};
          var n = e instanceof (o = o || r(39));
          this.objectMode = !!t.objectMode, n && (this.objectMode = this.objectMode || !!t.readableObjectMode);
          var i = t.highWaterMark,
              a = t.readableHighWaterMark,
              s = this.objectMode ? 16 : 16384;
          this.highWaterMark = i || 0 === i ? i : n && (a || 0 === a) ? a : s, this.highWaterMark = Math.floor(this.highWaterMark), this.buffer = new g(), this.length = 0, this.pipes = null, this.pipesCount = 0, this.flowing = null, this.ended = !1, this.endEmitted = !1, this.reading = !1, this.sync = !0, this.needReadable = !1, this.emittedReadable = !1, this.readableListening = !1, this.resumeScheduled = !1, this.destroyed = !1, this.defaultEncoding = t.defaultEncoding || "utf8", this.awaitDrain = 0, this.readingMore = !1, this.decoder = null, this.encoding = null, t.encoding && (p || (p = r(130).StringDecoder), this.decoder = new p(t.encoding), this.encoding = t.encoding);
        }

        function b(t) {
          if (o = o || r(39), !(this instanceof b)) return new b(t);
          this._readableState = new m(t, this), this.readable = !0, t && ("function" == typeof t.read && (this._read = t.read), "function" == typeof t.destroy && (this._destroy = t.destroy)), u.call(this);
        }

        function _(t, e, r, n, i) {
          var o,
              a = t._readableState;
          null === e ? (a.reading = !1, function (t, e) {
            if (e.ended) return;

            if (e.decoder) {
              var r = e.decoder.end();
              r && r.length && (e.buffer.push(r), e.length += e.objectMode ? 1 : r.length);
            }

            e.ended = !0, S(t);
          }(t, a)) : (i || (o = function (t, e) {
            var r;
            n = e, f.isBuffer(n) || n instanceof c || "string" == typeof e || void 0 === e || t.objectMode || (r = new TypeError("Invalid non-string/buffer chunk"));
            var n;
            return r;
          }(a, e)), o ? t.emit("error", o) : a.objectMode || e && e.length > 0 ? ("string" == typeof e || a.objectMode || Object.getPrototypeOf(e) === f.prototype || (e = function (t) {
            return f.from(t);
          }(e)), n ? a.endEmitted ? t.emit("error", new Error("stream.unshift() after end event")) : w(t, a, e, !0) : a.ended ? t.emit("error", new Error("stream.push() after EOF")) : (a.reading = !1, a.decoder && !r ? (e = a.decoder.write(e), a.objectMode || 0 !== e.length ? w(t, a, e, !1) : A(t, a)) : w(t, a, e, !1))) : n || (a.reading = !1));
          return function (t) {
            return !t.ended && (t.needReadable || t.length < t.highWaterMark || 0 === t.length);
          }(a);
        }

        function w(t, e, r, n) {
          e.flowing && 0 === e.length && !e.sync ? (t.emit("data", r), t.read(0)) : (e.length += e.objectMode ? 1 : r.length, n ? e.buffer.unshift(r) : e.buffer.push(r), e.needReadable && S(t)), A(t, e);
        }

        Object.defineProperty(b.prototype, "destroyed", {
          get: function () {
            return void 0 !== this._readableState && this._readableState.destroyed;
          },
          set: function (t) {
            this._readableState && (this._readableState.destroyed = t);
          }
        }), b.prototype.destroy = v.destroy, b.prototype._undestroy = v.undestroy, b.prototype._destroy = function (t, e) {
          this.push(null), e(t);
        }, b.prototype.push = function (t, e) {
          var r,
              n = this._readableState;
          return n.objectMode ? r = !0 : "string" == typeof t && ((e = e || n.defaultEncoding) !== n.encoding && (t = f.from(t, e), e = ""), r = !0), _(this, t, e, !1, r);
        }, b.prototype.unshift = function (t) {
          return _(this, t, null, !0, !1);
        }, b.prototype.isPaused = function () {
          return !1 === this._readableState.flowing;
        }, b.prototype.setEncoding = function (t) {
          return p || (p = r(130).StringDecoder), this._readableState.decoder = new p(t), this._readableState.encoding = t, this;
        };
        var x = 8388608;

        function E(t, e) {
          return t <= 0 || 0 === e.length && e.ended ? 0 : e.objectMode ? 1 : t != t ? e.flowing && e.length ? e.buffer.head.data.length : e.length : (t > e.highWaterMark && (e.highWaterMark = function (t) {
            return t >= x ? t = x : (t--, t |= t >>> 1, t |= t >>> 2, t |= t >>> 4, t |= t >>> 8, t |= t >>> 16, t++), t;
          }(t)), t <= e.length ? t : e.ended ? e.length : (e.needReadable = !0, 0));
        }

        function S(t) {
          var e = t._readableState;
          e.needReadable = !1, e.emittedReadable || (d("emitReadable", e.flowing), e.emittedReadable = !0, e.sync ? i.nextTick(k, t) : k(t));
        }

        function k(t) {
          d("emit readable"), t.emit("readable"), O(t);
        }

        function A(t, e) {
          e.readingMore || (e.readingMore = !0, i.nextTick(C, t, e));
        }

        function C(t, e) {
          for (var r = e.length; !e.reading && !e.flowing && !e.ended && e.length < e.highWaterMark && (d("maybeReadMore read 0"), t.read(0), r !== e.length);) r = e.length;

          e.readingMore = !1;
        }

        function T(t) {
          d("readable nexttick read 0"), t.read(0);
        }

        function R(t, e) {
          e.reading || (d("resume read 0"), t.read(0)), e.resumeScheduled = !1, e.awaitDrain = 0, t.emit("resume"), O(t), e.flowing && !e.reading && t.read(0);
        }

        function O(t) {
          var e = t._readableState;

          for (d("flow", e.flowing); e.flowing && null !== t.read(););
        }

        function I(t, e) {
          return 0 === e.length ? null : (e.objectMode ? r = e.buffer.shift() : !t || t >= e.length ? (r = e.decoder ? e.buffer.join("") : 1 === e.buffer.length ? e.buffer.head.data : e.buffer.concat(e.length), e.buffer.clear()) : r = function (t, e, r) {
            var n;
            t < e.head.data.length ? (n = e.head.data.slice(0, t), e.head.data = e.head.data.slice(t)) : n = t === e.head.data.length ? e.shift() : r ? function (t, e) {
              var r = e.head,
                  n = 1,
                  i = r.data;
              t -= i.length;

              for (; r = r.next;) {
                var o = r.data,
                    a = t > o.length ? o.length : t;

                if (a === o.length ? i += o : i += o.slice(0, t), 0 === (t -= a)) {
                  a === o.length ? (++n, r.next ? e.head = r.next : e.head = e.tail = null) : (e.head = r, r.data = o.slice(a));
                  break;
                }

                ++n;
              }

              return e.length -= n, i;
            }(t, e) : function (t, e) {
              var r = f.allocUnsafe(t),
                  n = e.head,
                  i = 1;
              n.data.copy(r), t -= n.data.length;

              for (; n = n.next;) {
                var o = n.data,
                    a = t > o.length ? o.length : t;

                if (o.copy(r, r.length - t, 0, a), 0 === (t -= a)) {
                  a === o.length ? (++i, n.next ? e.head = n.next : e.head = e.tail = null) : (e.head = n, n.data = o.slice(a));
                  break;
                }

                ++i;
              }

              return e.length -= i, r;
            }(t, e);
            return n;
          }(t, e.buffer, e.decoder), r);
          var r;
        }

        function L(t) {
          var e = t._readableState;
          if (e.length > 0) throw new Error('"endReadable()" called on non-empty stream');
          e.endEmitted || (e.ended = !0, i.nextTick(B, e, t));
        }

        function B(t, e) {
          t.endEmitted || 0 !== t.length || (t.endEmitted = !0, e.readable = !1, e.emit("end"));
        }

        function P(t, e) {
          for (var r = 0, n = t.length; r < n; r++) if (t[r] === e) return r;

          return -1;
        }

        b.prototype.read = function (t) {
          d("read", t), t = parseInt(t, 10);
          var e = this._readableState,
              r = t;
          if (0 !== t && (e.emittedReadable = !1), 0 === t && e.needReadable && (e.length >= e.highWaterMark || e.ended)) return d("read: emitReadable", e.length, e.ended), 0 === e.length && e.ended ? L(this) : S(this), null;
          if (0 === (t = E(t, e)) && e.ended) return 0 === e.length && L(this), null;
          var n,
              i = e.needReadable;
          return d("need readable", i), (0 === e.length || e.length - t < e.highWaterMark) && d("length less than watermark", i = !0), e.ended || e.reading ? d("reading or ended", i = !1) : i && (d("do read"), e.reading = !0, e.sync = !0, 0 === e.length && (e.needReadable = !0), this._read(e.highWaterMark), e.sync = !1, e.reading || (t = E(r, e))), null === (n = t > 0 ? I(t, e) : null) ? (e.needReadable = !0, t = 0) : e.length -= t, 0 === e.length && (e.ended || (e.needReadable = !0), r !== t && e.ended && L(this)), null !== n && this.emit("data", n), n;
        }, b.prototype._read = function (t) {
          this.emit("error", new Error("_read() is not implemented"));
        }, b.prototype.pipe = function (t, e) {
          var r = this,
              o = this._readableState;

          switch (o.pipesCount) {
            case 0:
              o.pipes = t;
              break;

            case 1:
              o.pipes = [o.pipes, t];
              break;

            default:
              o.pipes.push(t);
          }

          o.pipesCount += 1, d("pipe count=%d opts=%j", o.pipesCount, e);
          var u = (!e || !1 !== e.end) && t !== n.stdout && t !== n.stderr ? c : b;

          function f(e, n) {
            d("onunpipe"), e === r && n && !1 === n.hasUnpiped && (n.hasUnpiped = !0, d("cleanup"), t.removeListener("close", y), t.removeListener("finish", m), t.removeListener("drain", l), t.removeListener("error", v), t.removeListener("unpipe", f), r.removeListener("end", c), r.removeListener("end", b), r.removeListener("data", g), h = !0, !o.awaitDrain || t._writableState && !t._writableState.needDrain || l());
          }

          function c() {
            d("onend"), t.end();
          }

          o.endEmitted ? i.nextTick(u) : r.once("end", u), t.on("unpipe", f);

          var l = function (t) {
            return function () {
              var e = t._readableState;
              d("pipeOnDrain", e.awaitDrain), e.awaitDrain && e.awaitDrain--, 0 === e.awaitDrain && s(t, "data") && (e.flowing = !0, O(t));
            };
          }(r);

          t.on("drain", l);
          var h = !1;
          var p = !1;

          function g(e) {
            d("ondata"), p = !1, !1 !== t.write(e) || p || ((1 === o.pipesCount && o.pipes === t || o.pipesCount > 1 && -1 !== P(o.pipes, t)) && !h && (d("false write response, pause", r._readableState.awaitDrain), r._readableState.awaitDrain++, p = !0), r.pause());
          }

          function v(e) {
            d("onerror", e), b(), t.removeListener("error", v), 0 === s(t, "error") && t.emit("error", e);
          }

          function y() {
            t.removeListener("finish", m), b();
          }

          function m() {
            d("onfinish"), t.removeListener("close", y), b();
          }

          function b() {
            d("unpipe"), r.unpipe(t);
          }

          return r.on("data", g), function (t, e, r) {
            if ("function" == typeof t.prependListener) return t.prependListener(e, r);
            t._events && t._events[e] ? a(t._events[e]) ? t._events[e].unshift(r) : t._events[e] = [r, t._events[e]] : t.on(e, r);
          }(t, "error", v), t.once("close", y), t.once("finish", m), t.emit("pipe", r), o.flowing || (d("pipe resume"), r.resume()), t;
        }, b.prototype.unpipe = function (t) {
          var e = this._readableState,
              r = {
            hasUnpiped: !1
          };
          if (0 === e.pipesCount) return this;
          if (1 === e.pipesCount) return t && t !== e.pipes ? this : (t || (t = e.pipes), e.pipes = null, e.pipesCount = 0, e.flowing = !1, t && t.emit("unpipe", this, r), this);

          if (!t) {
            var n = e.pipes,
                i = e.pipesCount;
            e.pipes = null, e.pipesCount = 0, e.flowing = !1;

            for (var o = 0; o < i; o++) n[o].emit("unpipe", this, r);

            return this;
          }

          var a = P(e.pipes, t);
          return -1 === a ? this : (e.pipes.splice(a, 1), e.pipesCount -= 1, 1 === e.pipesCount && (e.pipes = e.pipes[0]), t.emit("unpipe", this, r), this);
        }, b.prototype.on = function (t, e) {
          var r = u.prototype.on.call(this, t, e);
          if ("data" === t) !1 !== this._readableState.flowing && this.resume();else if ("readable" === t) {
            var n = this._readableState;
            n.endEmitted || n.readableListening || (n.readableListening = n.needReadable = !0, n.emittedReadable = !1, n.reading ? n.length && S(this) : i.nextTick(T, this));
          }
          return r;
        }, b.prototype.addListener = b.prototype.on, b.prototype.resume = function () {
          var t = this._readableState;
          return t.flowing || (d("resume"), t.flowing = !0, function (t, e) {
            e.resumeScheduled || (e.resumeScheduled = !0, i.nextTick(R, t, e));
          }(this, t)), this;
        }, b.prototype.pause = function () {
          return d("call pause flowing=%j", this._readableState.flowing), !1 !== this._readableState.flowing && (d("pause"), this._readableState.flowing = !1, this.emit("pause")), this;
        }, b.prototype.wrap = function (t) {
          var e = this,
              r = this._readableState,
              n = !1;

          for (var i in t.on("end", function () {
            if (d("wrapped end"), r.decoder && !r.ended) {
              var t = r.decoder.end();
              t && t.length && e.push(t);
            }

            e.push(null);
          }), t.on("data", function (i) {
            (d("wrapped data"), r.decoder && (i = r.decoder.write(i)), r.objectMode && null == i) || (r.objectMode || i && i.length) && (e.push(i) || (n = !0, t.pause()));
          }), t) void 0 === this[i] && "function" == typeof t[i] && (this[i] = function (e) {
            return function () {
              return t[e].apply(t, arguments);
            };
          }(i));

          for (var o = 0; o < y.length; o++) t.on(y[o], this.emit.bind(this, y[o]));

          return this._read = function (e) {
            d("wrapped _read", e), n && (n = !1, t.resume());
          }, this;
        }, Object.defineProperty(b.prototype, "readableHighWaterMark", {
          enumerable: !1,
          get: function () {
            return this._readableState.highWaterMark;
          }
        }), b._fromList = I;
      }).call(this, r(23), r(38));
    }, function (t, e, r) {
      t.exports = r(91).EventEmitter;
    }, function (t, e, r) {

      var n = r(70);

      function i(t, e) {
        t.emit("error", e);
      }

      t.exports = {
        destroy: function (t, e) {
          var r = this,
              o = this._readableState && this._readableState.destroyed,
              a = this._writableState && this._writableState.destroyed;
          return o || a ? (e ? e(t) : !t || this._writableState && this._writableState.errorEmitted || n.nextTick(i, this, t), this) : (this._readableState && (this._readableState.destroyed = !0), this._writableState && (this._writableState.destroyed = !0), this._destroy(t || null, function (t) {
            !e && t ? (n.nextTick(i, r, t), r._writableState && (r._writableState.errorEmitted = !0)) : e && e(t);
          }), this);
        },
        undestroy: function () {
          this._readableState && (this._readableState.destroyed = !1, this._readableState.reading = !1, this._readableState.ended = !1, this._readableState.endEmitted = !1), this._writableState && (this._writableState.destroyed = !1, this._writableState.ended = !1, this._writableState.ending = !1, this._writableState.finished = !1, this._writableState.errorEmitted = !1);
        }
      };
    }, function (t, e, r) {

      var n = r(71).Buffer,
          i = n.isEncoding || function (t) {
        switch ((t = "" + t) && t.toLowerCase()) {
          case "hex":
          case "utf8":
          case "utf-8":
          case "ascii":
          case "binary":
          case "base64":
          case "ucs2":
          case "ucs-2":
          case "utf16le":
          case "utf-16le":
          case "raw":
            return !0;

          default:
            return !1;
        }
      };

      function o(t) {
        var e;

        switch (this.encoding = function (t) {
          var e = function (t) {
            if (!t) return "utf8";

            for (var e;;) switch (t) {
              case "utf8":
              case "utf-8":
                return "utf8";

              case "ucs2":
              case "ucs-2":
              case "utf16le":
              case "utf-16le":
                return "utf16le";

              case "latin1":
              case "binary":
                return "latin1";

              case "base64":
              case "ascii":
              case "hex":
                return t;

              default:
                if (e) return;
                t = ("" + t).toLowerCase(), e = !0;
            }
          }(t);

          if ("string" != typeof e && (n.isEncoding === i || !i(t))) throw new Error("Unknown encoding: " + t);
          return e || t;
        }(t), this.encoding) {
          case "utf16le":
            this.text = u, this.end = f, e = 4;
            break;

          case "utf8":
            this.fillLast = s, e = 4;
            break;

          case "base64":
            this.text = c, this.end = l, e = 3;
            break;

          default:
            return this.write = h, void (this.end = d);
        }

        this.lastNeed = 0, this.lastTotal = 0, this.lastChar = n.allocUnsafe(e);
      }

      function a(t) {
        return t <= 127 ? 0 : t >> 5 == 6 ? 2 : t >> 4 == 14 ? 3 : t >> 3 == 30 ? 4 : t >> 6 == 2 ? -1 : -2;
      }

      function s(t) {
        var e = this.lastTotal - this.lastNeed,
            r = function (t, e, r) {
          if (128 != (192 & e[0])) return t.lastNeed = 0, "�";

          if (t.lastNeed > 1 && e.length > 1) {
            if (128 != (192 & e[1])) return t.lastNeed = 1, "�";
            if (t.lastNeed > 2 && e.length > 2 && 128 != (192 & e[2])) return t.lastNeed = 2, "�";
          }
        }(this, t);

        return void 0 !== r ? r : this.lastNeed <= t.length ? (t.copy(this.lastChar, e, 0, this.lastNeed), this.lastChar.toString(this.encoding, 0, this.lastTotal)) : (t.copy(this.lastChar, e, 0, t.length), void (this.lastNeed -= t.length));
      }

      function u(t, e) {
        if ((t.length - e) % 2 == 0) {
          var r = t.toString("utf16le", e);

          if (r) {
            var n = r.charCodeAt(r.length - 1);
            if (n >= 55296 && n <= 56319) return this.lastNeed = 2, this.lastTotal = 4, this.lastChar[0] = t[t.length - 2], this.lastChar[1] = t[t.length - 1], r.slice(0, -1);
          }

          return r;
        }

        return this.lastNeed = 1, this.lastTotal = 2, this.lastChar[0] = t[t.length - 1], t.toString("utf16le", e, t.length - 1);
      }

      function f(t) {
        var e = t && t.length ? this.write(t) : "";

        if (this.lastNeed) {
          var r = this.lastTotal - this.lastNeed;
          return e + this.lastChar.toString("utf16le", 0, r);
        }

        return e;
      }

      function c(t, e) {
        var r = (t.length - e) % 3;
        return 0 === r ? t.toString("base64", e) : (this.lastNeed = 3 - r, this.lastTotal = 3, 1 === r ? this.lastChar[0] = t[t.length - 1] : (this.lastChar[0] = t[t.length - 2], this.lastChar[1] = t[t.length - 1]), t.toString("base64", e, t.length - r));
      }

      function l(t) {
        var e = t && t.length ? this.write(t) : "";
        return this.lastNeed ? e + this.lastChar.toString("base64", 0, 3 - this.lastNeed) : e;
      }

      function h(t) {
        return t.toString(this.encoding);
      }

      function d(t) {
        return t && t.length ? this.write(t) : "";
      }

      e.StringDecoder = o, o.prototype.write = function (t) {
        if (0 === t.length) return "";
        var e, r;

        if (this.lastNeed) {
          if (void 0 === (e = this.fillLast(t))) return "";
          r = this.lastNeed, this.lastNeed = 0;
        } else r = 0;

        return r < t.length ? e ? e + this.text(t, r) : this.text(t, r) : e || "";
      }, o.prototype.end = function (t) {
        var e = t && t.length ? this.write(t) : "";
        return this.lastNeed ? e + "�" : e;
      }, o.prototype.text = function (t, e) {
        var r = function (t, e, r) {
          var n = e.length - 1;
          if (n < r) return 0;
          var i = a(e[n]);
          if (i >= 0) return i > 0 && (t.lastNeed = i - 1), i;
          if (--n < r || -2 === i) return 0;
          if ((i = a(e[n])) >= 0) return i > 0 && (t.lastNeed = i - 2), i;
          if (--n < r || -2 === i) return 0;
          if ((i = a(e[n])) >= 0) return i > 0 && (2 === i ? i = 0 : t.lastNeed = i - 3), i;
          return 0;
        }(this, t, e);

        if (!this.lastNeed) return t.toString("utf8", e);
        this.lastTotal = r;
        var n = t.length - (r - this.lastNeed);
        return t.copy(this.lastChar, 0, n), t.toString("utf8", e, n);
      }, o.prototype.fillLast = function (t) {
        if (this.lastNeed <= t.length) return t.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed), this.lastChar.toString(this.encoding, 0, this.lastTotal);
        t.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, t.length), this.lastNeed -= t.length;
      };
    }, function (t, e, r) {

      t.exports = a;
      var n = r(39),
          i = r(57);

      function o(t, e) {
        var r = this._transformState;
        r.transforming = !1;
        var n = r.writecb;
        if (!n) return this.emit("error", new Error("write callback called multiple times"));
        r.writechunk = null, r.writecb = null, null != e && this.push(e), n(t);
        var i = this._readableState;
        i.reading = !1, (i.needReadable || i.length < i.highWaterMark) && this._read(i.highWaterMark);
      }

      function a(t) {
        if (!(this instanceof a)) return new a(t);
        n.call(this, t), this._transformState = {
          afterTransform: o.bind(this),
          needTransform: !1,
          transforming: !1,
          writecb: null,
          writechunk: null,
          writeencoding: null
        }, this._readableState.needReadable = !0, this._readableState.sync = !1, t && ("function" == typeof t.transform && (this._transform = t.transform), "function" == typeof t.flush && (this._flush = t.flush)), this.on("prefinish", s);
      }

      function s() {
        var t = this;
        "function" == typeof this._flush ? this._flush(function (e, r) {
          u(t, e, r);
        }) : u(this, null, null);
      }

      function u(t, e, r) {
        if (e) return t.emit("error", e);
        if (null != r && t.push(r), t._writableState.length) throw new Error("Calling transform done when ws.length != 0");
        if (t._transformState.transforming) throw new Error("Calling transform done when still transforming");
        return t.push(null);
      }

      i.inherits = r(47), i.inherits(a, n), a.prototype.push = function (t, e) {
        return this._transformState.needTransform = !1, n.prototype.push.call(this, t, e);
      }, a.prototype._transform = function (t, e, r) {
        throw new Error("_transform() is not implemented");
      }, a.prototype._write = function (t, e, r) {
        var n = this._transformState;

        if (n.writecb = r, n.writechunk = t, n.writeencoding = e, !n.transforming) {
          var i = this._readableState;
          (n.needTransform || i.needReadable || i.length < i.highWaterMark) && this._read(i.highWaterMark);
        }
      }, a.prototype._read = function (t) {
        var e = this._transformState;
        null !== e.writechunk && e.writecb && !e.transforming ? (e.transforming = !0, this._transform(e.writechunk, e.writeencoding, e.afterTransform)) : e.needTransform = !0;
      }, a.prototype._destroy = function (t, e) {
        var r = this;

        n.prototype._destroy.call(this, t, function (t) {
          e(t), r.emit("close");
        });
      };
    }, function (t, e, r) {

      (function (e) {
        var n = r(213);
        /*!
         * The buffer module from node.js, for the browser.
         *
         * @author   Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
         * @license  MIT
         */

        function i(t, e) {
          if (t === e) return 0;

          for (var r = t.length, n = e.length, i = 0, o = Math.min(r, n); i < o; ++i) if (t[i] !== e[i]) {
            r = t[i], n = e[i];
            break;
          }

          return r < n ? -1 : n < r ? 1 : 0;
        }

        function o(t) {
          return e.Buffer && "function" == typeof e.Buffer.isBuffer ? e.Buffer.isBuffer(t) : !(null == t || !t._isBuffer);
        }

        var a = r(133),
            s = Object.prototype.hasOwnProperty,
            u = Array.prototype.slice,
            f = "foo" === function () {}.name;

        function c(t) {
          return Object.prototype.toString.call(t);
        }

        function l(t) {
          return !o(t) && "function" == typeof e.ArrayBuffer && ("function" == typeof ArrayBuffer.isView ? ArrayBuffer.isView(t) : !!t && (t instanceof DataView || !!(t.buffer && t.buffer instanceof ArrayBuffer)));
        }

        var h = t.exports = m,
            d = /\s*function\s+([^\(\s]*)\s*/;

        function p(t) {
          if (a.isFunction(t)) {
            if (f) return t.name;
            var e = t.toString().match(d);
            return e && e[1];
          }
        }

        function g(t, e) {
          return "string" == typeof t ? t.length < e ? t : t.slice(0, e) : t;
        }

        function v(t) {
          if (f || !a.isFunction(t)) return a.inspect(t);
          var e = p(t);
          return "[Function" + (e ? ": " + e : "") + "]";
        }

        function y(t, e, r, n, i) {
          throw new h.AssertionError({
            message: r,
            actual: t,
            expected: e,
            operator: n,
            stackStartFunction: i
          });
        }

        function m(t, e) {
          t || y(t, !0, e, "==", h.ok);
        }

        function b(t, e, r, n) {
          if (t === e) return !0;
          if (o(t) && o(e)) return 0 === i(t, e);
          if (a.isDate(t) && a.isDate(e)) return t.getTime() === e.getTime();
          if (a.isRegExp(t) && a.isRegExp(e)) return t.source === e.source && t.global === e.global && t.multiline === e.multiline && t.lastIndex === e.lastIndex && t.ignoreCase === e.ignoreCase;

          if (null !== t && "object" == typeof t || null !== e && "object" == typeof e) {
            if (l(t) && l(e) && c(t) === c(e) && !(t instanceof Float32Array || t instanceof Float64Array)) return 0 === i(new Uint8Array(t.buffer), new Uint8Array(e.buffer));
            if (o(t) !== o(e)) return !1;
            var s = (n = n || {
              actual: [],
              expected: []
            }).actual.indexOf(t);
            return -1 !== s && s === n.expected.indexOf(e) || (n.actual.push(t), n.expected.push(e), function (t, e, r, n) {
              if (null == t || null == e) return !1;
              if (a.isPrimitive(t) || a.isPrimitive(e)) return t === e;
              if (r && Object.getPrototypeOf(t) !== Object.getPrototypeOf(e)) return !1;

              var i = _(t),
                  o = _(e);

              if (i && !o || !i && o) return !1;
              if (i) return t = u.call(t), e = u.call(e), b(t, e, r);
              var s,
                  f,
                  c = E(t),
                  l = E(e);
              if (c.length !== l.length) return !1;

              for (c.sort(), l.sort(), f = c.length - 1; f >= 0; f--) if (c[f] !== l[f]) return !1;

              for (f = c.length - 1; f >= 0; f--) if (s = c[f], !b(t[s], e[s], r, n)) return !1;

              return !0;
            }(t, e, r, n));
          }

          return r ? t === e : t == e;
        }

        function _(t) {
          return "[object Arguments]" == Object.prototype.toString.call(t);
        }

        function w(t, e) {
          if (!t || !e) return !1;
          if ("[object RegExp]" == Object.prototype.toString.call(e)) return e.test(t);

          try {
            if (t instanceof e) return !0;
          } catch (t) {}

          return !Error.isPrototypeOf(e) && !0 === e.call({}, t);
        }

        function x(t, e, r, n) {
          var i;
          if ("function" != typeof e) throw new TypeError('"block" argument must be a function');
          "string" == typeof r && (n = r, r = null), i = function (t) {
            var e;

            try {
              t();
            } catch (t) {
              e = t;
            }

            return e;
          }(e), n = (r && r.name ? " (" + r.name + ")." : ".") + (n ? " " + n : "."), t && !i && y(i, r, "Missing expected exception" + n);
          var o = "string" == typeof n,
              s = !t && i && !r;
          if ((!t && a.isError(i) && o && w(i, r) || s) && y(i, r, "Got unwanted exception" + n), t && i && r && !w(i, r) || !t && i) throw i;
        }

        h.AssertionError = function (t) {
          this.name = "AssertionError", this.actual = t.actual, this.expected = t.expected, this.operator = t.operator, t.message ? (this.message = t.message, this.generatedMessage = !1) : (this.message = function (t) {
            return g(v(t.actual), 128) + " " + t.operator + " " + g(v(t.expected), 128);
          }(this), this.generatedMessage = !0);
          var e = t.stackStartFunction || y;
          if (Error.captureStackTrace) Error.captureStackTrace(this, e);else {
            var r = new Error();

            if (r.stack) {
              var n = r.stack,
                  i = p(e),
                  o = n.indexOf("\n" + i);

              if (o >= 0) {
                var a = n.indexOf("\n", o + 1);
                n = n.substring(a + 1);
              }

              this.stack = n;
            }
          }
        }, a.inherits(h.AssertionError, Error), h.fail = y, h.ok = m, h.equal = function (t, e, r) {
          t != e && y(t, e, r, "==", h.equal);
        }, h.notEqual = function (t, e, r) {
          t == e && y(t, e, r, "!=", h.notEqual);
        }, h.deepEqual = function (t, e, r) {
          b(t, e, !1) || y(t, e, r, "deepEqual", h.deepEqual);
        }, h.deepStrictEqual = function (t, e, r) {
          b(t, e, !0) || y(t, e, r, "deepStrictEqual", h.deepStrictEqual);
        }, h.notDeepEqual = function (t, e, r) {
          b(t, e, !1) && y(t, e, r, "notDeepEqual", h.notDeepEqual);
        }, h.notDeepStrictEqual = function t(e, r, n) {
          b(e, r, !0) && y(e, r, n, "notDeepStrictEqual", t);
        }, h.strictEqual = function (t, e, r) {
          t !== e && y(t, e, r, "===", h.strictEqual);
        }, h.notStrictEqual = function (t, e, r) {
          t === e && y(t, e, r, "!==", h.notStrictEqual);
        }, h.throws = function (t, e, r) {
          x(!0, t, e, r);
        }, h.doesNotThrow = function (t, e, r) {
          x(!1, t, e, r);
        }, h.ifError = function (t) {
          if (t) throw t;
        }, h.strict = n(function t(e, r) {
          e || y(e, !0, r, "==", t);
        }, h, {
          equal: h.strictEqual,
          deepEqual: h.deepStrictEqual,
          notEqual: h.notStrictEqual,
          notDeepEqual: h.notDeepStrictEqual
        }), h.strict.strict = h.strict;

        var E = Object.keys || function (t) {
          var e = [];

          for (var r in t) s.call(t, r) && e.push(r);

          return e;
        };
      }).call(this, r(23));
    }, function (t, e, r) {
      (function (t) {
        var n = Object.getOwnPropertyDescriptors || function (t) {
          for (var e = Object.keys(t), r = {}, n = 0; n < e.length; n++) r[e[n]] = Object.getOwnPropertyDescriptor(t, e[n]);

          return r;
        },
            i = /%[sdj%]/g;

        e.format = function (t) {
          if (!y(t)) {
            for (var e = [], r = 0; r < arguments.length; r++) e.push(s(arguments[r]));

            return e.join(" ");
          }

          r = 1;

          for (var n = arguments, o = n.length, a = String(t).replace(i, function (t) {
            if ("%%" === t) return "%";
            if (r >= o) return t;

            switch (t) {
              case "%s":
                return String(n[r++]);

              case "%d":
                return Number(n[r++]);

              case "%j":
                try {
                  return JSON.stringify(n[r++]);
                } catch (t) {
                  return "[Circular]";
                }

              default:
                return t;
            }
          }), u = n[r]; r < o; u = n[++r]) g(u) || !_(u) ? a += " " + u : a += " " + s(u);

          return a;
        }, e.deprecate = function (r, n) {
          if (void 0 !== t && !0 === t.noDeprecation) return r;
          if (void 0 === t) return function () {
            return e.deprecate(r, n).apply(this, arguments);
          };
          var i = !1;
          return function () {
            if (!i) {
              if (t.throwDeprecation) throw new Error(n);
              t.traceDeprecation ? console.trace(n) : console.error(n), i = !0;
            }

            return r.apply(this, arguments);
          };
        };
        var o,
            a = {};

        function s(t, r) {
          var n = {
            seen: [],
            stylize: f
          };
          return arguments.length >= 3 && (n.depth = arguments[2]), arguments.length >= 4 && (n.colors = arguments[3]), p(r) ? n.showHidden = r : r && e._extend(n, r), m(n.showHidden) && (n.showHidden = !1), m(n.depth) && (n.depth = 2), m(n.colors) && (n.colors = !1), m(n.customInspect) && (n.customInspect = !0), n.colors && (n.stylize = u), c(n, t, n.depth);
        }

        function u(t, e) {
          var r = s.styles[e];
          return r ? "[" + s.colors[r][0] + "m" + t + "[" + s.colors[r][1] + "m" : t;
        }

        function f(t, e) {
          return t;
        }

        function c(t, r, n) {
          if (t.customInspect && r && E(r.inspect) && r.inspect !== e.inspect && (!r.constructor || r.constructor.prototype !== r)) {
            var i = r.inspect(n, t);
            return y(i) || (i = c(t, i, n)), i;
          }

          var o = function (t, e) {
            if (m(e)) return t.stylize("undefined", "undefined");

            if (y(e)) {
              var r = "'" + JSON.stringify(e).replace(/^"|"$/g, "").replace(/'/g, "\\'").replace(/\\"/g, '"') + "'";
              return t.stylize(r, "string");
            }

            if (v(e)) return t.stylize("" + e, "number");
            if (p(e)) return t.stylize("" + e, "boolean");
            if (g(e)) return t.stylize("null", "null");
          }(t, r);

          if (o) return o;

          var a = Object.keys(r),
              s = function (t) {
            var e = {};
            return t.forEach(function (t, r) {
              e[t] = !0;
            }), e;
          }(a);

          if (t.showHidden && (a = Object.getOwnPropertyNames(r)), x(r) && (a.indexOf("message") >= 0 || a.indexOf("description") >= 0)) return l(r);

          if (0 === a.length) {
            if (E(r)) {
              var u = r.name ? ": " + r.name : "";
              return t.stylize("[Function" + u + "]", "special");
            }

            if (b(r)) return t.stylize(RegExp.prototype.toString.call(r), "regexp");
            if (w(r)) return t.stylize(Date.prototype.toString.call(r), "date");
            if (x(r)) return l(r);
          }

          var f,
              _ = "",
              S = !1,
              k = ["{", "}"];
          (d(r) && (S = !0, k = ["[", "]"]), E(r)) && (_ = " [Function" + (r.name ? ": " + r.name : "") + "]");
          return b(r) && (_ = " " + RegExp.prototype.toString.call(r)), w(r) && (_ = " " + Date.prototype.toUTCString.call(r)), x(r) && (_ = " " + l(r)), 0 !== a.length || S && 0 != r.length ? n < 0 ? b(r) ? t.stylize(RegExp.prototype.toString.call(r), "regexp") : t.stylize("[Object]", "special") : (t.seen.push(r), f = S ? function (t, e, r, n, i) {
            for (var o = [], a = 0, s = e.length; a < s; ++a) T(e, String(a)) ? o.push(h(t, e, r, n, String(a), !0)) : o.push("");

            return i.forEach(function (i) {
              i.match(/^\d+$/) || o.push(h(t, e, r, n, i, !0));
            }), o;
          }(t, r, n, s, a) : a.map(function (e) {
            return h(t, r, n, s, e, S);
          }), t.seen.pop(), function (t, e, r) {
            if (t.reduce(function (t, e) {
              return e.indexOf("\n") >= 0 && 0, t + e.replace(/\u001b\[\d\d?m/g, "").length + 1;
            }, 0) > 60) return r[0] + ("" === e ? "" : e + "\n ") + " " + t.join(",\n  ") + " " + r[1];
            return r[0] + e + " " + t.join(", ") + " " + r[1];
          }(f, _, k)) : k[0] + _ + k[1];
        }

        function l(t) {
          return "[" + Error.prototype.toString.call(t) + "]";
        }

        function h(t, e, r, n, i, o) {
          var a, s, u;

          if ((u = Object.getOwnPropertyDescriptor(e, i) || {
            value: e[i]
          }).get ? s = u.set ? t.stylize("[Getter/Setter]", "special") : t.stylize("[Getter]", "special") : u.set && (s = t.stylize("[Setter]", "special")), T(n, i) || (a = "[" + i + "]"), s || (t.seen.indexOf(u.value) < 0 ? (s = g(r) ? c(t, u.value, null) : c(t, u.value, r - 1)).indexOf("\n") > -1 && (s = o ? s.split("\n").map(function (t) {
            return "  " + t;
          }).join("\n").substr(2) : "\n" + s.split("\n").map(function (t) {
            return "   " + t;
          }).join("\n")) : s = t.stylize("[Circular]", "special")), m(a)) {
            if (o && i.match(/^\d+$/)) return s;
            (a = JSON.stringify("" + i)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/) ? (a = a.substr(1, a.length - 2), a = t.stylize(a, "name")) : (a = a.replace(/'/g, "\\'").replace(/\\"/g, '"').replace(/(^"|"$)/g, "'"), a = t.stylize(a, "string"));
          }

          return a + ": " + s;
        }

        function d(t) {
          return Array.isArray(t);
        }

        function p(t) {
          return "boolean" == typeof t;
        }

        function g(t) {
          return null === t;
        }

        function v(t) {
          return "number" == typeof t;
        }

        function y(t) {
          return "string" == typeof t;
        }

        function m(t) {
          return void 0 === t;
        }

        function b(t) {
          return _(t) && "[object RegExp]" === S(t);
        }

        function _(t) {
          return "object" == typeof t && null !== t;
        }

        function w(t) {
          return _(t) && "[object Date]" === S(t);
        }

        function x(t) {
          return _(t) && ("[object Error]" === S(t) || t instanceof Error);
        }

        function E(t) {
          return "function" == typeof t;
        }

        function S(t) {
          return Object.prototype.toString.call(t);
        }

        function k(t) {
          return t < 10 ? "0" + t.toString(10) : t.toString(10);
        }

        e.debuglog = function (r) {
          if (m(o) && (o = t.env.NODE_DEBUG || ""), r = r.toUpperCase(), !a[r]) if (new RegExp("\\b" + r + "\\b", "i").test(o)) {
            var n = t.pid;

            a[r] = function () {
              var t = e.format.apply(e, arguments);
              console.error("%s %d: %s", r, n, t);
            };
          } else a[r] = function () {};
          return a[r];
        }, e.inspect = s, s.colors = {
          bold: [1, 22],
          italic: [3, 23],
          underline: [4, 24],
          inverse: [7, 27],
          white: [37, 39],
          grey: [90, 39],
          black: [30, 39],
          blue: [34, 39],
          cyan: [36, 39],
          green: [32, 39],
          magenta: [35, 39],
          red: [31, 39],
          yellow: [33, 39]
        }, s.styles = {
          special: "cyan",
          number: "yellow",
          boolean: "yellow",
          undefined: "grey",
          null: "bold",
          string: "green",
          date: "magenta",
          regexp: "red"
        }, e.isArray = d, e.isBoolean = p, e.isNull = g, e.isNullOrUndefined = function (t) {
          return null == t;
        }, e.isNumber = v, e.isString = y, e.isSymbol = function (t) {
          return "symbol" == typeof t;
        }, e.isUndefined = m, e.isRegExp = b, e.isObject = _, e.isDate = w, e.isError = x, e.isFunction = E, e.isPrimitive = function (t) {
          return null === t || "boolean" == typeof t || "number" == typeof t || "string" == typeof t || "symbol" == typeof t || void 0 === t;
        }, e.isBuffer = r(214);
        var A = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

        function C() {
          var t = new Date(),
              e = [k(t.getHours()), k(t.getMinutes()), k(t.getSeconds())].join(":");
          return [t.getDate(), A[t.getMonth()], e].join(" ");
        }

        function T(t, e) {
          return Object.prototype.hasOwnProperty.call(t, e);
        }

        e.log = function () {
          console.log("%s - %s", C(), e.format.apply(e, arguments));
        }, e.inherits = r(215), e._extend = function (t, e) {
          if (!e || !_(e)) return t;

          for (var r = Object.keys(e), n = r.length; n--;) t[r[n]] = e[r[n]];

          return t;
        };
        var R = "undefined" != typeof Symbol ? Symbol("util.promisify.custom") : void 0;

        function O(t, e) {
          if (!t) {
            var r = new Error("Promise was rejected with a falsy value");
            r.reason = t, t = r;
          }

          return e(t);
        }

        e.promisify = function (t) {
          if ("function" != typeof t) throw new TypeError('The "original" argument must be of type Function');

          if (R && t[R]) {
            var e;
            if ("function" != typeof (e = t[R])) throw new TypeError('The "util.promisify.custom" argument must be of type Function');
            return Object.defineProperty(e, R, {
              value: e,
              enumerable: !1,
              writable: !1,
              configurable: !0
            }), e;
          }

          function e() {
            for (var e, r, n = new Promise(function (t, n) {
              e = t, r = n;
            }), i = [], o = 0; o < arguments.length; o++) i.push(arguments[o]);

            i.push(function (t, n) {
              t ? r(t) : e(n);
            });

            try {
              t.apply(this, i);
            } catch (t) {
              r(t);
            }

            return n;
          }

          return Object.setPrototypeOf(e, Object.getPrototypeOf(t)), R && Object.defineProperty(e, R, {
            value: e,
            enumerable: !1,
            writable: !1,
            configurable: !0
          }), Object.defineProperties(e, n(t));
        }, e.promisify.custom = R, e.callbackify = function (e) {
          if ("function" != typeof e) throw new TypeError('The "original" argument must be of type Function');

          function r() {
            for (var r = [], n = 0; n < arguments.length; n++) r.push(arguments[n]);

            var i = r.pop();
            if ("function" != typeof i) throw new TypeError("The last argument must be of type Function");

            var o = this,
                a = function () {
              return i.apply(o, arguments);
            };

            e.apply(this, r).then(function (e) {
              t.nextTick(a, null, e);
            }, function (e) {
              t.nextTick(O, e, a);
            });
          }

          return Object.setPrototypeOf(r, Object.getPrototypeOf(e)), Object.defineProperties(r, n(e)), r;
        };
      }).call(this, r(38));
    }, function (t, e, r) {

      t.exports = function (t, e, r, n) {
        for (var i = 65535 & t | 0, o = t >>> 16 & 65535 | 0, a = 0; 0 !== r;) {
          r -= a = r > 2e3 ? 2e3 : r;

          do {
            o = o + (i = i + e[n++] | 0) | 0;
          } while (--a);

          i %= 65521, o %= 65521;
        }

        return i | o << 16 | 0;
      };
    }, function (t, e, r) {

      var n = function () {
        for (var t, e = [], r = 0; r < 256; r++) {
          t = r;

          for (var n = 0; n < 8; n++) t = 1 & t ? 3988292384 ^ t >>> 1 : t >>> 1;

          e[r] = t;
        }

        return e;
      }();

      t.exports = function (t, e, r, i) {
        var o = n,
            a = i + r;
        t ^= -1;

        for (var s = i; s < a; s++) t = t >>> 8 ^ o[255 & (t ^ e[s])];

        return -1 ^ t;
      };
    }, function (t, e, r) {
      r(11)("iterator");
    }, function (t, e, r) {
      var n = r(111),
          i = r(85).concat("length", "prototype");

      e.f = Object.getOwnPropertyNames || function (t) {
        return n(t, i);
      };
    }, function (t, e) {
      e.f = Object.getOwnPropertySymbols;
    }, function (t, e, r) {
      t.exports = r(259);
    }, function (t, e, r) {
      t.exports = r(262);
    }, function (t, e, r) {
      var n = r(50),
          i = r(14),
          o = r(24),
          a = r(21).f,
          s = r(64),
          u = r(271),
          f = s("meta"),
          c = 0,
          l = Object.isExtensible || function () {
        return !0;
      },
          h = function (t) {
        a(t, f, {
          value: {
            objectID: "O" + ++c,
            weakData: {}
          }
        });
      },
          d = t.exports = {
        REQUIRED: !1,
        fastKey: function (t, e) {
          if (!i(t)) return "symbol" == typeof t ? t : ("string" == typeof t ? "S" : "P") + t;

          if (!o(t, f)) {
            if (!l(t)) return "F";
            if (!e) return "E";
            h(t);
          }

          return t[f].objectID;
        },
        getWeakData: function (t, e) {
          if (!o(t, f)) {
            if (!l(t)) return !0;
            if (!e) return !1;
            h(t);
          }

          return t[f].weakData;
        },
        onFreeze: function (t) {
          return u && d.REQUIRED && l(t) && !o(t, f) && h(t), t;
        }
      };

      n[f] = !0;
    }, function (t, e, r) {
      var n = r(2),
          i = r(90);

      t.exports = function (t) {
        var e = i(t);
        if ("function" != typeof e) throw TypeError(String(t) + " is not iterable");
        return n(e.call(t));
      };
    }, function (t, e, r) {
      var n = r(298),
          i = Array.prototype;

      t.exports = function (t) {
        var e = t.indexOf;
        return t === i || t instanceof Array && e === i.indexOf ? n : e;
      };
    }, function (t, e, r) {
      var n = r(303),
          i = Function.prototype;

      t.exports = function (t) {
        var e = t.bind;
        return t === i || t instanceof Function && e === i.bind ? n : e;
      };
    }, function (t, e, r) {

      var n = r(5),
          i = r(14),
          o = [].slice,
          a = {},
          s = function (t, e, r) {
        if (!(e in a)) {
          for (var n = [], i = 0; i < e; i++) n[i] = "a[" + i + "]";

          a[e] = Function("C,a", "return new C(" + n.join(",") + ")");
        }

        return a[e](t, r);
      };

      t.exports = Function.bind || function (t) {
        var e = n(this),
            r = o.call(arguments, 1),
            a = function () {
          var n = r.concat(o.call(arguments));
          return this instanceof a ? s(e, n.length, n) : e.apply(t, n);
        };

        return i(e.prototype) && (a.prototype = e.prototype), a;
      };
    }, function (t, e) {
      t.exports = n;
      var r = null;

      try {
        r = new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 13, 2, 96, 0, 1, 127, 96, 4, 127, 127, 127, 127, 1, 127, 3, 7, 6, 0, 1, 1, 1, 1, 1, 6, 6, 1, 127, 1, 65, 0, 11, 7, 50, 6, 3, 109, 117, 108, 0, 1, 5, 100, 105, 118, 95, 115, 0, 2, 5, 100, 105, 118, 95, 117, 0, 3, 5, 114, 101, 109, 95, 115, 0, 4, 5, 114, 101, 109, 95, 117, 0, 5, 8, 103, 101, 116, 95, 104, 105, 103, 104, 0, 0, 10, 191, 1, 6, 4, 0, 35, 0, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 126, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 127, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 128, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 129, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 130, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11])), {}).exports;
      } catch (t) {}

      function n(t, e, r) {
        this.low = 0 | t, this.high = 0 | e, this.unsigned = !!r;
      }

      function i(t) {
        return !0 === (t && t.__isLong__);
      }

      n.prototype.__isLong__, Object.defineProperty(n.prototype, "__isLong__", {
        value: !0
      }), n.isLong = i;
      var o = {},
          a = {};

      function s(t, e) {
        var r, n, i;
        return e ? (i = 0 <= (t >>>= 0) && t < 256) && (n = a[t]) ? n : (r = f(t, (0 | t) < 0 ? -1 : 0, !0), i && (a[t] = r), r) : (i = -128 <= (t |= 0) && t < 128) && (n = o[t]) ? n : (r = f(t, t < 0 ? -1 : 0, !1), i && (o[t] = r), r);
      }

      function u(t, e) {
        if (isNaN(t)) return e ? m : y;

        if (e) {
          if (t < 0) return m;
          if (t >= p) return E;
        } else {
          if (t <= -g) return S;
          if (t + 1 >= g) return x;
        }

        return t < 0 ? u(-t, e).neg() : f(t % d | 0, t / d | 0, e);
      }

      function f(t, e, r) {
        return new n(t, e, r);
      }

      n.fromInt = s, n.fromNumber = u, n.fromBits = f;
      var c = Math.pow;

      function l(t, e, r) {
        if (0 === t.length) throw Error("empty string");
        if ("NaN" === t || "Infinity" === t || "+Infinity" === t || "-Infinity" === t) return y;
        if ("number" == typeof e ? (r = e, e = !1) : e = !!e, (r = r || 10) < 2 || 36 < r) throw RangeError("radix");
        var n;
        if ((n = t.indexOf("-")) > 0) throw Error("interior hyphen");
        if (0 === n) return l(t.substring(1), e, r).neg();

        for (var i = u(c(r, 8)), o = y, a = 0; a < t.length; a += 8) {
          var s = Math.min(8, t.length - a),
              f = parseInt(t.substring(a, a + s), r);

          if (s < 8) {
            var h = u(c(r, s));
            o = o.mul(h).add(u(f));
          } else o = (o = o.mul(i)).add(u(f));
        }

        return o.unsigned = e, o;
      }

      function h(t, e) {
        return "number" == typeof t ? u(t, e) : "string" == typeof t ? l(t, e) : f(t.low, t.high, "boolean" == typeof e ? e : t.unsigned);
      }

      n.fromString = l, n.fromValue = h;
      var d = 4294967296,
          p = d * d,
          g = p / 2,
          v = s(1 << 24),
          y = s(0);
      n.ZERO = y;
      var m = s(0, !0);
      n.UZERO = m;
      var b = s(1);
      n.ONE = b;

      var _ = s(1, !0);

      n.UONE = _;
      var w = s(-1);
      n.NEG_ONE = w;
      var x = f(-1, 2147483647, !1);
      n.MAX_VALUE = x;
      var E = f(-1, -1, !0);
      n.MAX_UNSIGNED_VALUE = E;
      var S = f(0, -2147483648, !1);
      n.MIN_VALUE = S;
      var k = n.prototype;
      k.toInt = function () {
        return this.unsigned ? this.low >>> 0 : this.low;
      }, k.toNumber = function () {
        return this.unsigned ? (this.high >>> 0) * d + (this.low >>> 0) : this.high * d + (this.low >>> 0);
      }, k.toString = function (t) {
        if ((t = t || 10) < 2 || 36 < t) throw RangeError("radix");
        if (this.isZero()) return "0";

        if (this.isNegative()) {
          if (this.eq(S)) {
            var e = u(t),
                r = this.div(e),
                n = r.mul(e).sub(this);
            return r.toString(t) + n.toInt().toString(t);
          }

          return "-" + this.neg().toString(t);
        }

        for (var i = u(c(t, 6), this.unsigned), o = this, a = "";;) {
          var s = o.div(i),
              f = (o.sub(s.mul(i)).toInt() >>> 0).toString(t);
          if ((o = s).isZero()) return f + a;

          for (; f.length < 6;) f = "0" + f;

          a = "" + f + a;
        }
      }, k.getHighBits = function () {
        return this.high;
      }, k.getHighBitsUnsigned = function () {
        return this.high >>> 0;
      }, k.getLowBits = function () {
        return this.low;
      }, k.getLowBitsUnsigned = function () {
        return this.low >>> 0;
      }, k.getNumBitsAbs = function () {
        if (this.isNegative()) return this.eq(S) ? 64 : this.neg().getNumBitsAbs();

        for (var t = 0 != this.high ? this.high : this.low, e = 31; e > 0 && 0 == (t & 1 << e); e--);

        return 0 != this.high ? e + 33 : e + 1;
      }, k.isZero = function () {
        return 0 === this.high && 0 === this.low;
      }, k.eqz = k.isZero, k.isNegative = function () {
        return !this.unsigned && this.high < 0;
      }, k.isPositive = function () {
        return this.unsigned || this.high >= 0;
      }, k.isOdd = function () {
        return 1 == (1 & this.low);
      }, k.isEven = function () {
        return 0 == (1 & this.low);
      }, k.equals = function (t) {
        return i(t) || (t = h(t)), (this.unsigned === t.unsigned || this.high >>> 31 != 1 || t.high >>> 31 != 1) && this.high === t.high && this.low === t.low;
      }, k.eq = k.equals, k.notEquals = function (t) {
        return !this.eq(t);
      }, k.neq = k.notEquals, k.ne = k.notEquals, k.lessThan = function (t) {
        return this.comp(t) < 0;
      }, k.lt = k.lessThan, k.lessThanOrEqual = function (t) {
        return this.comp(t) <= 0;
      }, k.lte = k.lessThanOrEqual, k.le = k.lessThanOrEqual, k.greaterThan = function (t) {
        return this.comp(t) > 0;
      }, k.gt = k.greaterThan, k.greaterThanOrEqual = function (t) {
        return this.comp(t) >= 0;
      }, k.gte = k.greaterThanOrEqual, k.ge = k.greaterThanOrEqual, k.compare = function (t) {
        if (i(t) || (t = h(t)), this.eq(t)) return 0;
        var e = this.isNegative(),
            r = t.isNegative();
        return e && !r ? -1 : !e && r ? 1 : this.unsigned ? t.high >>> 0 > this.high >>> 0 || t.high === this.high && t.low >>> 0 > this.low >>> 0 ? -1 : 1 : this.sub(t).isNegative() ? -1 : 1;
      }, k.comp = k.compare, k.negate = function () {
        return !this.unsigned && this.eq(S) ? S : this.not().add(b);
      }, k.neg = k.negate, k.add = function (t) {
        i(t) || (t = h(t));
        var e = this.high >>> 16,
            r = 65535 & this.high,
            n = this.low >>> 16,
            o = 65535 & this.low,
            a = t.high >>> 16,
            s = 65535 & t.high,
            u = t.low >>> 16,
            c = 0,
            l = 0,
            d = 0,
            p = 0;
        return d += (p += o + (65535 & t.low)) >>> 16, l += (d += n + u) >>> 16, c += (l += r + s) >>> 16, c += e + a, f((d &= 65535) << 16 | (p &= 65535), (c &= 65535) << 16 | (l &= 65535), this.unsigned);
      }, k.subtract = function (t) {
        return i(t) || (t = h(t)), this.add(t.neg());
      }, k.sub = k.subtract, k.multiply = function (t) {
        if (this.isZero()) return y;
        if (i(t) || (t = h(t)), r) return f(r.mul(this.low, this.high, t.low, t.high), r.get_high(), this.unsigned);
        if (t.isZero()) return y;
        if (this.eq(S)) return t.isOdd() ? S : y;
        if (t.eq(S)) return this.isOdd() ? S : y;
        if (this.isNegative()) return t.isNegative() ? this.neg().mul(t.neg()) : this.neg().mul(t).neg();
        if (t.isNegative()) return this.mul(t.neg()).neg();
        if (this.lt(v) && t.lt(v)) return u(this.toNumber() * t.toNumber(), this.unsigned);
        var e = this.high >>> 16,
            n = 65535 & this.high,
            o = this.low >>> 16,
            a = 65535 & this.low,
            s = t.high >>> 16,
            c = 65535 & t.high,
            l = t.low >>> 16,
            d = 65535 & t.low,
            p = 0,
            g = 0,
            m = 0,
            b = 0;
        return m += (b += a * d) >>> 16, g += (m += o * d) >>> 16, m &= 65535, g += (m += a * l) >>> 16, p += (g += n * d) >>> 16, g &= 65535, p += (g += o * l) >>> 16, g &= 65535, p += (g += a * c) >>> 16, p += e * d + n * l + o * c + a * s, f((m &= 65535) << 16 | (b &= 65535), (p &= 65535) << 16 | (g &= 65535), this.unsigned);
      }, k.mul = k.multiply, k.divide = function (t) {
        if (i(t) || (t = h(t)), t.isZero()) throw Error("division by zero");
        var e, n, o;
        if (r) return this.unsigned || -2147483648 !== this.high || -1 !== t.low || -1 !== t.high ? f((this.unsigned ? r.div_u : r.div_s)(this.low, this.high, t.low, t.high), r.get_high(), this.unsigned) : this;
        if (this.isZero()) return this.unsigned ? m : y;

        if (this.unsigned) {
          if (t.unsigned || (t = t.toUnsigned()), t.gt(this)) return m;
          if (t.gt(this.shru(1))) return _;
          o = m;
        } else {
          if (this.eq(S)) return t.eq(b) || t.eq(w) ? S : t.eq(S) ? b : (e = this.shr(1).div(t).shl(1)).eq(y) ? t.isNegative() ? b : w : (n = this.sub(t.mul(e)), o = e.add(n.div(t)));
          if (t.eq(S)) return this.unsigned ? m : y;
          if (this.isNegative()) return t.isNegative() ? this.neg().div(t.neg()) : this.neg().div(t).neg();
          if (t.isNegative()) return this.div(t.neg()).neg();
          o = y;
        }

        for (n = this; n.gte(t);) {
          e = Math.max(1, Math.floor(n.toNumber() / t.toNumber()));

          for (var a = Math.ceil(Math.log(e) / Math.LN2), s = a <= 48 ? 1 : c(2, a - 48), l = u(e), d = l.mul(t); d.isNegative() || d.gt(n);) d = (l = u(e -= s, this.unsigned)).mul(t);

          l.isZero() && (l = b), o = o.add(l), n = n.sub(d);
        }

        return o;
      }, k.div = k.divide, k.modulo = function (t) {
        return i(t) || (t = h(t)), r ? f((this.unsigned ? r.rem_u : r.rem_s)(this.low, this.high, t.low, t.high), r.get_high(), this.unsigned) : this.sub(this.div(t).mul(t));
      }, k.mod = k.modulo, k.rem = k.modulo, k.not = function () {
        return f(~this.low, ~this.high, this.unsigned);
      }, k.and = function (t) {
        return i(t) || (t = h(t)), f(this.low & t.low, this.high & t.high, this.unsigned);
      }, k.or = function (t) {
        return i(t) || (t = h(t)), f(this.low | t.low, this.high | t.high, this.unsigned);
      }, k.xor = function (t) {
        return i(t) || (t = h(t)), f(this.low ^ t.low, this.high ^ t.high, this.unsigned);
      }, k.shiftLeft = function (t) {
        return i(t) && (t = t.toInt()), 0 == (t &= 63) ? this : t < 32 ? f(this.low << t, this.high << t | this.low >>> 32 - t, this.unsigned) : f(0, this.low << t - 32, this.unsigned);
      }, k.shl = k.shiftLeft, k.shiftRight = function (t) {
        return i(t) && (t = t.toInt()), 0 == (t &= 63) ? this : t < 32 ? f(this.low >>> t | this.high << 32 - t, this.high >> t, this.unsigned) : f(this.high >> t - 32, this.high >= 0 ? 0 : -1, this.unsigned);
      }, k.shr = k.shiftRight, k.shiftRightUnsigned = function (t) {
        if (i(t) && (t = t.toInt()), 0 === (t &= 63)) return this;
        var e = this.high;
        return t < 32 ? f(this.low >>> t | e << 32 - t, e >>> t, this.unsigned) : f(32 === t ? e : e >>> t - 32, 0, this.unsigned);
      }, k.shru = k.shiftRightUnsigned, k.shr_u = k.shiftRightUnsigned, k.toSigned = function () {
        return this.unsigned ? f(this.low, this.high, !1) : this;
      }, k.toUnsigned = function () {
        return this.unsigned ? this : f(this.low, this.high, !0);
      }, k.toBytes = function (t) {
        return t ? this.toBytesLE() : this.toBytesBE();
      }, k.toBytesLE = function () {
        var t = this.high,
            e = this.low;
        return [255 & e, e >>> 8 & 255, e >>> 16 & 255, e >>> 24, 255 & t, t >>> 8 & 255, t >>> 16 & 255, t >>> 24];
      }, k.toBytesBE = function () {
        var t = this.high,
            e = this.low;
        return [t >>> 24, t >>> 16 & 255, t >>> 8 & 255, 255 & t, e >>> 24, e >>> 16 & 255, e >>> 8 & 255, 255 & e];
      }, n.fromBytes = function (t, e, r) {
        return r ? n.fromBytesLE(t, e) : n.fromBytesBE(t, e);
      }, n.fromBytesLE = function (t, e) {
        return new n(t[0] | t[1] << 8 | t[2] << 16 | t[3] << 24, t[4] | t[5] << 8 | t[6] << 16 | t[7] << 24, e);
      }, n.fromBytesBE = function (t, e) {
        return new n(t[4] << 24 | t[5] << 16 | t[6] << 8 | t[7], t[0] << 24 | t[1] << 16 | t[2] << 8 | t[3], e);
      };
    }, function (t, e) {
      var r = {
        utf8: {
          stringToBytes: function (t) {
            return r.bin.stringToBytes(unescape(encodeURIComponent(t)));
          },
          bytesToString: function (t) {
            return decodeURIComponent(escape(r.bin.bytesToString(t)));
          }
        },
        bin: {
          stringToBytes: function (t) {
            for (var e = [], r = 0; r < t.length; r++) e.push(255 & t.charCodeAt(r));

            return e;
          },
          bytesToString: function (t) {
            for (var e = [], r = 0; r < t.length; r++) e.push(String.fromCharCode(t[r]));

            return e.join("");
          }
        }
      };
      t.exports = r;
    }, function (t, e, r) {
      t.exports = r(335);
    }, function (t, e, r) {
      var n = r(17),
          i = r(51),
          o = r(28),
          a = r(65).f,
          s = function (t) {
        return function (e) {
          for (var r, s = o(e), u = i(s), f = u.length, c = 0, l = []; f > c;) r = u[c++], n && !a.call(s, r) || l.push(t ? [r, s[r]] : s[r]);

          return l;
        };
      };

      t.exports = {
        entries: s(!0),
        values: s(!1)
      };
    }, function (t, e, r) {
      t.exports = r(338);
    }, function (t, e, r) {
      t.exports = r(352);
    }, function (t, e, r) {
      var n = r(355),
          i = r(153);

      t.exports = function (t) {
        if (i(Object(t)) || "[object Arguments]" === Object.prototype.toString.call(t)) return n(t);
      };
    }, function (t, e, r) {
      t.exports = r(360);
    }, function (t, e, r) {
      t.exports = r(372);
    }, function (t, e, r) {
      var n = r(13),
          i = r(375).trim,
          o = r(156),
          a = n.parseInt,
          s = /^[+-]?0[Xx]/,
          u = 8 !== a(o + "08") || 22 !== a(o + "0x16");
      t.exports = u ? function (t, e) {
        var r = i(String(t));
        return a(r, e >>> 0 || (s.test(r) ? 16 : 10));
      } : a;
    }, function (t, e) {
      t.exports = "\t\n\v\f\r                　\u2028\u2029\ufeff";
    }, function (t, e, r) {
      var n = r(151);

      t.exports = function (t) {
        if (n(t)) return t;
      };
    }, function (t, e) {
      t.exports = function () {
        throw new TypeError("Invalid attempt to destructure non-iterable instance");
      };
    }, function (t, e, r) {
      t.exports = r(393);
    }, function (t, e, r) {

      var n = r(419),
          i = r(426),
          o = r(429);

      function a(t) {
        var e = n.parse(t),
            r = e.protocol,
            a = e.pathname;
        return "file:" === r ? new o(unescape(a)) : new i(t);
      }

      t.exports = {
        LocalFile: o,
        RemoteFile: i,
        fromUrl: a,
        open: function (t, e, r) {
          if (r) return r;
          if (t) return a(t);
          if (e) return new o(e);
          throw new Error("no url, path, or filehandle provided, cannot open");
        }
      };
    }, function (t, e, r) {

      Object.defineProperty(e, "__esModule", {
        value: !0
      }), e.promisify = i;
      var n = "__ES6-PROMISIFY--CUSTOM-ARGUMENTS__";

      function i(t) {
        if ("function" != typeof t) throw new TypeError("Argument to promisify must be a function");
        var e = t[n],
            r = i.Promise || Promise;
        if ("function" != typeof r) throw new Error("No Promise implementation found; do you need a polyfill?");
        return function () {
          for (var n = this, i = arguments.length, o = new Array(i), a = 0; a < i; a++) o[a] = arguments[a];

          return new r(function (r, i) {
            o.push(function (t) {
              if (t) return i(t);

              for (var n = arguments.length, o = new Array(n > 1 ? n - 1 : 0), a = 1; a < n; a++) o[a - 1] = arguments[a];

              if (1 === o.length || !e) return r(o[0]);
              var s = {};
              o.forEach(function (t, r) {
                var n = e[r];
                n && (s[n] = t);
              }), r(s);
            }), t.call.apply(t, [n].concat(o));
          });
        };
      }

      i.argumentNames = n, i.Promise = void 0;
    }, function (t, e, r) {

      (function (t) {
        Object.defineProperty(e, "__esModule", {
          value: !0
        });

        var n = r(449),
            i = function () {
          if ("undefined" != typeof self) return self;
          if ("undefined" != typeof window) return window;
          if (void 0 !== t) return t;
          throw new Error("unable to locate global object");
        },
            o = void 0 === i().AbortController ? n.AbortController : i().AbortController;

        e.AbortController = o;
        var a = void 0 === i().AbortController ? n.AbortSignal : i().AbortSignal;
        e.AbortSignal = a;
      }).call(this, r(23));
    }, function (t, e, r) {

      var n = r(104),
          i = r(432),
          o = r(439);
      t.exports = {
        CramFile: n,
        IndexedCramFile: i,
        CraiIndex: o
      };
    }, function (t, e, r) {

      (function (e) {
        var n,
            i = r(1),
            o = i(r(16)),
            a = i(r(37)),
            s = i(r(30)),
            u = i(r(31)),
            f = i(r(7)),
            c = i(r(10)),
            l = r(126),
            h = r(224),
            d = r(94),
            p = r(9),
            g = p.CramUnimplementedError,
            v = p.CramMalformedError,
            y = r(309),
            m = r(318),
            b = m.cramFileDefinition,
            _ = m.getSectionParsers,
            w = r(329),
            x = r(160).open,
            E = r(58),
            S = E.parseItem,
            k = E.tinyMemoize,
            A = r(430).parseHeaderText,
            C = function () {
          function t(e) {
            (0, f.default)(this, t), this.file = x(e.url, e.path, e.filehandle), this.validateChecksums = !0, this.fetchReferenceSequenceCallback = e.seqFetch, this.options = {
              checkSequenceMD5: !1 !== e.checkSequenceMD5,
              cacheSize: void 0 !== e.cacheSize ? e.cacheSize : 2e4
            }, this.featureCache = new d({
              maxSize: this.options.cacheSize
            });
          }

          var r, n, i, o, p, m, E, k, C, T;
          return (0, c.default)(t, [{
            key: "toString",
            value: function () {
              return this.file.filename ? this.file.filename : this.file.url ? this.file.url : "(cram file)";
            }
          }, {
            key: "read",
            value: function (t, e, r, n) {
              return this.file.read(t, e, r, n);
            }
          }, {
            key: "stat",
            value: function () {
              return this.file.stat();
            }
          }, {
            key: "getDefinition",
            value: (T = (0, u.default)(s.default.mark(function t() {
              var r, n;
              return s.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    return r = e.allocUnsafe(b.maxLength), t.next = 3, this.file.read(r, 0, b.maxLength, 0);

                  case 3:
                    if (2 === (n = b.parser.parse(r).result).majorVersion || 3 === n.majorVersion) {
                      t.next = 6;
                      break;
                    }

                    throw new g("CRAM version ".concat(n.majorVersion, " not supported"));

                  case 6:
                    return t.abrupt("return", n);

                  case 7:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function () {
              return T.apply(this, arguments);
            })
          }, {
            key: "getSamHeader",
            value: (C = (0, u.default)(s.default.mark(function t() {
              var e, r, n, i, o;
              return s.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    return t.next = 2, this.getContainerById(0);

                  case 2:
                    if (e = t.sent) {
                      t.next = 5;
                      break;
                    }

                    throw new v("file contains no containers");

                  case 5:
                    return t.next = 7, e.getFirstBlock();

                  case 7:
                    return r = t.sent, n = r.content, i = n.readInt32LE(0), o = n.toString("utf8", 4, 4 + i), this.header = o, t.abrupt("return", A(o));

                  case 14:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function () {
              return C.apply(this, arguments);
            })
          }, {
            key: "getHeaderText",
            value: (k = (0, u.default)(s.default.mark(function t() {
              return s.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    return t.next = 2, this.getSamHeader();

                  case 2:
                    return t.abrupt("return", this.header);

                  case 3:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function () {
              return k.apply(this, arguments);
            })
          }, {
            key: "getSectionParsers",
            value: (E = (0, u.default)(s.default.mark(function t() {
              var e, r;
              return s.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    return t.next = 2, this.getDefinition();

                  case 2:
                    return e = t.sent, r = e.majorVersion, t.abrupt("return", _(r));

                  case 5:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function () {
              return E.apply(this, arguments);
            })
          }, {
            key: "getContainerById",
            value: (m = (0, u.default)(s.default.mark(function t(e) {
              var r, n, i, o, a, u, f, c, l, h;
              return s.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    return t.next = 2, this.getSectionParsers();

                  case 2:
                    return r = t.sent, n = r.cramFileDefinition.maxLength, t.next = 6, this.file.stat();

                  case 6:
                    i = t.sent, o = i.size, a = r.cramContainerHeader1, f = 0;

                  case 10:
                    if (!(f <= e)) {
                      t.next = 36;
                      break;
                    }

                    if (!(n + a.maxLength + 8 >= o)) {
                      t.next = 13;
                      break;
                    }

                    return t.abrupt("return", void 0);

                  case 13:
                    return u = this.getContainerAtPosition(n), t.next = 16, u.getHeader();

                  case 16:
                    if (c = t.sent) {
                      t.next = 19;
                      break;
                    }

                    throw new v("container ".concat(e, " not found in file"));

                  case 19:
                    if (0 !== f) {
                      t.next = 32;
                      break;
                    }

                    n = c._endPosition, l = 0;

                  case 22:
                    if (!(l < c.numBlocks)) {
                      t.next = 30;
                      break;
                    }

                    return t.next = 25, this.readBlock(n);

                  case 25:
                    h = t.sent, n = h._endPosition;

                  case 27:
                    l += 1, t.next = 22;
                    break;

                  case 30:
                    t.next = 33;
                    break;

                  case 32:
                    n += c._size + c.length;

                  case 33:
                    f += 1, t.next = 10;
                    break;

                  case 36:
                    return t.abrupt("return", u);

                  case 37:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function (t) {
              return m.apply(this, arguments);
            })
          }, {
            key: "checkCrc32",
            value: (p = (0, u.default)(s.default.mark(function t(r, n, i, o) {
              var u, f, c, l;
              return s.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    return u = e.allocUnsafe(n), t.next = 3, this.file.read(u, 0, n, r);

                  case 3:
                    if ((f = h.unsigned(u)) === i) {
                      t.next = 6;
                      break;
                    }

                    throw new v((0, a.default)(c = (0, a.default)(l = "crc mismatch in ".concat(o, ": recorded CRC32 = ")).call(l, i, ", but calculated CRC32 = ")).call(c, f));

                  case 6:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function (t, e, r, n) {
              return p.apply(this, arguments);
            })
          }, {
            key: "containerCount",
            value: (o = (0, u.default)(s.default.mark(function t() {
              var e, r, n, i, o, a, u, f, c;
              return s.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    return t.next = 2, this.getSectionParsers();

                  case 2:
                    return e = t.sent, t.next = 5, this.file.stat();

                  case 5:
                    r = t.sent, n = r.size, i = e.cramContainerHeader1, o = 0, a = e.cramFileDefinition.maxLength;

                  case 10:
                    if (!(a + i.maxLength + 8 < n)) {
                      t.next = 33;
                      break;
                    }

                    return t.next = 13, this.getContainerAtPosition(a).getHeader();

                  case 13:
                    if (u = t.sent) {
                      t.next = 16;
                      break;
                    }

                    return t.abrupt("break", 33);

                  case 16:
                    if (0 !== o) {
                      t.next = 29;
                      break;
                    }

                    a = u._endPosition, f = 0;

                  case 19:
                    if (!(f < u.numBlocks)) {
                      t.next = 27;
                      break;
                    }

                    return t.next = 22, this.readBlock(a);

                  case 22:
                    c = t.sent, a = c._endPosition;

                  case 24:
                    f += 1, t.next = 19;
                    break;

                  case 27:
                    t.next = 30;
                    break;

                  case 29:
                    a += u._size + u.length;

                  case 30:
                    o += 1, t.next = 10;
                    break;

                  case 33:
                    return t.abrupt("return", o);

                  case 34:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function () {
              return o.apply(this, arguments);
            })
          }, {
            key: "getContainerAtPosition",
            value: function (t) {
              return new w(this, t);
            }
          }, {
            key: "readBlockHeader",
            value: (i = (0, u.default)(s.default.mark(function t(r) {
              var n, i, o, a, u;
              return s.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    return t.next = 2, this.getSectionParsers();

                  case 2:
                    return n = t.sent, i = n.cramBlockHeader, t.next = 6, this.file.stat();

                  case 6:
                    if (o = t.sent, a = o.size, !(r + i.maxLength >= a)) {
                      t.next = 10;
                      break;
                    }

                    return t.abrupt("return", void 0);

                  case 10:
                    return u = e.allocUnsafe(i.maxLength), t.next = 13, this.file.read(u, 0, i.maxLength, r);

                  case 13:
                    return t.abrupt("return", S(u, i.parser, 0, r));

                  case 14:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function (t) {
              return i.apply(this, arguments);
            })
          }, {
            key: "_parseSection",
            value: (n = (0, u.default)(s.default.mark(function t(r, n) {
              var i,
                  o,
                  u,
                  f,
                  c,
                  l,
                  h,
                  d = arguments;
              return s.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    if (o = d.length > 2 && void 0 !== d[2] ? d[2] : r.maxLength, !(u = d.length > 3 ? d[3] : void 0)) {
                      t.next = 6;
                      break;
                    }

                    f = u, t.next = 15;
                    break;

                  case 6:
                    return t.next = 8, this.file.stat();

                  case 8:
                    if (c = t.sent, l = c.size, !(n + o >= l)) {
                      t.next = 12;
                      break;
                    }

                    return t.abrupt("return", void 0);

                  case 12:
                    return f = e.allocUnsafe(o), t.next = 15, this.file.read(f, 0, o, n);

                  case 15:
                    if ((h = S(f, r.parser, 0, n))._size === o) {
                      t.next = 18;
                      break;
                    }

                    throw new v((0, a.default)(i = "section read error: requested size ".concat(o, " does not equal parsed size ")).call(i, h._size));

                  case 18:
                    return t.abrupt("return", h);

                  case 19:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function (t, e) {
              return n.apply(this, arguments);
            })
          }, {
            key: "_uncompress",
            value: function (t, e, r) {
              if ("gzip" === t) {
                l.gunzipSync(e).copy(r);
              } else {
                if ("rans" !== t) throw new g("".concat(t, " decompression not yet implemented"));
                y.uncompress(e, r);
              }
            }
          }, {
            key: "readBlock",
            value: (r = (0, u.default)(s.default.mark(function t(r) {
              var n, i, o, a, u, f, c, l;
              return s.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    return t.next = 2, this.getDefinition();

                  case 2:
                    return n = t.sent, i = n.majorVersion, t.next = 6, this.getSectionParsers();

                  case 6:
                    return o = t.sent, t.next = 9, this.readBlockHeader(r);

                  case 9:
                    if (a = t.sent, u = a._endPosition, a.contentPosition = a._endPosition, f = e.allocUnsafe(a.uncompressedSize), "raw" === a.compressionMethod) {
                      t.next = 20;
                      break;
                    }

                    return c = e.allocUnsafe(a.compressedSize), t.next = 17, this.read(c, 0, a.compressedSize, u);

                  case 17:
                    this._uncompress(a.compressionMethod, c, f), t.next = 22;
                    break;

                  case 20:
                    return t.next = 22, this.read(f, 0, a.uncompressedSize, u);

                  case 22:
                    if (a.content = f, !(i >= 3)) {
                      t.next = 35;
                      break;
                    }

                    return t.next = 26, this._parseSection(o.cramBlockCrc32, u + a.compressedSize);

                  case 26:
                    if (l = t.sent, a.crc32 = l.crc32, !this.validateChecksums) {
                      t.next = 31;
                      break;
                    }

                    return t.next = 31, this.checkCrc32(r, a._size + a.compressedSize, a.crc32, "block data");

                  case 31:
                    a._endPosition = l._endPosition, a._size = a.compressedSize + o.cramBlockCrc32.maxLength, t.next = 37;
                    break;

                  case 35:
                    a._endPosition = u + a.compressedSize, a._size = a.compressedSize;

                  case 37:
                    return t.abrupt("return", a);

                  case 38:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function (t) {
              return r.apply(this, arguments);
            })
          }]), t;
        }();

        (0, o.default)(n = "getDefinition getSectionParsers getSamHeader".split(" ")).call(n, function (t) {
          return k(C, t);
        }), t.exports = C;
      }).call(this, r(19).Buffer);
    }, function (t, e, r) {

      e.byteLength = function (t) {
        var e = f(t),
            r = e[0],
            n = e[1];
        return 3 * (r + n) / 4 - n;
      }, e.toByteArray = function (t) {
        var e,
            r,
            n = f(t),
            a = n[0],
            s = n[1],
            u = new o(function (t, e, r) {
          return 3 * (e + r) / 4 - r;
        }(0, a, s)),
            c = 0,
            l = s > 0 ? a - 4 : a;

        for (r = 0; r < l; r += 4) e = i[t.charCodeAt(r)] << 18 | i[t.charCodeAt(r + 1)] << 12 | i[t.charCodeAt(r + 2)] << 6 | i[t.charCodeAt(r + 3)], u[c++] = e >> 16 & 255, u[c++] = e >> 8 & 255, u[c++] = 255 & e;

        2 === s && (e = i[t.charCodeAt(r)] << 2 | i[t.charCodeAt(r + 1)] >> 4, u[c++] = 255 & e);
        1 === s && (e = i[t.charCodeAt(r)] << 10 | i[t.charCodeAt(r + 1)] << 4 | i[t.charCodeAt(r + 2)] >> 2, u[c++] = e >> 8 & 255, u[c++] = 255 & e);
        return u;
      }, e.fromByteArray = function (t) {
        for (var e, r = t.length, i = r % 3, o = [], a = 0, s = r - i; a < s; a += 16383) o.push(c(t, a, a + 16383 > s ? s : a + 16383));

        1 === i ? (e = t[r - 1], o.push(n[e >> 2] + n[e << 4 & 63] + "==")) : 2 === i && (e = (t[r - 2] << 8) + t[r - 1], o.push(n[e >> 10] + n[e >> 4 & 63] + n[e << 2 & 63] + "="));
        return o.join("");
      };

      for (var n = [], i = [], o = "undefined" != typeof Uint8Array ? Uint8Array : Array, a = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", s = 0, u = a.length; s < u; ++s) n[s] = a[s], i[a.charCodeAt(s)] = s;

      function f(t) {
        var e = t.length;
        if (e % 4 > 0) throw new Error("Invalid string. Length must be a multiple of 4");
        var r = t.indexOf("=");
        return -1 === r && (r = e), [r, r === e ? 0 : 4 - r % 4];
      }

      function c(t, e, r) {
        for (var i, o, a = [], s = e; s < r; s += 3) i = (t[s] << 16 & 16711680) + (t[s + 1] << 8 & 65280) + (255 & t[s + 2]), a.push(n[(o = i) >> 18 & 63] + n[o >> 12 & 63] + n[o >> 6 & 63] + n[63 & o]);

        return a.join("");
      }

      i["-".charCodeAt(0)] = 62, i["_".charCodeAt(0)] = 63;
    }, function (t, e) {
      e.read = function (t, e, r, n, i) {
        var o,
            a,
            s = 8 * i - n - 1,
            u = (1 << s) - 1,
            f = u >> 1,
            c = -7,
            l = r ? i - 1 : 0,
            h = r ? -1 : 1,
            d = t[e + l];

        for (l += h, o = d & (1 << -c) - 1, d >>= -c, c += s; c > 0; o = 256 * o + t[e + l], l += h, c -= 8);

        for (a = o & (1 << -c) - 1, o >>= -c, c += n; c > 0; a = 256 * a + t[e + l], l += h, c -= 8);

        if (0 === o) o = 1 - f;else {
          if (o === u) return a ? NaN : 1 / 0 * (d ? -1 : 1);
          a += Math.pow(2, n), o -= f;
        }
        return (d ? -1 : 1) * a * Math.pow(2, o - n);
      }, e.write = function (t, e, r, n, i, o) {
        var a,
            s,
            u,
            f = 8 * o - i - 1,
            c = (1 << f) - 1,
            l = c >> 1,
            h = 23 === i ? Math.pow(2, -24) - Math.pow(2, -77) : 0,
            d = n ? 0 : o - 1,
            p = n ? 1 : -1,
            g = e < 0 || 0 === e && 1 / e < 0 ? 1 : 0;

        for (e = Math.abs(e), isNaN(e) || e === 1 / 0 ? (s = isNaN(e) ? 1 : 0, a = c) : (a = Math.floor(Math.log(e) / Math.LN2), e * (u = Math.pow(2, -a)) < 1 && (a--, u *= 2), (e += a + l >= 1 ? h / u : h * Math.pow(2, 1 - l)) * u >= 2 && (a++, u /= 2), a + l >= c ? (s = 0, a = c) : a + l >= 1 ? (s = (e * u - 1) * Math.pow(2, i), a += l) : (s = e * Math.pow(2, l - 1) * Math.pow(2, i), a = 0)); i >= 8; t[r + d] = 255 & s, d += p, s /= 256, i -= 8);

        for (a = a << i | s, f += i; f > 0; t[r + d] = 255 & a, d += p, a /= 256, f -= 8);

        t[r + d - p] |= 128 * g;
      };
    }, function (t, e, r) {
      r(41);
      var n = r(177),
          i = r(67),
          o = Array.prototype,
          a = {
        DOMTokenList: !0,
        NodeList: !0
      };

      t.exports = function (t) {
        var e = t.forEach;
        return t === o || t instanceof Array && e === o.forEach || a.hasOwnProperty(i(t)) ? n : e;
      };
    }, function (t, e, r) {

      var n = r(28),
          i = r(78),
          o = r(35),
          a = r(42),
          s = r(80),
          u = a.set,
          f = a.getterFor("Array Iterator");
      t.exports = s(Array, "Array", function (t, e) {
        u(this, {
          type: "Array Iterator",
          target: n(t),
          index: 0,
          kind: e
        });
      }, function () {
        var t = f(this),
            e = t.target,
            r = t.kind,
            n = t.index++;
        return !e || n >= e.length ? (t.target = void 0, {
          value: void 0,
          done: !0
        }) : "keys" == r ? {
          value: n,
          done: !1
        } : "values" == r ? {
          value: e[n],
          done: !1
        } : {
          value: [n, e[n]],
          done: !1
        };
      }, "values"), o.Arguments = o.Array, i("keys"), i("values"), i("entries");
    }, function (t, e, r) {
      var n = r(13),
          i = r(170),
          o = n.WeakMap;
      t.exports = "function" == typeof o && /native code/.test(i.call(o));
    }, function (t, e, r) {
      var n = r(61);
      t.exports = n("native-function-to-string", Function.toString);
    }, function (t, e, r) {
      var n = r(13),
          i = r(20);

      t.exports = function (t, e) {
        try {
          i(n, t, e);
        } catch (r) {
          n[t] = e;
        }

        return e;
      };
    }, function (t, e, r) {

      var n = r(108).IteratorPrototype,
          i = r(44),
          o = r(43),
          a = r(36),
          s = r(35),
          u = function () {
        return this;
      };

      t.exports = function (t, e, r) {
        var f = e + " Iterator";
        return t.prototype = i(n, {
          next: o(1, r)
        }), a(t, f, !1, !0), s[f] = u, t;
      };
    }, function (t, e, r) {
      var n = r(17),
          i = r(21),
          o = r(2),
          a = r(51);
      t.exports = n ? Object.defineProperties : function (t, e) {
        o(t);

        for (var r, n = a(e), s = n.length, u = 0; s > u;) i.f(t, r = n[u++], e[r]);

        return t;
      };
    }, function (t, e, r) {

      var n = r(67),
          i = {};
      i[r(6)("toStringTag")] = "z", t.exports = "[object z]" !== String(i) ? function () {
        return "[object " + n(this) + "]";
      } : i.toString;
    }, function (t, e, r) {
      var n = r(14);

      t.exports = function (t) {
        if (!n(t) && null !== t) throw TypeError("Can't set " + String(t) + " as a prototype");
        return t;
      };
    }, function (t, e) {
      t.exports = {
        CSSRuleList: 0,
        CSSStyleDeclaration: 0,
        CSSValueList: 0,
        ClientRectList: 0,
        DOMRectList: 0,
        DOMStringList: 0,
        DOMTokenList: 1,
        DataTransferItemList: 0,
        FileList: 0,
        HTMLAllCollection: 0,
        HTMLCollection: 0,
        HTMLFormElement: 0,
        HTMLSelectElement: 0,
        MediaList: 0,
        MimeTypeArray: 0,
        NamedNodeMap: 0,
        NodeList: 1,
        PaintRequestList: 0,
        Plugin: 0,
        PluginArray: 0,
        SVGLengthList: 0,
        SVGNumberList: 0,
        SVGPathSegList: 0,
        SVGPointList: 0,
        SVGStringList: 0,
        SVGTransformList: 0,
        SourceBufferList: 0,
        StyleSheetList: 0,
        TextTrackCueList: 0,
        TextTrackList: 0,
        TouchList: 0
      };
    }, function (t, e, r) {
      t.exports = r(178);
    }, function (t, e, r) {
      r(179);
      var n = r(18);
      t.exports = n("Array").forEach;
    }, function (t, e, r) {

      var n = r(0),
          i = r(180);
      n({
        target: "Array",
        proto: !0,
        forced: [].forEach != i
      }, {
        forEach: i
      });
    }, function (t, e, r) {

      var n = r(45).forEach,
          i = r(54);
      t.exports = i("forEach") ? function (t) {
        return n(this, t, arguments.length > 1 ? arguments[1] : void 0);
      } : [].forEach;
    }, function (t, e, r) {
      t.exports = r(182);
    }, function (t, e, r) {
      var n = r(183),
          i = Array.prototype;

      t.exports = function (t) {
        var e = t.concat;
        return t === i || t instanceof Array && e === i.concat ? n : e;
      };
    }, function (t, e, r) {
      r(114);
      var n = r(18);
      t.exports = n("Array").concat;
    }, function (t, e, r) {
      var n = function (t) {

        var e,
            r = Object.prototype,
            n = r.hasOwnProperty,
            i = "function" == typeof Symbol ? Symbol : {},
            o = i.iterator || "@@iterator",
            a = i.asyncIterator || "@@asyncIterator",
            s = i.toStringTag || "@@toStringTag";

        function u(t, e, r, n) {
          var i = e && e.prototype instanceof g ? e : g,
              o = Object.create(i.prototype),
              a = new C(n || []);
          return o._invoke = function (t, e, r) {
            var n = c;
            return function (i, o) {
              if (n === h) throw new Error("Generator is already running");

              if (n === d) {
                if ("throw" === i) throw o;
                return R();
              }

              for (r.method = i, r.arg = o;;) {
                var a = r.delegate;

                if (a) {
                  var s = S(a, r);

                  if (s) {
                    if (s === p) continue;
                    return s;
                  }
                }

                if ("next" === r.method) r.sent = r._sent = r.arg;else if ("throw" === r.method) {
                  if (n === c) throw n = d, r.arg;
                  r.dispatchException(r.arg);
                } else "return" === r.method && r.abrupt("return", r.arg);
                n = h;
                var u = f(t, e, r);

                if ("normal" === u.type) {
                  if (n = r.done ? d : l, u.arg === p) continue;
                  return {
                    value: u.arg,
                    done: r.done
                  };
                }

                "throw" === u.type && (n = d, r.method = "throw", r.arg = u.arg);
              }
            };
          }(t, r, a), o;
        }

        function f(t, e, r) {
          try {
            return {
              type: "normal",
              arg: t.call(e, r)
            };
          } catch (t) {
            return {
              type: "throw",
              arg: t
            };
          }
        }

        t.wrap = u;
        var c = "suspendedStart",
            l = "suspendedYield",
            h = "executing",
            d = "completed",
            p = {};

        function g() {}

        function v() {}

        function y() {}

        var m = {};

        m[o] = function () {
          return this;
        };

        var b = Object.getPrototypeOf,
            _ = b && b(b(T([])));

        _ && _ !== r && n.call(_, o) && (m = _);
        var w = y.prototype = g.prototype = Object.create(m);

        function x(t) {
          ["next", "throw", "return"].forEach(function (e) {
            t[e] = function (t) {
              return this._invoke(e, t);
            };
          });
        }

        function E(t, e) {
          var r;

          this._invoke = function (i, o) {
            function a() {
              return new e(function (r, a) {
                !function r(i, o, a, s) {
                  var u = f(t[i], t, o);

                  if ("throw" !== u.type) {
                    var c = u.arg,
                        l = c.value;
                    return l && "object" == typeof l && n.call(l, "__await") ? e.resolve(l.__await).then(function (t) {
                      r("next", t, a, s);
                    }, function (t) {
                      r("throw", t, a, s);
                    }) : e.resolve(l).then(function (t) {
                      c.value = t, a(c);
                    }, function (t) {
                      return r("throw", t, a, s);
                    });
                  }

                  s(u.arg);
                }(i, o, r, a);
              });
            }

            return r = r ? r.then(a, a) : a();
          };
        }

        function S(t, r) {
          var n = t.iterator[r.method];

          if (n === e) {
            if (r.delegate = null, "throw" === r.method) {
              if (t.iterator.return && (r.method = "return", r.arg = e, S(t, r), "throw" === r.method)) return p;
              r.method = "throw", r.arg = new TypeError("The iterator does not provide a 'throw' method");
            }

            return p;
          }

          var i = f(n, t.iterator, r.arg);
          if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, p;
          var o = i.arg;
          return o ? o.done ? (r[t.resultName] = o.value, r.next = t.nextLoc, "return" !== r.method && (r.method = "next", r.arg = e), r.delegate = null, p) : o : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, p);
        }

        function k(t) {
          var e = {
            tryLoc: t[0]
          };
          1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e);
        }

        function A(t) {
          var e = t.completion || {};
          e.type = "normal", delete e.arg, t.completion = e;
        }

        function C(t) {
          this.tryEntries = [{
            tryLoc: "root"
          }], t.forEach(k, this), this.reset(!0);
        }

        function T(t) {
          if (t) {
            var r = t[o];
            if (r) return r.call(t);
            if ("function" == typeof t.next) return t;

            if (!isNaN(t.length)) {
              var i = -1,
                  a = function r() {
                for (; ++i < t.length;) if (n.call(t, i)) return r.value = t[i], r.done = !1, r;

                return r.value = e, r.done = !0, r;
              };

              return a.next = a;
            }
          }

          return {
            next: R
          };
        }

        function R() {
          return {
            value: e,
            done: !0
          };
        }

        return v.prototype = w.constructor = y, y.constructor = v, y[s] = v.displayName = "GeneratorFunction", t.isGeneratorFunction = function (t) {
          var e = "function" == typeof t && t.constructor;
          return !!e && (e === v || "GeneratorFunction" === (e.displayName || e.name));
        }, t.mark = function (t) {
          return Object.setPrototypeOf ? Object.setPrototypeOf(t, y) : (t.__proto__ = y, s in t || (t[s] = "GeneratorFunction")), t.prototype = Object.create(w), t;
        }, t.awrap = function (t) {
          return {
            __await: t
          };
        }, x(E.prototype), E.prototype[a] = function () {
          return this;
        }, t.AsyncIterator = E, t.async = function (e, r, n, i, o) {
          void 0 === o && (o = Promise);
          var a = new E(u(e, r, n, i), o);
          return t.isGeneratorFunction(r) ? a : a.next().then(function (t) {
            return t.done ? t.value : a.next();
          });
        }, x(w), w[s] = "Generator", w[o] = function () {
          return this;
        }, w.toString = function () {
          return "[object Generator]";
        }, t.keys = function (t) {
          var e = [];

          for (var r in t) e.push(r);

          return e.reverse(), function r() {
            for (; e.length;) {
              var n = e.pop();
              if (n in t) return r.value = n, r.done = !1, r;
            }

            return r.done = !0, r;
          };
        }, t.values = T, C.prototype = {
          constructor: C,
          reset: function (t) {
            if (this.prev = 0, this.next = 0, this.sent = this._sent = e, this.done = !1, this.delegate = null, this.method = "next", this.arg = e, this.tryEntries.forEach(A), !t) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = e);
          },
          stop: function () {
            this.done = !0;
            var t = this.tryEntries[0].completion;
            if ("throw" === t.type) throw t.arg;
            return this.rval;
          },
          dispatchException: function (t) {
            if (this.done) throw t;
            var r = this;

            function i(n, i) {
              return s.type = "throw", s.arg = t, r.next = n, i && (r.method = "next", r.arg = e), !!i;
            }

            for (var o = this.tryEntries.length - 1; o >= 0; --o) {
              var a = this.tryEntries[o],
                  s = a.completion;
              if ("root" === a.tryLoc) return i("end");

              if (a.tryLoc <= this.prev) {
                var u = n.call(a, "catchLoc"),
                    f = n.call(a, "finallyLoc");

                if (u && f) {
                  if (this.prev < a.catchLoc) return i(a.catchLoc, !0);
                  if (this.prev < a.finallyLoc) return i(a.finallyLoc);
                } else if (u) {
                  if (this.prev < a.catchLoc) return i(a.catchLoc, !0);
                } else {
                  if (!f) throw new Error("try statement without catch or finally");
                  if (this.prev < a.finallyLoc) return i(a.finallyLoc);
                }
              }
            }
          },
          abrupt: function (t, e) {
            for (var r = this.tryEntries.length - 1; r >= 0; --r) {
              var i = this.tryEntries[r];

              if (i.tryLoc <= this.prev && n.call(i, "finallyLoc") && this.prev < i.finallyLoc) {
                var o = i;
                break;
              }
            }

            o && ("break" === t || "continue" === t) && o.tryLoc <= e && e <= o.finallyLoc && (o = null);
            var a = o ? o.completion : {};
            return a.type = t, a.arg = e, o ? (this.method = "next", this.next = o.finallyLoc, p) : this.complete(a);
          },
          complete: function (t, e) {
            if ("throw" === t.type) throw t.arg;
            return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), p;
          },
          finish: function (t) {
            for (var e = this.tryEntries.length - 1; e >= 0; --e) {
              var r = this.tryEntries[e];
              if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), A(r), p;
            }
          },
          catch: function (t) {
            for (var e = this.tryEntries.length - 1; e >= 0; --e) {
              var r = this.tryEntries[e];

              if (r.tryLoc === t) {
                var n = r.completion;

                if ("throw" === n.type) {
                  var i = n.arg;
                  A(r);
                }

                return i;
              }
            }

            throw new Error("illegal catch attempt");
          },
          delegateYield: function (t, r, n) {
            return this.delegate = {
              iterator: T(t),
              resultName: r,
              nextLoc: n
            }, "next" === this.method && (this.arg = e), p;
          }
        }, t;
      }(t.exports);

      try {
        regeneratorRuntime = n;
      } catch (t) {
        Function("r", "regeneratorRuntime = r")(n);
      }
    }, function (t, e, r) {
      t.exports = r(186);
    }, function (t, e, r) {
      t.exports = r(115), r(192), r(193), r(194), r(195);
    }, function (t, e, r) {
      var n = r(83),
          i = r(49),
          o = function (t) {
        return function (e, r) {
          var o,
              a,
              s = String(i(e)),
              u = n(r),
              f = s.length;
          return u < 0 || u >= f ? t ? "" : void 0 : (o = s.charCodeAt(u)) < 55296 || o > 56319 || u + 1 === f || (a = s.charCodeAt(u + 1)) < 56320 || a > 57343 ? t ? s.charAt(u) : o : t ? s.slice(u, u + 2) : a - 56320 + (o - 55296 << 10) + 65536;
        };
      };

      t.exports = {
        codeAt: o(!1),
        charAt: o(!0)
      };
    }, function (t, e, r) {

      var n,
          i,
          o,
          a,
          s = r(0),
          u = r(4),
          f = r(13),
          c = r(3),
          l = r(116),
          h = r(52),
          d = r(117),
          p = r(36),
          g = r(118),
          v = r(14),
          y = r(5),
          m = r(89),
          b = r(34),
          _ = r(8),
          w = r(121),
          x = r(55),
          E = r(122).set,
          S = r(189),
          k = r(124),
          A = r(190),
          C = r(56),
          T = r(69),
          R = r(123),
          O = r(42),
          I = r(107),
          L = r(6)("species"),
          B = "Promise",
          P = O.get,
          F = O.set,
          N = O.getterFor(B),
          M = l,
          j = f.TypeError,
          z = f.document,
          U = f.process,
          D = f.fetch,
          q = U && U.versions,
          Z = q && q.v8 || "",
          H = C.f,
          V = H,
          G = "process" == b(U),
          Y = !!(z && z.createEvent && f.dispatchEvent),
          W = I(B, function () {
        var t = M.resolve(1),
            e = function () {},
            r = (t.constructor = {})[L] = function (t) {
          t(e, e);
        };

        return !((G || "function" == typeof PromiseRejectionEvent) && (!u || t.finally) && t.then(e) instanceof r && 0 !== Z.indexOf("6.6") && -1 === R.indexOf("Chrome/66"));
      }),
          K = W || !w(function (t) {
        M.all(t).catch(function () {});
      }),
          Q = function (t) {
        var e;
        return !(!v(t) || "function" != typeof (e = t.then)) && e;
      },
          X = function (t, e, r) {
        if (!e.notified) {
          e.notified = !0;
          var n = e.reactions;
          S(function () {
            for (var i = e.value, o = 1 == e.state, a = 0; n.length > a;) {
              var s,
                  u,
                  f,
                  c = n[a++],
                  l = o ? c.ok : c.fail,
                  h = c.resolve,
                  d = c.reject,
                  p = c.domain;

              try {
                l ? (o || (2 === e.rejection && et(t, e), e.rejection = 1), !0 === l ? s = i : (p && p.enter(), s = l(i), p && (p.exit(), f = !0)), s === c.promise ? d(j("Promise-chain cycle")) : (u = Q(s)) ? u.call(s, h, d) : h(s)) : d(i);
              } catch (t) {
                p && !f && p.exit(), d(t);
              }
            }

            e.reactions = [], e.notified = !1, r && !e.rejection && J(t, e);
          });
        }
      },
          $ = function (t, e, r) {
        var n, i;
        Y ? ((n = z.createEvent("Event")).promise = e, n.reason = r, n.initEvent(t, !1, !0), f.dispatchEvent(n)) : n = {
          promise: e,
          reason: r
        }, (i = f["on" + t]) ? i(n) : "unhandledrejection" === t && A("Unhandled promise rejection", r);
      },
          J = function (t, e) {
        E.call(f, function () {
          var r,
              n = e.value;
          if (tt(e) && (r = T(function () {
            G ? U.emit("unhandledRejection", n, t) : $("unhandledrejection", t, n);
          }), e.rejection = G || tt(e) ? 2 : 1, r.error)) throw r.value;
        });
      },
          tt = function (t) {
        return 1 !== t.rejection && !t.parent;
      },
          et = function (t, e) {
        E.call(f, function () {
          G ? U.emit("rejectionHandled", t) : $("rejectionhandled", t, e.value);
        });
      },
          rt = function (t, e, r, n) {
        return function (i) {
          t(e, r, i, n);
        };
      },
          nt = function (t, e, r, n) {
        e.done || (e.done = !0, n && (e = n), e.value = r, e.state = 2, X(t, e, !0));
      },
          it = function (t, e, r, n) {
        if (!e.done) {
          e.done = !0, n && (e = n);

          try {
            if (t === r) throw j("Promise can't be resolved itself");
            var i = Q(r);
            i ? S(function () {
              var n = {
                done: !1
              };

              try {
                i.call(r, rt(it, t, n, e), rt(nt, t, n, e));
              } catch (r) {
                nt(t, n, r, e);
              }
            }) : (e.value = r, e.state = 1, X(t, e, !1));
          } catch (r) {
            nt(t, {
              done: !1
            }, r, e);
          }
        }
      };

      W && (M = function (t) {
        m(this, M, B), y(t), n.call(this);
        var e = P(this);

        try {
          t(rt(it, this, e), rt(nt, this, e));
        } catch (t) {
          nt(this, e, t);
        }
      }, (n = function (t) {
        F(this, {
          type: B,
          done: !1,
          notified: !1,
          parent: !1,
          reactions: [],
          rejection: !1,
          state: 0,
          value: void 0
        });
      }).prototype = d(M.prototype, {
        then: function (t, e) {
          var r = N(this),
              n = H(x(this, M));
          return n.ok = "function" != typeof t || t, n.fail = "function" == typeof e && e, n.domain = G ? U.domain : void 0, r.parent = !0, r.reactions.push(n), 0 != r.state && X(this, r, !1), n.promise;
        },
        catch: function (t) {
          return this.then(void 0, t);
        }
      }), i = function () {
        var t = new n(),
            e = P(t);
        this.promise = t, this.resolve = rt(it, t, e), this.reject = rt(nt, t, e);
      }, C.f = H = function (t) {
        return t === M || t === o ? new i(t) : V(t);
      }, u || "function" != typeof l || (a = l.prototype.then, h(l.prototype, "then", function (t, e) {
        var r = this;
        return new M(function (t, e) {
          a.call(r, t, e);
        }).then(t, e);
      }), "function" == typeof D && s({
        global: !0,
        enumerable: !0,
        forced: !0
      }, {
        fetch: function (t) {
          return k(M, D.apply(f, arguments));
        }
      }))), s({
        global: !0,
        wrap: !0,
        forced: W
      }, {
        Promise: M
      }), p(M, B, !1, !0), g(B), o = c.Promise, s({
        target: B,
        stat: !0,
        forced: W
      }, {
        reject: function (t) {
          var e = H(this);
          return e.reject.call(void 0, t), e.promise;
        }
      }), s({
        target: B,
        stat: !0,
        forced: u || W
      }, {
        resolve: function (t) {
          return k(u && this === o ? M : this, t);
        }
      }), s({
        target: B,
        stat: !0,
        forced: K
      }, {
        all: function (t) {
          var e = this,
              r = H(e),
              n = r.resolve,
              i = r.reject,
              o = T(function () {
            var r = y(e.resolve),
                o = [],
                a = 0,
                s = 1;
            _(t, function (t) {
              var u = a++,
                  f = !1;
              o.push(void 0), s++, r.call(e, t).then(function (t) {
                f || (f = !0, o[u] = t, --s || n(o));
              }, i);
            }), --s || n(o);
          });
          return o.error && i(o.value), r.promise;
        },
        race: function (t) {
          var e = this,
              r = H(e),
              n = r.reject,
              i = T(function () {
            var i = y(e.resolve);

            _(t, function (t) {
              i.call(e, t).then(r.resolve, n);
            });
          });
          return i.error && n(i.value), r.promise;
        }
      });
    }, function (t, e, r) {
      var n,
          i,
          o,
          a,
          s,
          u,
          f,
          c,
          l = r(13),
          h = r(81).f,
          d = r(34),
          p = r(122).set,
          g = r(123),
          v = l.MutationObserver || l.WebKitMutationObserver,
          y = l.process,
          m = l.Promise,
          b = "process" == d(y),
          _ = h(l, "queueMicrotask"),
          w = _ && _.value;

      w || (n = function () {
        var t, e;

        for (b && (t = y.domain) && t.exit(); i;) {
          e = i.fn, i = i.next;

          try {
            e();
          } catch (t) {
            throw i ? a() : o = void 0, t;
          }
        }

        o = void 0, t && t.enter();
      }, b ? a = function () {
        y.nextTick(n);
      } : v && !/(iphone|ipod|ipad).*applewebkit/i.test(g) ? (s = !0, u = document.createTextNode(""), new v(n).observe(u, {
        characterData: !0
      }), a = function () {
        u.data = s = !s;
      }) : m && m.resolve ? (f = m.resolve(void 0), c = f.then, a = function () {
        c.call(f, n);
      }) : a = function () {
        p.call(l, n);
      }), t.exports = w || function (t) {
        var e = {
          fn: t,
          next: void 0
        };
        o && (o.next = e), i || (i = e, a()), o = e;
      };
    }, function (t, e, r) {
      var n = r(13);

      t.exports = function (t, e) {
        var r = n.console;
        r && r.error && (1 === arguments.length ? r.error(t) : r.error(t, e));
      };
    }, function (t, e, r) {

      var n = r(0),
          i = r(4),
          o = r(116),
          a = r(26),
          s = r(55),
          u = r(124),
          f = r(52);
      n({
        target: "Promise",
        proto: !0,
        real: !0
      }, {
        finally: function (t) {
          var e = s(this, a("Promise")),
              r = "function" == typeof t;
          return this.then(r ? function (r) {
            return u(e, t()).then(function () {
              return r;
            });
          } : t, r ? function (r) {
            return u(e, t()).then(function () {
              throw r;
            });
          } : t);
        }
      }), i || "function" != typeof o || o.prototype.finally || f(o.prototype, "finally", a("Promise").prototype.finally);
    }, function (t, e, r) {
      var n = r(0),
          i = r(66),
          o = r(86),
          a = r(44),
          s = r(43),
          u = r(8),
          f = r(20),
          c = function (t, e) {
        var r = this;
        if (!(r instanceof c)) return new c(t, e);
        o && (r = o(new Error(e), i(r)));
        var n = [];
        return u(t, n.push, n), f(r, "errors", n), void 0 !== e && f(r, "message", String(e)), r;
      };

      c.prototype = a(Error.prototype, {
        constructor: s(5, c),
        name: s(5, "AggregateError")
      }), n({
        global: !0
      }, {
        AggregateError: c
      });
    }, function (t, e, r) {
      r(125);
    }, function (t, e, r) {

      var n = r(0),
          i = r(56),
          o = r(69);
      n({
        target: "Promise",
        stat: !0
      }, {
        try: function (t) {
          var e = i.f(this),
              r = o(t);
          return (r.error ? e.reject : e.resolve)(r.value), e.promise;
        }
      });
    }, function (t, e, r) {

      var n = r(0),
          i = r(5),
          o = r(26),
          a = r(56),
          s = r(69),
          u = r(8);
      n({
        target: "Promise",
        stat: !0
      }, {
        any: function (t) {
          var e = this,
              r = a.f(e),
              n = r.resolve,
              f = r.reject,
              c = s(function () {
            var r = i(e.resolve),
                a = [],
                s = 0,
                c = 1,
                l = !1;
            u(t, function (t) {
              var i = s++,
                  u = !1;
              a.push(void 0), c++, r.call(e, t).then(function (t) {
                u || l || (l = !0, n(t));
              }, function (t) {
                u || l || (u = !0, a[i] = t, --c || f(new (o("AggregateError"))(a, "No one promise resolved")));
              });
            }), --c || f(new (o("AggregateError"))(a, "No one promise resolved"));
          });
          return c.error && f(c.value), r.promise;
        }
      });
    }, function (t, e, r) {
      t.exports = r(197);
    }, function (t, e, r) {
      t.exports = r(198);
    }, function (t, e, r) {
      r(199);

      var n = r(3).Object,
          i = t.exports = function (t, e, r) {
        return n.defineProperty(t, e, r);
      };

      n.defineProperty.sham && (i.sham = !0);
    }, function (t, e, r) {
      var n = r(0),
          i = r(17);
      n({
        target: "Object",
        stat: !0,
        forced: !i,
        sham: !i
      }, {
        defineProperty: r(21).f
      });
    }, function (t, e, r) {
      t.exports = i;
      var n = r(91).EventEmitter;

      function i() {
        n.call(this);
      }

      r(47)(i, n), i.Readable = r(92), i.Writable = r(208), i.Duplex = r(209), i.Transform = r(210), i.PassThrough = r(211), i.Stream = i, i.prototype.pipe = function (t, e) {
        var r = this;

        function i(e) {
          t.writable && !1 === t.write(e) && r.pause && r.pause();
        }

        function o() {
          r.readable && r.resume && r.resume();
        }

        r.on("data", i), t.on("drain", o), t._isStdio || e && !1 === e.end || (r.on("end", s), r.on("close", u));
        var a = !1;

        function s() {
          a || (a = !0, t.end());
        }

        function u() {
          a || (a = !0, "function" == typeof t.destroy && t.destroy());
        }

        function f(t) {
          if (c(), 0 === n.listenerCount(this, "error")) throw t;
        }

        function c() {
          r.removeListener("data", i), t.removeListener("drain", o), r.removeListener("end", s), r.removeListener("close", u), r.removeListener("error", f), t.removeListener("error", f), r.removeListener("end", c), r.removeListener("close", c), t.removeListener("close", c);
        }

        return r.on("error", f), t.on("error", f), r.on("end", c), r.on("close", c), t.on("close", c), t.emit("pipe", r), t;
      };
    }, function (t, e) {}, function (t, e, r) {

      var n = r(71).Buffer,
          i = r(203);
      t.exports = function () {
        function t() {
          !function (t, e) {
            if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function");
          }(this, t), this.head = null, this.tail = null, this.length = 0;
        }

        return t.prototype.push = function (t) {
          var e = {
            data: t,
            next: null
          };
          this.length > 0 ? this.tail.next = e : this.head = e, this.tail = e, ++this.length;
        }, t.prototype.unshift = function (t) {
          var e = {
            data: t,
            next: this.head
          };
          0 === this.length && (this.tail = e), this.head = e, ++this.length;
        }, t.prototype.shift = function () {
          if (0 !== this.length) {
            var t = this.head.data;
            return 1 === this.length ? this.head = this.tail = null : this.head = this.head.next, --this.length, t;
          }
        }, t.prototype.clear = function () {
          this.head = this.tail = null, this.length = 0;
        }, t.prototype.join = function (t) {
          if (0 === this.length) return "";

          for (var e = this.head, r = "" + e.data; e = e.next;) r += t + e.data;

          return r;
        }, t.prototype.concat = function (t) {
          if (0 === this.length) return n.alloc(0);
          if (1 === this.length) return this.head.data;

          for (var e, r, i, o = n.allocUnsafe(t >>> 0), a = this.head, s = 0; a;) e = a.data, r = o, i = s, e.copy(r, i), s += a.data.length, a = a.next;

          return o;
        }, t;
      }(), i && i.inspect && i.inspect.custom && (t.exports.prototype[i.inspect.custom] = function () {
        var t = i.inspect({
          length: this.length
        });
        return this.constructor.name + " " + t;
      });
    }, function (t, e) {}, function (t, e, r) {
      (function (t) {
        var n = void 0 !== t && t || "undefined" != typeof self && self || window,
            i = Function.prototype.apply;

        function o(t, e) {
          this._id = t, this._clearFn = e;
        }

        e.setTimeout = function () {
          return new o(i.call(setTimeout, n, arguments), clearTimeout);
        }, e.setInterval = function () {
          return new o(i.call(setInterval, n, arguments), clearInterval);
        }, e.clearTimeout = e.clearInterval = function (t) {
          t && t.close();
        }, o.prototype.unref = o.prototype.ref = function () {}, o.prototype.close = function () {
          this._clearFn.call(n, this._id);
        }, e.enroll = function (t, e) {
          clearTimeout(t._idleTimeoutId), t._idleTimeout = e;
        }, e.unenroll = function (t) {
          clearTimeout(t._idleTimeoutId), t._idleTimeout = -1;
        }, e._unrefActive = e.active = function (t) {
          clearTimeout(t._idleTimeoutId);
          var e = t._idleTimeout;
          e >= 0 && (t._idleTimeoutId = setTimeout(function () {
            t._onTimeout && t._onTimeout();
          }, e));
        }, r(205), e.setImmediate = "undefined" != typeof self && self.setImmediate || void 0 !== t && t.setImmediate || this && this.setImmediate, e.clearImmediate = "undefined" != typeof self && self.clearImmediate || void 0 !== t && t.clearImmediate || this && this.clearImmediate;
      }).call(this, r(23));
    }, function (t, e, r) {
      (function (t, e) {
        !function (t, r) {

          if (!t.setImmediate) {
            var n,
                i,
                o,
                a,
                s,
                u = 1,
                f = {},
                c = !1,
                l = t.document,
                h = Object.getPrototypeOf && Object.getPrototypeOf(t);
            h = h && h.setTimeout ? h : t, "[object process]" === {}.toString.call(t.process) ? n = function (t) {
              e.nextTick(function () {
                p(t);
              });
            } : !function () {
              if (t.postMessage && !t.importScripts) {
                var e = !0,
                    r = t.onmessage;
                return t.onmessage = function () {
                  e = !1;
                }, t.postMessage("", "*"), t.onmessage = r, e;
              }
            }() ? t.MessageChannel ? ((o = new MessageChannel()).port1.onmessage = function (t) {
              p(t.data);
            }, n = function (t) {
              o.port2.postMessage(t);
            }) : l && "onreadystatechange" in l.createElement("script") ? (i = l.documentElement, n = function (t) {
              var e = l.createElement("script");
              e.onreadystatechange = function () {
                p(t), e.onreadystatechange = null, i.removeChild(e), e = null;
              }, i.appendChild(e);
            }) : n = function (t) {
              setTimeout(p, 0, t);
            } : (a = "setImmediate$" + Math.random() + "$", s = function (e) {
              e.source === t && "string" == typeof e.data && 0 === e.data.indexOf(a) && p(+e.data.slice(a.length));
            }, t.addEventListener ? t.addEventListener("message", s, !1) : t.attachEvent("onmessage", s), n = function (e) {
              t.postMessage(a + e, "*");
            }), h.setImmediate = function (t) {
              "function" != typeof t && (t = new Function("" + t));

              for (var e = new Array(arguments.length - 1), r = 0; r < e.length; r++) e[r] = arguments[r + 1];

              var i = {
                callback: t,
                args: e
              };
              return f[u] = i, n(u), u++;
            }, h.clearImmediate = d;
          }

          function d(t) {
            delete f[t];
          }

          function p(t) {
            if (c) setTimeout(p, 0, t);else {
              var e = f[t];

              if (e) {
                c = !0;

                try {
                  !function (t) {
                    var e = t.callback,
                        n = t.args;

                    switch (n.length) {
                      case 0:
                        e();
                        break;

                      case 1:
                        e(n[0]);
                        break;

                      case 2:
                        e(n[0], n[1]);
                        break;

                      case 3:
                        e(n[0], n[1], n[2]);
                        break;

                      default:
                        e.apply(r, n);
                    }
                  }(e);
                } finally {
                  d(t), c = !1;
                }
              }
            }
          }
        }("undefined" == typeof self ? void 0 === t ? this : t : self);
      }).call(this, r(23), r(38));
    }, function (t, e, r) {
      (function (e) {
        function r(t) {
          try {
            if (!e.localStorage) return !1;
          } catch (t) {
            return !1;
          }

          var r = e.localStorage[t];
          return null != r && "true" === String(r).toLowerCase();
        }

        t.exports = function (t, e) {
          if (r("noDeprecation")) return t;
          var n = !1;
          return function () {
            if (!n) {
              if (r("throwDeprecation")) throw new Error(e);
              r("traceDeprecation") ? console.trace(e) : console.warn(e), n = !0;
            }

            return t.apply(this, arguments);
          };
        };
      }).call(this, r(23));
    }, function (t, e, r) {

      t.exports = o;
      var n = r(131),
          i = r(57);

      function o(t) {
        if (!(this instanceof o)) return new o(t);
        n.call(this, t);
      }

      i.inherits = r(47), i.inherits(o, n), o.prototype._transform = function (t, e, r) {
        r(null, t);
      };
    }, function (t, e, r) {
      t.exports = r(93);
    }, function (t, e, r) {
      t.exports = r(39);
    }, function (t, e, r) {
      t.exports = r(92).Transform;
    }, function (t, e, r) {
      t.exports = r(92).PassThrough;
    }, function (t, e, r) {

      (function (t, n) {
        var i = r(132),
            o = r(216),
            a = r(217),
            s = r(220),
            u = r(223);

        for (var f in u) e[f] = u[f];

        e.NONE = 0, e.DEFLATE = 1, e.INFLATE = 2, e.GZIP = 3, e.GUNZIP = 4, e.DEFLATERAW = 5, e.INFLATERAW = 6, e.UNZIP = 7;

        function c(t) {
          if ("number" != typeof t || t < e.DEFLATE || t > e.UNZIP) throw new TypeError("Bad argument");
          this.dictionary = null, this.err = 0, this.flush = 0, this.init_done = !1, this.level = 0, this.memLevel = 0, this.mode = t, this.strategy = 0, this.windowBits = 0, this.write_in_progress = !1, this.pending_close = !1, this.gzip_id_bytes_read = 0;
        }

        c.prototype.close = function () {
          this.write_in_progress ? this.pending_close = !0 : (this.pending_close = !1, i(this.init_done, "close before init"), i(this.mode <= e.UNZIP), this.mode === e.DEFLATE || this.mode === e.GZIP || this.mode === e.DEFLATERAW ? a.deflateEnd(this.strm) : this.mode !== e.INFLATE && this.mode !== e.GUNZIP && this.mode !== e.INFLATERAW && this.mode !== e.UNZIP || s.inflateEnd(this.strm), this.mode = e.NONE, this.dictionary = null);
        }, c.prototype.write = function (t, e, r, n, i, o, a) {
          return this._write(!0, t, e, r, n, i, o, a);
        }, c.prototype.writeSync = function (t, e, r, n, i, o, a) {
          return this._write(!1, t, e, r, n, i, o, a);
        }, c.prototype._write = function (r, o, a, s, u, f, c, l) {
          if (i.equal(arguments.length, 8), i(this.init_done, "write before init"), i(this.mode !== e.NONE, "already finalized"), i.equal(!1, this.write_in_progress, "write already in progress"), i.equal(!1, this.pending_close, "close is pending"), this.write_in_progress = !0, i.equal(!1, void 0 === o, "must provide flush value"), this.write_in_progress = !0, o !== e.Z_NO_FLUSH && o !== e.Z_PARTIAL_FLUSH && o !== e.Z_SYNC_FLUSH && o !== e.Z_FULL_FLUSH && o !== e.Z_FINISH && o !== e.Z_BLOCK) throw new Error("Invalid flush value");
          if (null == a && (a = t.alloc(0), u = 0, s = 0), this.strm.avail_in = u, this.strm.input = a, this.strm.next_in = s, this.strm.avail_out = l, this.strm.output = f, this.strm.next_out = c, this.flush = o, !r) return this._process(), this._checkError() ? this._afterSync() : void 0;
          var h = this;
          return n.nextTick(function () {
            h._process(), h._after();
          }), this;
        }, c.prototype._afterSync = function () {
          var t = this.strm.avail_out,
              e = this.strm.avail_in;
          return this.write_in_progress = !1, [e, t];
        }, c.prototype._process = function () {
          var t = null;

          switch (this.mode) {
            case e.DEFLATE:
            case e.GZIP:
            case e.DEFLATERAW:
              this.err = a.deflate(this.strm, this.flush);
              break;

            case e.UNZIP:
              switch (this.strm.avail_in > 0 && (t = this.strm.next_in), this.gzip_id_bytes_read) {
                case 0:
                  if (null === t) break;

                  if (31 !== this.strm.input[t]) {
                    this.mode = e.INFLATE;
                    break;
                  }

                  if (this.gzip_id_bytes_read = 1, t++, 1 === this.strm.avail_in) break;

                case 1:
                  if (null === t) break;
                  139 === this.strm.input[t] ? (this.gzip_id_bytes_read = 2, this.mode = e.GUNZIP) : this.mode = e.INFLATE;
                  break;

                default:
                  throw new Error("invalid number of gzip magic number bytes read");
              }

            case e.INFLATE:
            case e.GUNZIP:
            case e.INFLATERAW:
              for (this.err = s.inflate(this.strm, this.flush), this.err === e.Z_NEED_DICT && this.dictionary && (this.err = s.inflateSetDictionary(this.strm, this.dictionary), this.err === e.Z_OK ? this.err = s.inflate(this.strm, this.flush) : this.err === e.Z_DATA_ERROR && (this.err = e.Z_NEED_DICT)); this.strm.avail_in > 0 && this.mode === e.GUNZIP && this.err === e.Z_STREAM_END && 0 !== this.strm.next_in[0];) this.reset(), this.err = s.inflate(this.strm, this.flush);

              break;

            default:
              throw new Error("Unknown mode " + this.mode);
          }
        }, c.prototype._checkError = function () {
          switch (this.err) {
            case e.Z_OK:
            case e.Z_BUF_ERROR:
              if (0 !== this.strm.avail_out && this.flush === e.Z_FINISH) return this._error("unexpected end of file"), !1;
              break;

            case e.Z_STREAM_END:
              break;

            case e.Z_NEED_DICT:
              return null == this.dictionary ? this._error("Missing dictionary") : this._error("Bad dictionary"), !1;

            default:
              return this._error("Zlib error"), !1;
          }

          return !0;
        }, c.prototype._after = function () {
          if (this._checkError()) {
            var t = this.strm.avail_out,
                e = this.strm.avail_in;
            this.write_in_progress = !1, this.callback(e, t), this.pending_close && this.close();
          }
        }, c.prototype._error = function (t) {
          this.strm.msg && (t = this.strm.msg), this.onerror(t, this.err), this.write_in_progress = !1, this.pending_close && this.close();
        }, c.prototype.init = function (t, r, n, o, a) {
          i(4 === arguments.length || 5 === arguments.length, "init(windowBits, level, memLevel, strategy, [dictionary])"), i(t >= 8 && t <= 15, "invalid windowBits"), i(r >= -1 && r <= 9, "invalid compression level"), i(n >= 1 && n <= 9, "invalid memlevel"), i(o === e.Z_FILTERED || o === e.Z_HUFFMAN_ONLY || o === e.Z_RLE || o === e.Z_FIXED || o === e.Z_DEFAULT_STRATEGY, "invalid strategy"), this._init(r, t, n, o, a), this._setDictionary();
        }, c.prototype.params = function () {
          throw new Error("deflateParams Not supported");
        }, c.prototype.reset = function () {
          this._reset(), this._setDictionary();
        }, c.prototype._init = function (t, r, n, i, u) {
          switch (this.level = t, this.windowBits = r, this.memLevel = n, this.strategy = i, this.flush = e.Z_NO_FLUSH, this.err = e.Z_OK, this.mode !== e.GZIP && this.mode !== e.GUNZIP || (this.windowBits += 16), this.mode === e.UNZIP && (this.windowBits += 32), this.mode !== e.DEFLATERAW && this.mode !== e.INFLATERAW || (this.windowBits = -1 * this.windowBits), this.strm = new o(), this.mode) {
            case e.DEFLATE:
            case e.GZIP:
            case e.DEFLATERAW:
              this.err = a.deflateInit2(this.strm, this.level, e.Z_DEFLATED, this.windowBits, this.memLevel, this.strategy);
              break;

            case e.INFLATE:
            case e.GUNZIP:
            case e.INFLATERAW:
            case e.UNZIP:
              this.err = s.inflateInit2(this.strm, this.windowBits);
              break;

            default:
              throw new Error("Unknown mode " + this.mode);
          }

          this.err !== e.Z_OK && this._error("Init error"), this.dictionary = u, this.write_in_progress = !1, this.init_done = !0;
        }, c.prototype._setDictionary = function () {
          if (null != this.dictionary) {
            switch (this.err = e.Z_OK, this.mode) {
              case e.DEFLATE:
              case e.DEFLATERAW:
                this.err = a.deflateSetDictionary(this.strm, this.dictionary);
            }

            this.err !== e.Z_OK && this._error("Failed to set dictionary");
          }
        }, c.prototype._reset = function () {
          switch (this.err = e.Z_OK, this.mode) {
            case e.DEFLATE:
            case e.DEFLATERAW:
            case e.GZIP:
              this.err = a.deflateReset(this.strm);
              break;

            case e.INFLATE:
            case e.INFLATERAW:
            case e.GUNZIP:
              this.err = s.inflateReset(this.strm);
          }

          this.err !== e.Z_OK && this._error("Failed to reset stream");
        }, e.Zlib = c;
      }).call(this, r(19).Buffer, r(38));
    }, function (t, e, r) {
      /*
      object-assign
      (c) Sindre Sorhus
      @license MIT
      */

      var n = Object.getOwnPropertySymbols,
          i = Object.prototype.hasOwnProperty,
          o = Object.prototype.propertyIsEnumerable;

      function a(t) {
        if (null == t) throw new TypeError("Object.assign cannot be called with null or undefined");
        return Object(t);
      }

      t.exports = function () {
        try {
          if (!Object.assign) return !1;
          var t = new String("abc");
          if (t[5] = "de", "5" === Object.getOwnPropertyNames(t)[0]) return !1;

          for (var e = {}, r = 0; r < 10; r++) e["_" + String.fromCharCode(r)] = r;

          if ("0123456789" !== Object.getOwnPropertyNames(e).map(function (t) {
            return e[t];
          }).join("")) return !1;
          var n = {};
          return "abcdefghijklmnopqrst".split("").forEach(function (t) {
            n[t] = t;
          }), "abcdefghijklmnopqrst" === Object.keys(Object.assign({}, n)).join("");
        } catch (t) {
          return !1;
        }
      }() ? Object.assign : function (t, e) {
        for (var r, s, u = a(t), f = 1; f < arguments.length; f++) {
          for (var c in r = Object(arguments[f])) i.call(r, c) && (u[c] = r[c]);

          if (n) {
            s = n(r);

            for (var l = 0; l < s.length; l++) o.call(r, s[l]) && (u[s[l]] = r[s[l]]);
          }
        }

        return u;
      };
    }, function (t, e) {
      t.exports = function (t) {
        return t && "object" == typeof t && "function" == typeof t.copy && "function" == typeof t.fill && "function" == typeof t.readUInt8;
      };
    }, function (t, e) {
      "function" == typeof Object.create ? t.exports = function (t, e) {
        t.super_ = e, t.prototype = Object.create(e.prototype, {
          constructor: {
            value: t,
            enumerable: !1,
            writable: !0,
            configurable: !0
          }
        });
      } : t.exports = function (t, e) {
        t.super_ = e;

        var r = function () {};

        r.prototype = e.prototype, t.prototype = new r(), t.prototype.constructor = t;
      };
    }, function (t, e, r) {

      t.exports = function () {
        this.input = null, this.next_in = 0, this.avail_in = 0, this.total_in = 0, this.output = null, this.next_out = 0, this.avail_out = 0, this.total_out = 0, this.msg = "", this.state = null, this.data_type = 2, this.adler = 0;
      };
    }, function (t, e, r) {

      var n,
          i = r(72),
          o = r(218),
          a = r(134),
          s = r(135),
          u = r(219),
          f = 0,
          c = 1,
          l = 3,
          h = 4,
          d = 5,
          p = 0,
          g = 1,
          v = -2,
          y = -3,
          m = -5,
          b = -1,
          _ = 1,
          w = 2,
          x = 3,
          E = 4,
          S = 0,
          k = 2,
          A = 8,
          C = 9,
          T = 15,
          R = 8,
          O = 286,
          I = 30,
          L = 19,
          B = 2 * O + 1,
          P = 15,
          F = 3,
          N = 258,
          M = N + F + 1,
          j = 32,
          z = 42,
          U = 69,
          D = 73,
          q = 91,
          Z = 103,
          H = 113,
          V = 666,
          G = 1,
          Y = 2,
          W = 3,
          K = 4,
          Q = 3;

      function X(t, e) {
        return t.msg = u[e], e;
      }

      function $(t) {
        return (t << 1) - (t > 4 ? 9 : 0);
      }

      function J(t) {
        for (var e = t.length; --e >= 0;) t[e] = 0;
      }

      function tt(t) {
        var e = t.state,
            r = e.pending;
        r > t.avail_out && (r = t.avail_out), 0 !== r && (i.arraySet(t.output, e.pending_buf, e.pending_out, r, t.next_out), t.next_out += r, e.pending_out += r, t.total_out += r, t.avail_out -= r, e.pending -= r, 0 === e.pending && (e.pending_out = 0));
      }

      function et(t, e) {
        o._tr_flush_block(t, t.block_start >= 0 ? t.block_start : -1, t.strstart - t.block_start, e), t.block_start = t.strstart, tt(t.strm);
      }

      function rt(t, e) {
        t.pending_buf[t.pending++] = e;
      }

      function nt(t, e) {
        t.pending_buf[t.pending++] = e >>> 8 & 255, t.pending_buf[t.pending++] = 255 & e;
      }

      function it(t, e) {
        var r,
            n,
            i = t.max_chain_length,
            o = t.strstart,
            a = t.prev_length,
            s = t.nice_match,
            u = t.strstart > t.w_size - M ? t.strstart - (t.w_size - M) : 0,
            f = t.window,
            c = t.w_mask,
            l = t.prev,
            h = t.strstart + N,
            d = f[o + a - 1],
            p = f[o + a];
        t.prev_length >= t.good_match && (i >>= 2), s > t.lookahead && (s = t.lookahead);

        do {
          if (f[(r = e) + a] === p && f[r + a - 1] === d && f[r] === f[o] && f[++r] === f[o + 1]) {
            o += 2, r++;

            do {} while (f[++o] === f[++r] && f[++o] === f[++r] && f[++o] === f[++r] && f[++o] === f[++r] && f[++o] === f[++r] && f[++o] === f[++r] && f[++o] === f[++r] && f[++o] === f[++r] && o < h);

            if (n = N - (h - o), o = h - N, n > a) {
              if (t.match_start = e, a = n, n >= s) break;
              d = f[o + a - 1], p = f[o + a];
            }
          }
        } while ((e = l[e & c]) > u && 0 != --i);

        return a <= t.lookahead ? a : t.lookahead;
      }

      function ot(t) {
        var e,
            r,
            n,
            o,
            u,
            f,
            c,
            l,
            h,
            d,
            p = t.w_size;

        do {
          if (o = t.window_size - t.lookahead - t.strstart, t.strstart >= p + (p - M)) {
            i.arraySet(t.window, t.window, p, p, 0), t.match_start -= p, t.strstart -= p, t.block_start -= p, e = r = t.hash_size;

            do {
              n = t.head[--e], t.head[e] = n >= p ? n - p : 0;
            } while (--r);

            e = r = p;

            do {
              n = t.prev[--e], t.prev[e] = n >= p ? n - p : 0;
            } while (--r);

            o += p;
          }

          if (0 === t.strm.avail_in) break;
          if (f = t.strm, c = t.window, l = t.strstart + t.lookahead, h = o, d = void 0, (d = f.avail_in) > h && (d = h), r = 0 === d ? 0 : (f.avail_in -= d, i.arraySet(c, f.input, f.next_in, d, l), 1 === f.state.wrap ? f.adler = a(f.adler, c, d, l) : 2 === f.state.wrap && (f.adler = s(f.adler, c, d, l)), f.next_in += d, f.total_in += d, d), t.lookahead += r, t.lookahead + t.insert >= F) for (u = t.strstart - t.insert, t.ins_h = t.window[u], t.ins_h = (t.ins_h << t.hash_shift ^ t.window[u + 1]) & t.hash_mask; t.insert && (t.ins_h = (t.ins_h << t.hash_shift ^ t.window[u + F - 1]) & t.hash_mask, t.prev[u & t.w_mask] = t.head[t.ins_h], t.head[t.ins_h] = u, u++, t.insert--, !(t.lookahead + t.insert < F)););
        } while (t.lookahead < M && 0 !== t.strm.avail_in);
      }

      function at(t, e) {
        for (var r, n;;) {
          if (t.lookahead < M) {
            if (ot(t), t.lookahead < M && e === f) return G;
            if (0 === t.lookahead) break;
          }

          if (r = 0, t.lookahead >= F && (t.ins_h = (t.ins_h << t.hash_shift ^ t.window[t.strstart + F - 1]) & t.hash_mask, r = t.prev[t.strstart & t.w_mask] = t.head[t.ins_h], t.head[t.ins_h] = t.strstart), 0 !== r && t.strstart - r <= t.w_size - M && (t.match_length = it(t, r)), t.match_length >= F) {
            if (n = o._tr_tally(t, t.strstart - t.match_start, t.match_length - F), t.lookahead -= t.match_length, t.match_length <= t.max_lazy_match && t.lookahead >= F) {
              t.match_length--;

              do {
                t.strstart++, t.ins_h = (t.ins_h << t.hash_shift ^ t.window[t.strstart + F - 1]) & t.hash_mask, r = t.prev[t.strstart & t.w_mask] = t.head[t.ins_h], t.head[t.ins_h] = t.strstart;
              } while (0 != --t.match_length);

              t.strstart++;
            } else t.strstart += t.match_length, t.match_length = 0, t.ins_h = t.window[t.strstart], t.ins_h = (t.ins_h << t.hash_shift ^ t.window[t.strstart + 1]) & t.hash_mask;
          } else n = o._tr_tally(t, 0, t.window[t.strstart]), t.lookahead--, t.strstart++;
          if (n && (et(t, !1), 0 === t.strm.avail_out)) return G;
        }

        return t.insert = t.strstart < F - 1 ? t.strstart : F - 1, e === h ? (et(t, !0), 0 === t.strm.avail_out ? W : K) : t.last_lit && (et(t, !1), 0 === t.strm.avail_out) ? G : Y;
      }

      function st(t, e) {
        for (var r, n, i;;) {
          if (t.lookahead < M) {
            if (ot(t), t.lookahead < M && e === f) return G;
            if (0 === t.lookahead) break;
          }

          if (r = 0, t.lookahead >= F && (t.ins_h = (t.ins_h << t.hash_shift ^ t.window[t.strstart + F - 1]) & t.hash_mask, r = t.prev[t.strstart & t.w_mask] = t.head[t.ins_h], t.head[t.ins_h] = t.strstart), t.prev_length = t.match_length, t.prev_match = t.match_start, t.match_length = F - 1, 0 !== r && t.prev_length < t.max_lazy_match && t.strstart - r <= t.w_size - M && (t.match_length = it(t, r), t.match_length <= 5 && (t.strategy === _ || t.match_length === F && t.strstart - t.match_start > 4096) && (t.match_length = F - 1)), t.prev_length >= F && t.match_length <= t.prev_length) {
            i = t.strstart + t.lookahead - F, n = o._tr_tally(t, t.strstart - 1 - t.prev_match, t.prev_length - F), t.lookahead -= t.prev_length - 1, t.prev_length -= 2;

            do {
              ++t.strstart <= i && (t.ins_h = (t.ins_h << t.hash_shift ^ t.window[t.strstart + F - 1]) & t.hash_mask, r = t.prev[t.strstart & t.w_mask] = t.head[t.ins_h], t.head[t.ins_h] = t.strstart);
            } while (0 != --t.prev_length);

            if (t.match_available = 0, t.match_length = F - 1, t.strstart++, n && (et(t, !1), 0 === t.strm.avail_out)) return G;
          } else if (t.match_available) {
            if ((n = o._tr_tally(t, 0, t.window[t.strstart - 1])) && et(t, !1), t.strstart++, t.lookahead--, 0 === t.strm.avail_out) return G;
          } else t.match_available = 1, t.strstart++, t.lookahead--;
        }

        return t.match_available && (n = o._tr_tally(t, 0, t.window[t.strstart - 1]), t.match_available = 0), t.insert = t.strstart < F - 1 ? t.strstart : F - 1, e === h ? (et(t, !0), 0 === t.strm.avail_out ? W : K) : t.last_lit && (et(t, !1), 0 === t.strm.avail_out) ? G : Y;
      }

      function ut(t, e, r, n, i) {
        this.good_length = t, this.max_lazy = e, this.nice_length = r, this.max_chain = n, this.func = i;
      }

      function ft() {
        this.strm = null, this.status = 0, this.pending_buf = null, this.pending_buf_size = 0, this.pending_out = 0, this.pending = 0, this.wrap = 0, this.gzhead = null, this.gzindex = 0, this.method = A, this.last_flush = -1, this.w_size = 0, this.w_bits = 0, this.w_mask = 0, this.window = null, this.window_size = 0, this.prev = null, this.head = null, this.ins_h = 0, this.hash_size = 0, this.hash_bits = 0, this.hash_mask = 0, this.hash_shift = 0, this.block_start = 0, this.match_length = 0, this.prev_match = 0, this.match_available = 0, this.strstart = 0, this.match_start = 0, this.lookahead = 0, this.prev_length = 0, this.max_chain_length = 0, this.max_lazy_match = 0, this.level = 0, this.strategy = 0, this.good_match = 0, this.nice_match = 0, this.dyn_ltree = new i.Buf16(2 * B), this.dyn_dtree = new i.Buf16(2 * (2 * I + 1)), this.bl_tree = new i.Buf16(2 * (2 * L + 1)), J(this.dyn_ltree), J(this.dyn_dtree), J(this.bl_tree), this.l_desc = null, this.d_desc = null, this.bl_desc = null, this.bl_count = new i.Buf16(P + 1), this.heap = new i.Buf16(2 * O + 1), J(this.heap), this.heap_len = 0, this.heap_max = 0, this.depth = new i.Buf16(2 * O + 1), J(this.depth), this.l_buf = 0, this.lit_bufsize = 0, this.last_lit = 0, this.d_buf = 0, this.opt_len = 0, this.static_len = 0, this.matches = 0, this.insert = 0, this.bi_buf = 0, this.bi_valid = 0;
      }

      function ct(t) {
        var e;
        return t && t.state ? (t.total_in = t.total_out = 0, t.data_type = k, (e = t.state).pending = 0, e.pending_out = 0, e.wrap < 0 && (e.wrap = -e.wrap), e.status = e.wrap ? z : H, t.adler = 2 === e.wrap ? 0 : 1, e.last_flush = f, o._tr_init(e), p) : X(t, v);
      }

      function lt(t) {
        var e,
            r = ct(t);
        return r === p && ((e = t.state).window_size = 2 * e.w_size, J(e.head), e.max_lazy_match = n[e.level].max_lazy, e.good_match = n[e.level].good_length, e.nice_match = n[e.level].nice_length, e.max_chain_length = n[e.level].max_chain, e.strstart = 0, e.block_start = 0, e.lookahead = 0, e.insert = 0, e.match_length = e.prev_length = F - 1, e.match_available = 0, e.ins_h = 0), r;
      }

      function ht(t, e, r, n, o, a) {
        if (!t) return v;
        var s = 1;
        if (e === b && (e = 6), n < 0 ? (s = 0, n = -n) : n > 15 && (s = 2, n -= 16), o < 1 || o > C || r !== A || n < 8 || n > 15 || e < 0 || e > 9 || a < 0 || a > E) return X(t, v);
        8 === n && (n = 9);
        var u = new ft();
        return t.state = u, u.strm = t, u.wrap = s, u.gzhead = null, u.w_bits = n, u.w_size = 1 << u.w_bits, u.w_mask = u.w_size - 1, u.hash_bits = o + 7, u.hash_size = 1 << u.hash_bits, u.hash_mask = u.hash_size - 1, u.hash_shift = ~~((u.hash_bits + F - 1) / F), u.window = new i.Buf8(2 * u.w_size), u.head = new i.Buf16(u.hash_size), u.prev = new i.Buf16(u.w_size), u.lit_bufsize = 1 << o + 6, u.pending_buf_size = 4 * u.lit_bufsize, u.pending_buf = new i.Buf8(u.pending_buf_size), u.d_buf = 1 * u.lit_bufsize, u.l_buf = 3 * u.lit_bufsize, u.level = e, u.strategy = a, u.method = r, lt(t);
      }

      n = [new ut(0, 0, 0, 0, function (t, e) {
        var r = 65535;

        for (r > t.pending_buf_size - 5 && (r = t.pending_buf_size - 5);;) {
          if (t.lookahead <= 1) {
            if (ot(t), 0 === t.lookahead && e === f) return G;
            if (0 === t.lookahead) break;
          }

          t.strstart += t.lookahead, t.lookahead = 0;
          var n = t.block_start + r;
          if ((0 === t.strstart || t.strstart >= n) && (t.lookahead = t.strstart - n, t.strstart = n, et(t, !1), 0 === t.strm.avail_out)) return G;
          if (t.strstart - t.block_start >= t.w_size - M && (et(t, !1), 0 === t.strm.avail_out)) return G;
        }

        return t.insert = 0, e === h ? (et(t, !0), 0 === t.strm.avail_out ? W : K) : (t.strstart > t.block_start && (et(t, !1), t.strm.avail_out), G);
      }), new ut(4, 4, 8, 4, at), new ut(4, 5, 16, 8, at), new ut(4, 6, 32, 32, at), new ut(4, 4, 16, 16, st), new ut(8, 16, 32, 32, st), new ut(8, 16, 128, 128, st), new ut(8, 32, 128, 256, st), new ut(32, 128, 258, 1024, st), new ut(32, 258, 258, 4096, st)], e.deflateInit = function (t, e) {
        return ht(t, e, A, T, R, S);
      }, e.deflateInit2 = ht, e.deflateReset = lt, e.deflateResetKeep = ct, e.deflateSetHeader = function (t, e) {
        return t && t.state ? 2 !== t.state.wrap ? v : (t.state.gzhead = e, p) : v;
      }, e.deflate = function (t, e) {
        var r, i, a, u;
        if (!t || !t.state || e > d || e < 0) return t ? X(t, v) : v;
        if (i = t.state, !t.output || !t.input && 0 !== t.avail_in || i.status === V && e !== h) return X(t, 0 === t.avail_out ? m : v);
        if (i.strm = t, r = i.last_flush, i.last_flush = e, i.status === z) if (2 === i.wrap) t.adler = 0, rt(i, 31), rt(i, 139), rt(i, 8), i.gzhead ? (rt(i, (i.gzhead.text ? 1 : 0) + (i.gzhead.hcrc ? 2 : 0) + (i.gzhead.extra ? 4 : 0) + (i.gzhead.name ? 8 : 0) + (i.gzhead.comment ? 16 : 0)), rt(i, 255 & i.gzhead.time), rt(i, i.gzhead.time >> 8 & 255), rt(i, i.gzhead.time >> 16 & 255), rt(i, i.gzhead.time >> 24 & 255), rt(i, 9 === i.level ? 2 : i.strategy >= w || i.level < 2 ? 4 : 0), rt(i, 255 & i.gzhead.os), i.gzhead.extra && i.gzhead.extra.length && (rt(i, 255 & i.gzhead.extra.length), rt(i, i.gzhead.extra.length >> 8 & 255)), i.gzhead.hcrc && (t.adler = s(t.adler, i.pending_buf, i.pending, 0)), i.gzindex = 0, i.status = U) : (rt(i, 0), rt(i, 0), rt(i, 0), rt(i, 0), rt(i, 0), rt(i, 9 === i.level ? 2 : i.strategy >= w || i.level < 2 ? 4 : 0), rt(i, Q), i.status = H);else {
          var y = A + (i.w_bits - 8 << 4) << 8;
          y |= (i.strategy >= w || i.level < 2 ? 0 : i.level < 6 ? 1 : 6 === i.level ? 2 : 3) << 6, 0 !== i.strstart && (y |= j), y += 31 - y % 31, i.status = H, nt(i, y), 0 !== i.strstart && (nt(i, t.adler >>> 16), nt(i, 65535 & t.adler)), t.adler = 1;
        }
        if (i.status === U) if (i.gzhead.extra) {
          for (a = i.pending; i.gzindex < (65535 & i.gzhead.extra.length) && (i.pending !== i.pending_buf_size || (i.gzhead.hcrc && i.pending > a && (t.adler = s(t.adler, i.pending_buf, i.pending - a, a)), tt(t), a = i.pending, i.pending !== i.pending_buf_size));) rt(i, 255 & i.gzhead.extra[i.gzindex]), i.gzindex++;

          i.gzhead.hcrc && i.pending > a && (t.adler = s(t.adler, i.pending_buf, i.pending - a, a)), i.gzindex === i.gzhead.extra.length && (i.gzindex = 0, i.status = D);
        } else i.status = D;
        if (i.status === D) if (i.gzhead.name) {
          a = i.pending;

          do {
            if (i.pending === i.pending_buf_size && (i.gzhead.hcrc && i.pending > a && (t.adler = s(t.adler, i.pending_buf, i.pending - a, a)), tt(t), a = i.pending, i.pending === i.pending_buf_size)) {
              u = 1;
              break;
            }

            u = i.gzindex < i.gzhead.name.length ? 255 & i.gzhead.name.charCodeAt(i.gzindex++) : 0, rt(i, u);
          } while (0 !== u);

          i.gzhead.hcrc && i.pending > a && (t.adler = s(t.adler, i.pending_buf, i.pending - a, a)), 0 === u && (i.gzindex = 0, i.status = q);
        } else i.status = q;
        if (i.status === q) if (i.gzhead.comment) {
          a = i.pending;

          do {
            if (i.pending === i.pending_buf_size && (i.gzhead.hcrc && i.pending > a && (t.adler = s(t.adler, i.pending_buf, i.pending - a, a)), tt(t), a = i.pending, i.pending === i.pending_buf_size)) {
              u = 1;
              break;
            }

            u = i.gzindex < i.gzhead.comment.length ? 255 & i.gzhead.comment.charCodeAt(i.gzindex++) : 0, rt(i, u);
          } while (0 !== u);

          i.gzhead.hcrc && i.pending > a && (t.adler = s(t.adler, i.pending_buf, i.pending - a, a)), 0 === u && (i.status = Z);
        } else i.status = Z;

        if (i.status === Z && (i.gzhead.hcrc ? (i.pending + 2 > i.pending_buf_size && tt(t), i.pending + 2 <= i.pending_buf_size && (rt(i, 255 & t.adler), rt(i, t.adler >> 8 & 255), t.adler = 0, i.status = H)) : i.status = H), 0 !== i.pending) {
          if (tt(t), 0 === t.avail_out) return i.last_flush = -1, p;
        } else if (0 === t.avail_in && $(e) <= $(r) && e !== h) return X(t, m);

        if (i.status === V && 0 !== t.avail_in) return X(t, m);

        if (0 !== t.avail_in || 0 !== i.lookahead || e !== f && i.status !== V) {
          var b = i.strategy === w ? function (t, e) {
            for (var r;;) {
              if (0 === t.lookahead && (ot(t), 0 === t.lookahead)) {
                if (e === f) return G;
                break;
              }

              if (t.match_length = 0, r = o._tr_tally(t, 0, t.window[t.strstart]), t.lookahead--, t.strstart++, r && (et(t, !1), 0 === t.strm.avail_out)) return G;
            }

            return t.insert = 0, e === h ? (et(t, !0), 0 === t.strm.avail_out ? W : K) : t.last_lit && (et(t, !1), 0 === t.strm.avail_out) ? G : Y;
          }(i, e) : i.strategy === x ? function (t, e) {
            for (var r, n, i, a, s = t.window;;) {
              if (t.lookahead <= N) {
                if (ot(t), t.lookahead <= N && e === f) return G;
                if (0 === t.lookahead) break;
              }

              if (t.match_length = 0, t.lookahead >= F && t.strstart > 0 && (n = s[i = t.strstart - 1]) === s[++i] && n === s[++i] && n === s[++i]) {
                a = t.strstart + N;

                do {} while (n === s[++i] && n === s[++i] && n === s[++i] && n === s[++i] && n === s[++i] && n === s[++i] && n === s[++i] && n === s[++i] && i < a);

                t.match_length = N - (a - i), t.match_length > t.lookahead && (t.match_length = t.lookahead);
              }

              if (t.match_length >= F ? (r = o._tr_tally(t, 1, t.match_length - F), t.lookahead -= t.match_length, t.strstart += t.match_length, t.match_length = 0) : (r = o._tr_tally(t, 0, t.window[t.strstart]), t.lookahead--, t.strstart++), r && (et(t, !1), 0 === t.strm.avail_out)) return G;
            }

            return t.insert = 0, e === h ? (et(t, !0), 0 === t.strm.avail_out ? W : K) : t.last_lit && (et(t, !1), 0 === t.strm.avail_out) ? G : Y;
          }(i, e) : n[i.level].func(i, e);
          if (b !== W && b !== K || (i.status = V), b === G || b === W) return 0 === t.avail_out && (i.last_flush = -1), p;
          if (b === Y && (e === c ? o._tr_align(i) : e !== d && (o._tr_stored_block(i, 0, 0, !1), e === l && (J(i.head), 0 === i.lookahead && (i.strstart = 0, i.block_start = 0, i.insert = 0))), tt(t), 0 === t.avail_out)) return i.last_flush = -1, p;
        }

        return e !== h ? p : i.wrap <= 0 ? g : (2 === i.wrap ? (rt(i, 255 & t.adler), rt(i, t.adler >> 8 & 255), rt(i, t.adler >> 16 & 255), rt(i, t.adler >> 24 & 255), rt(i, 255 & t.total_in), rt(i, t.total_in >> 8 & 255), rt(i, t.total_in >> 16 & 255), rt(i, t.total_in >> 24 & 255)) : (nt(i, t.adler >>> 16), nt(i, 65535 & t.adler)), tt(t), i.wrap > 0 && (i.wrap = -i.wrap), 0 !== i.pending ? p : g);
      }, e.deflateEnd = function (t) {
        var e;
        return t && t.state ? (e = t.state.status) !== z && e !== U && e !== D && e !== q && e !== Z && e !== H && e !== V ? X(t, v) : (t.state = null, e === H ? X(t, y) : p) : v;
      }, e.deflateSetDictionary = function (t, e) {
        var r,
            n,
            o,
            s,
            u,
            f,
            c,
            l,
            h = e.length;
        if (!t || !t.state) return v;
        if (2 === (s = (r = t.state).wrap) || 1 === s && r.status !== z || r.lookahead) return v;

        for (1 === s && (t.adler = a(t.adler, e, h, 0)), r.wrap = 0, h >= r.w_size && (0 === s && (J(r.head), r.strstart = 0, r.block_start = 0, r.insert = 0), l = new i.Buf8(r.w_size), i.arraySet(l, e, h - r.w_size, r.w_size, 0), e = l, h = r.w_size), u = t.avail_in, f = t.next_in, c = t.input, t.avail_in = h, t.next_in = 0, t.input = e, ot(r); r.lookahead >= F;) {
          n = r.strstart, o = r.lookahead - (F - 1);

          do {
            r.ins_h = (r.ins_h << r.hash_shift ^ r.window[n + F - 1]) & r.hash_mask, r.prev[n & r.w_mask] = r.head[r.ins_h], r.head[r.ins_h] = n, n++;
          } while (--o);

          r.strstart = n, r.lookahead = F - 1, ot(r);
        }

        return r.strstart += r.lookahead, r.block_start = r.strstart, r.insert = r.lookahead, r.lookahead = 0, r.match_length = r.prev_length = F - 1, r.match_available = 0, t.next_in = f, t.input = c, t.avail_in = u, r.wrap = s, p;
      }, e.deflateInfo = "pako deflate (from Nodeca project)";
    }, function (t, e, r) {

      var n = r(72),
          i = 4,
          o = 0,
          a = 1,
          s = 2;

      function u(t) {
        for (var e = t.length; --e >= 0;) t[e] = 0;
      }

      var f = 0,
          c = 1,
          l = 2,
          h = 29,
          d = 256,
          p = d + 1 + h,
          g = 30,
          v = 19,
          y = 2 * p + 1,
          m = 15,
          b = 16,
          _ = 7,
          w = 256,
          x = 16,
          E = 17,
          S = 18,
          k = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0],
          A = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13],
          C = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7],
          T = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15],
          R = new Array(2 * (p + 2));
      u(R);
      var O = new Array(2 * g);
      u(O);
      var I = new Array(512);
      u(I);
      var L = new Array(256);
      u(L);
      var B = new Array(h);
      u(B);
      var P,
          F,
          N,
          M = new Array(g);

      function j(t, e, r, n, i) {
        this.static_tree = t, this.extra_bits = e, this.extra_base = r, this.elems = n, this.max_length = i, this.has_stree = t && t.length;
      }

      function z(t, e) {
        this.dyn_tree = t, this.max_code = 0, this.stat_desc = e;
      }

      function U(t) {
        return t < 256 ? I[t] : I[256 + (t >>> 7)];
      }

      function D(t, e) {
        t.pending_buf[t.pending++] = 255 & e, t.pending_buf[t.pending++] = e >>> 8 & 255;
      }

      function q(t, e, r) {
        t.bi_valid > b - r ? (t.bi_buf |= e << t.bi_valid & 65535, D(t, t.bi_buf), t.bi_buf = e >> b - t.bi_valid, t.bi_valid += r - b) : (t.bi_buf |= e << t.bi_valid & 65535, t.bi_valid += r);
      }

      function Z(t, e, r) {
        q(t, r[2 * e], r[2 * e + 1]);
      }

      function H(t, e) {
        var r = 0;

        do {
          r |= 1 & t, t >>>= 1, r <<= 1;
        } while (--e > 0);

        return r >>> 1;
      }

      function V(t, e, r) {
        var n,
            i,
            o = new Array(m + 1),
            a = 0;

        for (n = 1; n <= m; n++) o[n] = a = a + r[n - 1] << 1;

        for (i = 0; i <= e; i++) {
          var s = t[2 * i + 1];
          0 !== s && (t[2 * i] = H(o[s]++, s));
        }
      }

      function G(t) {
        var e;

        for (e = 0; e < p; e++) t.dyn_ltree[2 * e] = 0;

        for (e = 0; e < g; e++) t.dyn_dtree[2 * e] = 0;

        for (e = 0; e < v; e++) t.bl_tree[2 * e] = 0;

        t.dyn_ltree[2 * w] = 1, t.opt_len = t.static_len = 0, t.last_lit = t.matches = 0;
      }

      function Y(t) {
        t.bi_valid > 8 ? D(t, t.bi_buf) : t.bi_valid > 0 && (t.pending_buf[t.pending++] = t.bi_buf), t.bi_buf = 0, t.bi_valid = 0;
      }

      function W(t, e, r, n) {
        var i = 2 * e,
            o = 2 * r;
        return t[i] < t[o] || t[i] === t[o] && n[e] <= n[r];
      }

      function K(t, e, r) {
        for (var n = t.heap[r], i = r << 1; i <= t.heap_len && (i < t.heap_len && W(e, t.heap[i + 1], t.heap[i], t.depth) && i++, !W(e, n, t.heap[i], t.depth));) t.heap[r] = t.heap[i], r = i, i <<= 1;

        t.heap[r] = n;
      }

      function Q(t, e, r) {
        var n,
            i,
            o,
            a,
            s = 0;
        if (0 !== t.last_lit) do {
          n = t.pending_buf[t.d_buf + 2 * s] << 8 | t.pending_buf[t.d_buf + 2 * s + 1], i = t.pending_buf[t.l_buf + s], s++, 0 === n ? Z(t, i, e) : (Z(t, (o = L[i]) + d + 1, e), 0 !== (a = k[o]) && q(t, i -= B[o], a), Z(t, o = U(--n), r), 0 !== (a = A[o]) && q(t, n -= M[o], a));
        } while (s < t.last_lit);
        Z(t, w, e);
      }

      function X(t, e) {
        var r,
            n,
            i,
            o = e.dyn_tree,
            a = e.stat_desc.static_tree,
            s = e.stat_desc.has_stree,
            u = e.stat_desc.elems,
            f = -1;

        for (t.heap_len = 0, t.heap_max = y, r = 0; r < u; r++) 0 !== o[2 * r] ? (t.heap[++t.heap_len] = f = r, t.depth[r] = 0) : o[2 * r + 1] = 0;

        for (; t.heap_len < 2;) o[2 * (i = t.heap[++t.heap_len] = f < 2 ? ++f : 0)] = 1, t.depth[i] = 0, t.opt_len--, s && (t.static_len -= a[2 * i + 1]);

        for (e.max_code = f, r = t.heap_len >> 1; r >= 1; r--) K(t, o, r);

        i = u;

        do {
          r = t.heap[1], t.heap[1] = t.heap[t.heap_len--], K(t, o, 1), n = t.heap[1], t.heap[--t.heap_max] = r, t.heap[--t.heap_max] = n, o[2 * i] = o[2 * r] + o[2 * n], t.depth[i] = (t.depth[r] >= t.depth[n] ? t.depth[r] : t.depth[n]) + 1, o[2 * r + 1] = o[2 * n + 1] = i, t.heap[1] = i++, K(t, o, 1);
        } while (t.heap_len >= 2);

        t.heap[--t.heap_max] = t.heap[1], function (t, e) {
          var r,
              n,
              i,
              o,
              a,
              s,
              u = e.dyn_tree,
              f = e.max_code,
              c = e.stat_desc.static_tree,
              l = e.stat_desc.has_stree,
              h = e.stat_desc.extra_bits,
              d = e.stat_desc.extra_base,
              p = e.stat_desc.max_length,
              g = 0;

          for (o = 0; o <= m; o++) t.bl_count[o] = 0;

          for (u[2 * t.heap[t.heap_max] + 1] = 0, r = t.heap_max + 1; r < y; r++) (o = u[2 * u[2 * (n = t.heap[r]) + 1] + 1] + 1) > p && (o = p, g++), u[2 * n + 1] = o, n > f || (t.bl_count[o]++, a = 0, n >= d && (a = h[n - d]), s = u[2 * n], t.opt_len += s * (o + a), l && (t.static_len += s * (c[2 * n + 1] + a)));

          if (0 !== g) {
            do {
              for (o = p - 1; 0 === t.bl_count[o];) o--;

              t.bl_count[o]--, t.bl_count[o + 1] += 2, t.bl_count[p]--, g -= 2;
            } while (g > 0);

            for (o = p; 0 !== o; o--) for (n = t.bl_count[o]; 0 !== n;) (i = t.heap[--r]) > f || (u[2 * i + 1] !== o && (t.opt_len += (o - u[2 * i + 1]) * u[2 * i], u[2 * i + 1] = o), n--);
          }
        }(t, e), V(o, f, t.bl_count);
      }

      function $(t, e, r) {
        var n,
            i,
            o = -1,
            a = e[1],
            s = 0,
            u = 7,
            f = 4;

        for (0 === a && (u = 138, f = 3), e[2 * (r + 1) + 1] = 65535, n = 0; n <= r; n++) i = a, a = e[2 * (n + 1) + 1], ++s < u && i === a || (s < f ? t.bl_tree[2 * i] += s : 0 !== i ? (i !== o && t.bl_tree[2 * i]++, t.bl_tree[2 * x]++) : s <= 10 ? t.bl_tree[2 * E]++ : t.bl_tree[2 * S]++, s = 0, o = i, 0 === a ? (u = 138, f = 3) : i === a ? (u = 6, f = 3) : (u = 7, f = 4));
      }

      function J(t, e, r) {
        var n,
            i,
            o = -1,
            a = e[1],
            s = 0,
            u = 7,
            f = 4;

        for (0 === a && (u = 138, f = 3), n = 0; n <= r; n++) if (i = a, a = e[2 * (n + 1) + 1], !(++s < u && i === a)) {
          if (s < f) do {
            Z(t, i, t.bl_tree);
          } while (0 != --s);else 0 !== i ? (i !== o && (Z(t, i, t.bl_tree), s--), Z(t, x, t.bl_tree), q(t, s - 3, 2)) : s <= 10 ? (Z(t, E, t.bl_tree), q(t, s - 3, 3)) : (Z(t, S, t.bl_tree), q(t, s - 11, 7));
          s = 0, o = i, 0 === a ? (u = 138, f = 3) : i === a ? (u = 6, f = 3) : (u = 7, f = 4);
        }
      }

      u(M);
      var tt = !1;

      function et(t, e, r, i) {
        q(t, (f << 1) + (i ? 1 : 0), 3), function (t, e, r, i) {
          Y(t), i && (D(t, r), D(t, ~r)), n.arraySet(t.pending_buf, t.window, e, r, t.pending), t.pending += r;
        }(t, e, r, !0);
      }

      e._tr_init = function (t) {
        tt || (!function () {
          var t,
              e,
              r,
              n,
              i,
              o = new Array(m + 1);

          for (r = 0, n = 0; n < h - 1; n++) for (B[n] = r, t = 0; t < 1 << k[n]; t++) L[r++] = n;

          for (L[r - 1] = n, i = 0, n = 0; n < 16; n++) for (M[n] = i, t = 0; t < 1 << A[n]; t++) I[i++] = n;

          for (i >>= 7; n < g; n++) for (M[n] = i << 7, t = 0; t < 1 << A[n] - 7; t++) I[256 + i++] = n;

          for (e = 0; e <= m; e++) o[e] = 0;

          for (t = 0; t <= 143;) R[2 * t + 1] = 8, t++, o[8]++;

          for (; t <= 255;) R[2 * t + 1] = 9, t++, o[9]++;

          for (; t <= 279;) R[2 * t + 1] = 7, t++, o[7]++;

          for (; t <= 287;) R[2 * t + 1] = 8, t++, o[8]++;

          for (V(R, p + 1, o), t = 0; t < g; t++) O[2 * t + 1] = 5, O[2 * t] = H(t, 5);

          P = new j(R, k, d + 1, p, m), F = new j(O, A, 0, g, m), N = new j(new Array(0), C, 0, v, _);
        }(), tt = !0), t.l_desc = new z(t.dyn_ltree, P), t.d_desc = new z(t.dyn_dtree, F), t.bl_desc = new z(t.bl_tree, N), t.bi_buf = 0, t.bi_valid = 0, G(t);
      }, e._tr_stored_block = et, e._tr_flush_block = function (t, e, r, n) {
        var u,
            f,
            h = 0;
        t.level > 0 ? (t.strm.data_type === s && (t.strm.data_type = function (t) {
          var e,
              r = 4093624447;

          for (e = 0; e <= 31; e++, r >>>= 1) if (1 & r && 0 !== t.dyn_ltree[2 * e]) return o;

          if (0 !== t.dyn_ltree[18] || 0 !== t.dyn_ltree[20] || 0 !== t.dyn_ltree[26]) return a;

          for (e = 32; e < d; e++) if (0 !== t.dyn_ltree[2 * e]) return a;

          return o;
        }(t)), X(t, t.l_desc), X(t, t.d_desc), h = function (t) {
          var e;

          for ($(t, t.dyn_ltree, t.l_desc.max_code), $(t, t.dyn_dtree, t.d_desc.max_code), X(t, t.bl_desc), e = v - 1; e >= 3 && 0 === t.bl_tree[2 * T[e] + 1]; e--);

          return t.opt_len += 3 * (e + 1) + 5 + 5 + 4, e;
        }(t), u = t.opt_len + 3 + 7 >>> 3, (f = t.static_len + 3 + 7 >>> 3) <= u && (u = f)) : u = f = r + 5, r + 4 <= u && -1 !== e ? et(t, e, r, n) : t.strategy === i || f === u ? (q(t, (c << 1) + (n ? 1 : 0), 3), Q(t, R, O)) : (q(t, (l << 1) + (n ? 1 : 0), 3), function (t, e, r, n) {
          var i;

          for (q(t, e - 257, 5), q(t, r - 1, 5), q(t, n - 4, 4), i = 0; i < n; i++) q(t, t.bl_tree[2 * T[i] + 1], 3);

          J(t, t.dyn_ltree, e - 1), J(t, t.dyn_dtree, r - 1);
        }(t, t.l_desc.max_code + 1, t.d_desc.max_code + 1, h + 1), Q(t, t.dyn_ltree, t.dyn_dtree)), G(t), n && Y(t);
      }, e._tr_tally = function (t, e, r) {
        return t.pending_buf[t.d_buf + 2 * t.last_lit] = e >>> 8 & 255, t.pending_buf[t.d_buf + 2 * t.last_lit + 1] = 255 & e, t.pending_buf[t.l_buf + t.last_lit] = 255 & r, t.last_lit++, 0 === e ? t.dyn_ltree[2 * r]++ : (t.matches++, e--, t.dyn_ltree[2 * (L[r] + d + 1)]++, t.dyn_dtree[2 * U(e)]++), t.last_lit === t.lit_bufsize - 1;
      }, e._tr_align = function (t) {
        q(t, c << 1, 3), Z(t, w, R), function (t) {
          16 === t.bi_valid ? (D(t, t.bi_buf), t.bi_buf = 0, t.bi_valid = 0) : t.bi_valid >= 8 && (t.pending_buf[t.pending++] = 255 & t.bi_buf, t.bi_buf >>= 8, t.bi_valid -= 8);
        }(t);
      };
    }, function (t, e, r) {

      t.exports = {
        2: "need dictionary",
        1: "stream end",
        0: "",
        "-1": "file error",
        "-2": "stream error",
        "-3": "data error",
        "-4": "insufficient memory",
        "-5": "buffer error",
        "-6": "incompatible version"
      };
    }, function (t, e, r) {

      var n = r(72),
          i = r(134),
          o = r(135),
          a = r(221),
          s = r(222),
          u = 0,
          f = 1,
          c = 2,
          l = 4,
          h = 5,
          d = 6,
          p = 0,
          g = 1,
          v = 2,
          y = -2,
          m = -3,
          b = -4,
          _ = -5,
          w = 8,
          x = 1,
          E = 2,
          S = 3,
          k = 4,
          A = 5,
          C = 6,
          T = 7,
          R = 8,
          O = 9,
          I = 10,
          L = 11,
          B = 12,
          P = 13,
          F = 14,
          N = 15,
          M = 16,
          j = 17,
          z = 18,
          U = 19,
          D = 20,
          q = 21,
          Z = 22,
          H = 23,
          V = 24,
          G = 25,
          Y = 26,
          W = 27,
          K = 28,
          Q = 29,
          X = 30,
          $ = 31,
          J = 32,
          tt = 852,
          et = 592,
          rt = 15;

      function nt(t) {
        return (t >>> 24 & 255) + (t >>> 8 & 65280) + ((65280 & t) << 8) + ((255 & t) << 24);
      }

      function it() {
        this.mode = 0, this.last = !1, this.wrap = 0, this.havedict = !1, this.flags = 0, this.dmax = 0, this.check = 0, this.total = 0, this.head = null, this.wbits = 0, this.wsize = 0, this.whave = 0, this.wnext = 0, this.window = null, this.hold = 0, this.bits = 0, this.length = 0, this.offset = 0, this.extra = 0, this.lencode = null, this.distcode = null, this.lenbits = 0, this.distbits = 0, this.ncode = 0, this.nlen = 0, this.ndist = 0, this.have = 0, this.next = null, this.lens = new n.Buf16(320), this.work = new n.Buf16(288), this.lendyn = null, this.distdyn = null, this.sane = 0, this.back = 0, this.was = 0;
      }

      function ot(t) {
        var e;
        return t && t.state ? (e = t.state, t.total_in = t.total_out = e.total = 0, t.msg = "", e.wrap && (t.adler = 1 & e.wrap), e.mode = x, e.last = 0, e.havedict = 0, e.dmax = 32768, e.head = null, e.hold = 0, e.bits = 0, e.lencode = e.lendyn = new n.Buf32(tt), e.distcode = e.distdyn = new n.Buf32(et), e.sane = 1, e.back = -1, p) : y;
      }

      function at(t) {
        var e;
        return t && t.state ? ((e = t.state).wsize = 0, e.whave = 0, e.wnext = 0, ot(t)) : y;
      }

      function st(t, e) {
        var r, n;
        return t && t.state ? (n = t.state, e < 0 ? (r = 0, e = -e) : (r = 1 + (e >> 4), e < 48 && (e &= 15)), e && (e < 8 || e > 15) ? y : (null !== n.window && n.wbits !== e && (n.window = null), n.wrap = r, n.wbits = e, at(t))) : y;
      }

      function ut(t, e) {
        var r, n;
        return t ? (n = new it(), t.state = n, n.window = null, (r = st(t, e)) !== p && (t.state = null), r) : y;
      }

      var ft,
          ct,
          lt = !0;

      function ht(t) {
        if (lt) {
          var e;

          for (ft = new n.Buf32(512), ct = new n.Buf32(32), e = 0; e < 144;) t.lens[e++] = 8;

          for (; e < 256;) t.lens[e++] = 9;

          for (; e < 280;) t.lens[e++] = 7;

          for (; e < 288;) t.lens[e++] = 8;

          for (s(f, t.lens, 0, 288, ft, 0, t.work, {
            bits: 9
          }), e = 0; e < 32;) t.lens[e++] = 5;

          s(c, t.lens, 0, 32, ct, 0, t.work, {
            bits: 5
          }), lt = !1;
        }

        t.lencode = ft, t.lenbits = 9, t.distcode = ct, t.distbits = 5;
      }

      function dt(t, e, r, i) {
        var o,
            a = t.state;
        return null === a.window && (a.wsize = 1 << a.wbits, a.wnext = 0, a.whave = 0, a.window = new n.Buf8(a.wsize)), i >= a.wsize ? (n.arraySet(a.window, e, r - a.wsize, a.wsize, 0), a.wnext = 0, a.whave = a.wsize) : ((o = a.wsize - a.wnext) > i && (o = i), n.arraySet(a.window, e, r - i, o, a.wnext), (i -= o) ? (n.arraySet(a.window, e, r - i, i, 0), a.wnext = i, a.whave = a.wsize) : (a.wnext += o, a.wnext === a.wsize && (a.wnext = 0), a.whave < a.wsize && (a.whave += o))), 0;
      }

      e.inflateReset = at, e.inflateReset2 = st, e.inflateResetKeep = ot, e.inflateInit = function (t) {
        return ut(t, rt);
      }, e.inflateInit2 = ut, e.inflate = function (t, e) {
        var r,
            tt,
            et,
            rt,
            it,
            ot,
            at,
            st,
            ut,
            ft,
            ct,
            lt,
            pt,
            gt,
            vt,
            yt,
            mt,
            bt,
            _t,
            wt,
            xt,
            Et,
            St,
            kt,
            At = 0,
            Ct = new n.Buf8(4),
            Tt = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];

        if (!t || !t.state || !t.output || !t.input && 0 !== t.avail_in) return y;
        (r = t.state).mode === B && (r.mode = P), it = t.next_out, et = t.output, at = t.avail_out, rt = t.next_in, tt = t.input, ot = t.avail_in, st = r.hold, ut = r.bits, ft = ot, ct = at, Et = p;

        t: for (;;) switch (r.mode) {
          case x:
            if (0 === r.wrap) {
              r.mode = P;
              break;
            }

            for (; ut < 16;) {
              if (0 === ot) break t;
              ot--, st += tt[rt++] << ut, ut += 8;
            }

            if (2 & r.wrap && 35615 === st) {
              r.check = 0, Ct[0] = 255 & st, Ct[1] = st >>> 8 & 255, r.check = o(r.check, Ct, 2, 0), st = 0, ut = 0, r.mode = E;
              break;
            }

            if (r.flags = 0, r.head && (r.head.done = !1), !(1 & r.wrap) || (((255 & st) << 8) + (st >> 8)) % 31) {
              t.msg = "incorrect header check", r.mode = X;
              break;
            }

            if ((15 & st) !== w) {
              t.msg = "unknown compression method", r.mode = X;
              break;
            }

            if (ut -= 4, xt = 8 + (15 & (st >>>= 4)), 0 === r.wbits) r.wbits = xt;else if (xt > r.wbits) {
              t.msg = "invalid window size", r.mode = X;
              break;
            }
            r.dmax = 1 << xt, t.adler = r.check = 1, r.mode = 512 & st ? I : B, st = 0, ut = 0;
            break;

          case E:
            for (; ut < 16;) {
              if (0 === ot) break t;
              ot--, st += tt[rt++] << ut, ut += 8;
            }

            if (r.flags = st, (255 & r.flags) !== w) {
              t.msg = "unknown compression method", r.mode = X;
              break;
            }

            if (57344 & r.flags) {
              t.msg = "unknown header flags set", r.mode = X;
              break;
            }

            r.head && (r.head.text = st >> 8 & 1), 512 & r.flags && (Ct[0] = 255 & st, Ct[1] = st >>> 8 & 255, r.check = o(r.check, Ct, 2, 0)), st = 0, ut = 0, r.mode = S;

          case S:
            for (; ut < 32;) {
              if (0 === ot) break t;
              ot--, st += tt[rt++] << ut, ut += 8;
            }

            r.head && (r.head.time = st), 512 & r.flags && (Ct[0] = 255 & st, Ct[1] = st >>> 8 & 255, Ct[2] = st >>> 16 & 255, Ct[3] = st >>> 24 & 255, r.check = o(r.check, Ct, 4, 0)), st = 0, ut = 0, r.mode = k;

          case k:
            for (; ut < 16;) {
              if (0 === ot) break t;
              ot--, st += tt[rt++] << ut, ut += 8;
            }

            r.head && (r.head.xflags = 255 & st, r.head.os = st >> 8), 512 & r.flags && (Ct[0] = 255 & st, Ct[1] = st >>> 8 & 255, r.check = o(r.check, Ct, 2, 0)), st = 0, ut = 0, r.mode = A;

          case A:
            if (1024 & r.flags) {
              for (; ut < 16;) {
                if (0 === ot) break t;
                ot--, st += tt[rt++] << ut, ut += 8;
              }

              r.length = st, r.head && (r.head.extra_len = st), 512 & r.flags && (Ct[0] = 255 & st, Ct[1] = st >>> 8 & 255, r.check = o(r.check, Ct, 2, 0)), st = 0, ut = 0;
            } else r.head && (r.head.extra = null);

            r.mode = C;

          case C:
            if (1024 & r.flags && ((lt = r.length) > ot && (lt = ot), lt && (r.head && (xt = r.head.extra_len - r.length, r.head.extra || (r.head.extra = new Array(r.head.extra_len)), n.arraySet(r.head.extra, tt, rt, lt, xt)), 512 & r.flags && (r.check = o(r.check, tt, lt, rt)), ot -= lt, rt += lt, r.length -= lt), r.length)) break t;
            r.length = 0, r.mode = T;

          case T:
            if (2048 & r.flags) {
              if (0 === ot) break t;
              lt = 0;

              do {
                xt = tt[rt + lt++], r.head && xt && r.length < 65536 && (r.head.name += String.fromCharCode(xt));
              } while (xt && lt < ot);

              if (512 & r.flags && (r.check = o(r.check, tt, lt, rt)), ot -= lt, rt += lt, xt) break t;
            } else r.head && (r.head.name = null);

            r.length = 0, r.mode = R;

          case R:
            if (4096 & r.flags) {
              if (0 === ot) break t;
              lt = 0;

              do {
                xt = tt[rt + lt++], r.head && xt && r.length < 65536 && (r.head.comment += String.fromCharCode(xt));
              } while (xt && lt < ot);

              if (512 & r.flags && (r.check = o(r.check, tt, lt, rt)), ot -= lt, rt += lt, xt) break t;
            } else r.head && (r.head.comment = null);

            r.mode = O;

          case O:
            if (512 & r.flags) {
              for (; ut < 16;) {
                if (0 === ot) break t;
                ot--, st += tt[rt++] << ut, ut += 8;
              }

              if (st !== (65535 & r.check)) {
                t.msg = "header crc mismatch", r.mode = X;
                break;
              }

              st = 0, ut = 0;
            }

            r.head && (r.head.hcrc = r.flags >> 9 & 1, r.head.done = !0), t.adler = r.check = 0, r.mode = B;
            break;

          case I:
            for (; ut < 32;) {
              if (0 === ot) break t;
              ot--, st += tt[rt++] << ut, ut += 8;
            }

            t.adler = r.check = nt(st), st = 0, ut = 0, r.mode = L;

          case L:
            if (0 === r.havedict) return t.next_out = it, t.avail_out = at, t.next_in = rt, t.avail_in = ot, r.hold = st, r.bits = ut, v;
            t.adler = r.check = 1, r.mode = B;

          case B:
            if (e === h || e === d) break t;

          case P:
            if (r.last) {
              st >>>= 7 & ut, ut -= 7 & ut, r.mode = W;
              break;
            }

            for (; ut < 3;) {
              if (0 === ot) break t;
              ot--, st += tt[rt++] << ut, ut += 8;
            }

            switch (r.last = 1 & st, ut -= 1, 3 & (st >>>= 1)) {
              case 0:
                r.mode = F;
                break;

              case 1:
                if (ht(r), r.mode = D, e === d) {
                  st >>>= 2, ut -= 2;
                  break t;
                }

                break;

              case 2:
                r.mode = j;
                break;

              case 3:
                t.msg = "invalid block type", r.mode = X;
            }

            st >>>= 2, ut -= 2;
            break;

          case F:
            for (st >>>= 7 & ut, ut -= 7 & ut; ut < 32;) {
              if (0 === ot) break t;
              ot--, st += tt[rt++] << ut, ut += 8;
            }

            if ((65535 & st) != (st >>> 16 ^ 65535)) {
              t.msg = "invalid stored block lengths", r.mode = X;
              break;
            }

            if (r.length = 65535 & st, st = 0, ut = 0, r.mode = N, e === d) break t;

          case N:
            r.mode = M;

          case M:
            if (lt = r.length) {
              if (lt > ot && (lt = ot), lt > at && (lt = at), 0 === lt) break t;
              n.arraySet(et, tt, rt, lt, it), ot -= lt, rt += lt, at -= lt, it += lt, r.length -= lt;
              break;
            }

            r.mode = B;
            break;

          case j:
            for (; ut < 14;) {
              if (0 === ot) break t;
              ot--, st += tt[rt++] << ut, ut += 8;
            }

            if (r.nlen = 257 + (31 & st), st >>>= 5, ut -= 5, r.ndist = 1 + (31 & st), st >>>= 5, ut -= 5, r.ncode = 4 + (15 & st), st >>>= 4, ut -= 4, r.nlen > 286 || r.ndist > 30) {
              t.msg = "too many length or distance symbols", r.mode = X;
              break;
            }

            r.have = 0, r.mode = z;

          case z:
            for (; r.have < r.ncode;) {
              for (; ut < 3;) {
                if (0 === ot) break t;
                ot--, st += tt[rt++] << ut, ut += 8;
              }

              r.lens[Tt[r.have++]] = 7 & st, st >>>= 3, ut -= 3;
            }

            for (; r.have < 19;) r.lens[Tt[r.have++]] = 0;

            if (r.lencode = r.lendyn, r.lenbits = 7, St = {
              bits: r.lenbits
            }, Et = s(u, r.lens, 0, 19, r.lencode, 0, r.work, St), r.lenbits = St.bits, Et) {
              t.msg = "invalid code lengths set", r.mode = X;
              break;
            }

            r.have = 0, r.mode = U;

          case U:
            for (; r.have < r.nlen + r.ndist;) {
              for (; yt = (At = r.lencode[st & (1 << r.lenbits) - 1]) >>> 16 & 255, mt = 65535 & At, !((vt = At >>> 24) <= ut);) {
                if (0 === ot) break t;
                ot--, st += tt[rt++] << ut, ut += 8;
              }

              if (mt < 16) st >>>= vt, ut -= vt, r.lens[r.have++] = mt;else {
                if (16 === mt) {
                  for (kt = vt + 2; ut < kt;) {
                    if (0 === ot) break t;
                    ot--, st += tt[rt++] << ut, ut += 8;
                  }

                  if (st >>>= vt, ut -= vt, 0 === r.have) {
                    t.msg = "invalid bit length repeat", r.mode = X;
                    break;
                  }

                  xt = r.lens[r.have - 1], lt = 3 + (3 & st), st >>>= 2, ut -= 2;
                } else if (17 === mt) {
                  for (kt = vt + 3; ut < kt;) {
                    if (0 === ot) break t;
                    ot--, st += tt[rt++] << ut, ut += 8;
                  }

                  ut -= vt, xt = 0, lt = 3 + (7 & (st >>>= vt)), st >>>= 3, ut -= 3;
                } else {
                  for (kt = vt + 7; ut < kt;) {
                    if (0 === ot) break t;
                    ot--, st += tt[rt++] << ut, ut += 8;
                  }

                  ut -= vt, xt = 0, lt = 11 + (127 & (st >>>= vt)), st >>>= 7, ut -= 7;
                }

                if (r.have + lt > r.nlen + r.ndist) {
                  t.msg = "invalid bit length repeat", r.mode = X;
                  break;
                }

                for (; lt--;) r.lens[r.have++] = xt;
              }
            }

            if (r.mode === X) break;

            if (0 === r.lens[256]) {
              t.msg = "invalid code -- missing end-of-block", r.mode = X;
              break;
            }

            if (r.lenbits = 9, St = {
              bits: r.lenbits
            }, Et = s(f, r.lens, 0, r.nlen, r.lencode, 0, r.work, St), r.lenbits = St.bits, Et) {
              t.msg = "invalid literal/lengths set", r.mode = X;
              break;
            }

            if (r.distbits = 6, r.distcode = r.distdyn, St = {
              bits: r.distbits
            }, Et = s(c, r.lens, r.nlen, r.ndist, r.distcode, 0, r.work, St), r.distbits = St.bits, Et) {
              t.msg = "invalid distances set", r.mode = X;
              break;
            }

            if (r.mode = D, e === d) break t;

          case D:
            r.mode = q;

          case q:
            if (ot >= 6 && at >= 258) {
              t.next_out = it, t.avail_out = at, t.next_in = rt, t.avail_in = ot, r.hold = st, r.bits = ut, a(t, ct), it = t.next_out, et = t.output, at = t.avail_out, rt = t.next_in, tt = t.input, ot = t.avail_in, st = r.hold, ut = r.bits, r.mode === B && (r.back = -1);
              break;
            }

            for (r.back = 0; yt = (At = r.lencode[st & (1 << r.lenbits) - 1]) >>> 16 & 255, mt = 65535 & At, !((vt = At >>> 24) <= ut);) {
              if (0 === ot) break t;
              ot--, st += tt[rt++] << ut, ut += 8;
            }

            if (yt && 0 == (240 & yt)) {
              for (bt = vt, _t = yt, wt = mt; yt = (At = r.lencode[wt + ((st & (1 << bt + _t) - 1) >> bt)]) >>> 16 & 255, mt = 65535 & At, !(bt + (vt = At >>> 24) <= ut);) {
                if (0 === ot) break t;
                ot--, st += tt[rt++] << ut, ut += 8;
              }

              st >>>= bt, ut -= bt, r.back += bt;
            }

            if (st >>>= vt, ut -= vt, r.back += vt, r.length = mt, 0 === yt) {
              r.mode = Y;
              break;
            }

            if (32 & yt) {
              r.back = -1, r.mode = B;
              break;
            }

            if (64 & yt) {
              t.msg = "invalid literal/length code", r.mode = X;
              break;
            }

            r.extra = 15 & yt, r.mode = Z;

          case Z:
            if (r.extra) {
              for (kt = r.extra; ut < kt;) {
                if (0 === ot) break t;
                ot--, st += tt[rt++] << ut, ut += 8;
              }

              r.length += st & (1 << r.extra) - 1, st >>>= r.extra, ut -= r.extra, r.back += r.extra;
            }

            r.was = r.length, r.mode = H;

          case H:
            for (; yt = (At = r.distcode[st & (1 << r.distbits) - 1]) >>> 16 & 255, mt = 65535 & At, !((vt = At >>> 24) <= ut);) {
              if (0 === ot) break t;
              ot--, st += tt[rt++] << ut, ut += 8;
            }

            if (0 == (240 & yt)) {
              for (bt = vt, _t = yt, wt = mt; yt = (At = r.distcode[wt + ((st & (1 << bt + _t) - 1) >> bt)]) >>> 16 & 255, mt = 65535 & At, !(bt + (vt = At >>> 24) <= ut);) {
                if (0 === ot) break t;
                ot--, st += tt[rt++] << ut, ut += 8;
              }

              st >>>= bt, ut -= bt, r.back += bt;
            }

            if (st >>>= vt, ut -= vt, r.back += vt, 64 & yt) {
              t.msg = "invalid distance code", r.mode = X;
              break;
            }

            r.offset = mt, r.extra = 15 & yt, r.mode = V;

          case V:
            if (r.extra) {
              for (kt = r.extra; ut < kt;) {
                if (0 === ot) break t;
                ot--, st += tt[rt++] << ut, ut += 8;
              }

              r.offset += st & (1 << r.extra) - 1, st >>>= r.extra, ut -= r.extra, r.back += r.extra;
            }

            if (r.offset > r.dmax) {
              t.msg = "invalid distance too far back", r.mode = X;
              break;
            }

            r.mode = G;

          case G:
            if (0 === at) break t;

            if (lt = ct - at, r.offset > lt) {
              if ((lt = r.offset - lt) > r.whave && r.sane) {
                t.msg = "invalid distance too far back", r.mode = X;
                break;
              }

              lt > r.wnext ? (lt -= r.wnext, pt = r.wsize - lt) : pt = r.wnext - lt, lt > r.length && (lt = r.length), gt = r.window;
            } else gt = et, pt = it - r.offset, lt = r.length;

            lt > at && (lt = at), at -= lt, r.length -= lt;

            do {
              et[it++] = gt[pt++];
            } while (--lt);

            0 === r.length && (r.mode = q);
            break;

          case Y:
            if (0 === at) break t;
            et[it++] = r.length, at--, r.mode = q;
            break;

          case W:
            if (r.wrap) {
              for (; ut < 32;) {
                if (0 === ot) break t;
                ot--, st |= tt[rt++] << ut, ut += 8;
              }

              if (ct -= at, t.total_out += ct, r.total += ct, ct && (t.adler = r.check = r.flags ? o(r.check, et, ct, it - ct) : i(r.check, et, ct, it - ct)), ct = at, (r.flags ? st : nt(st)) !== r.check) {
                t.msg = "incorrect data check", r.mode = X;
                break;
              }

              st = 0, ut = 0;
            }

            r.mode = K;

          case K:
            if (r.wrap && r.flags) {
              for (; ut < 32;) {
                if (0 === ot) break t;
                ot--, st += tt[rt++] << ut, ut += 8;
              }

              if (st !== (4294967295 & r.total)) {
                t.msg = "incorrect length check", r.mode = X;
                break;
              }

              st = 0, ut = 0;
            }

            r.mode = Q;

          case Q:
            Et = g;
            break t;

          case X:
            Et = m;
            break t;

          case $:
            return b;

          case J:
          default:
            return y;
        }

        return t.next_out = it, t.avail_out = at, t.next_in = rt, t.avail_in = ot, r.hold = st, r.bits = ut, (r.wsize || ct !== t.avail_out && r.mode < X && (r.mode < W || e !== l)) && dt(t, t.output, t.next_out, ct - t.avail_out) ? (r.mode = $, b) : (ft -= t.avail_in, ct -= t.avail_out, t.total_in += ft, t.total_out += ct, r.total += ct, r.wrap && ct && (t.adler = r.check = r.flags ? o(r.check, et, ct, t.next_out - ct) : i(r.check, et, ct, t.next_out - ct)), t.data_type = r.bits + (r.last ? 64 : 0) + (r.mode === B ? 128 : 0) + (r.mode === D || r.mode === N ? 256 : 0), (0 === ft && 0 === ct || e === l) && Et === p && (Et = _), Et);
      }, e.inflateEnd = function (t) {
        if (!t || !t.state) return y;
        var e = t.state;
        return e.window && (e.window = null), t.state = null, p;
      }, e.inflateGetHeader = function (t, e) {
        var r;
        return t && t.state ? 0 == (2 & (r = t.state).wrap) ? y : (r.head = e, e.done = !1, p) : y;
      }, e.inflateSetDictionary = function (t, e) {
        var r,
            n = e.length;
        return t && t.state ? 0 !== (r = t.state).wrap && r.mode !== L ? y : r.mode === L && i(1, e, n, 0) !== r.check ? m : dt(t, e, n, n) ? (r.mode = $, b) : (r.havedict = 1, p) : y;
      }, e.inflateInfo = "pako inflate (from Nodeca project)";
    }, function (t, e, r) {

      t.exports = function (t, e) {
        var r, n, i, o, a, s, u, f, c, l, h, d, p, g, v, y, m, b, _, w, x, E, S, k, A;

        r = t.state, n = t.next_in, k = t.input, i = n + (t.avail_in - 5), o = t.next_out, A = t.output, a = o - (e - t.avail_out), s = o + (t.avail_out - 257), u = r.dmax, f = r.wsize, c = r.whave, l = r.wnext, h = r.window, d = r.hold, p = r.bits, g = r.lencode, v = r.distcode, y = (1 << r.lenbits) - 1, m = (1 << r.distbits) - 1;

        t: do {
          p < 15 && (d += k[n++] << p, p += 8, d += k[n++] << p, p += 8), b = g[d & y];

          e: for (;;) {
            if (d >>>= _ = b >>> 24, p -= _, 0 === (_ = b >>> 16 & 255)) A[o++] = 65535 & b;else {
              if (!(16 & _)) {
                if (0 == (64 & _)) {
                  b = g[(65535 & b) + (d & (1 << _) - 1)];
                  continue e;
                }

                if (32 & _) {
                  r.mode = 12;
                  break t;
                }

                t.msg = "invalid literal/length code", r.mode = 30;
                break t;
              }

              w = 65535 & b, (_ &= 15) && (p < _ && (d += k[n++] << p, p += 8), w += d & (1 << _) - 1, d >>>= _, p -= _), p < 15 && (d += k[n++] << p, p += 8, d += k[n++] << p, p += 8), b = v[d & m];

              r: for (;;) {
                if (d >>>= _ = b >>> 24, p -= _, !(16 & (_ = b >>> 16 & 255))) {
                  if (0 == (64 & _)) {
                    b = v[(65535 & b) + (d & (1 << _) - 1)];
                    continue r;
                  }

                  t.msg = "invalid distance code", r.mode = 30;
                  break t;
                }

                if (x = 65535 & b, p < (_ &= 15) && (d += k[n++] << p, (p += 8) < _ && (d += k[n++] << p, p += 8)), (x += d & (1 << _) - 1) > u) {
                  t.msg = "invalid distance too far back", r.mode = 30;
                  break t;
                }

                if (d >>>= _, p -= _, x > (_ = o - a)) {
                  if ((_ = x - _) > c && r.sane) {
                    t.msg = "invalid distance too far back", r.mode = 30;
                    break t;
                  }

                  if (E = 0, S = h, 0 === l) {
                    if (E += f - _, _ < w) {
                      w -= _;

                      do {
                        A[o++] = h[E++];
                      } while (--_);

                      E = o - x, S = A;
                    }
                  } else if (l < _) {
                    if (E += f + l - _, (_ -= l) < w) {
                      w -= _;

                      do {
                        A[o++] = h[E++];
                      } while (--_);

                      if (E = 0, l < w) {
                        w -= _ = l;

                        do {
                          A[o++] = h[E++];
                        } while (--_);

                        E = o - x, S = A;
                      }
                    }
                  } else if (E += l - _, _ < w) {
                    w -= _;

                    do {
                      A[o++] = h[E++];
                    } while (--_);

                    E = o - x, S = A;
                  }

                  for (; w > 2;) A[o++] = S[E++], A[o++] = S[E++], A[o++] = S[E++], w -= 3;

                  w && (A[o++] = S[E++], w > 1 && (A[o++] = S[E++]));
                } else {
                  E = o - x;

                  do {
                    A[o++] = A[E++], A[o++] = A[E++], A[o++] = A[E++], w -= 3;
                  } while (w > 2);

                  w && (A[o++] = A[E++], w > 1 && (A[o++] = A[E++]));
                }

                break;
              }
            }
            break;
          }
        } while (n < i && o < s);

        n -= w = p >> 3, d &= (1 << (p -= w << 3)) - 1, t.next_in = n, t.next_out = o, t.avail_in = n < i ? i - n + 5 : 5 - (n - i), t.avail_out = o < s ? s - o + 257 : 257 - (o - s), r.hold = d, r.bits = p;
      };
    }, function (t, e, r) {

      var n = r(72),
          i = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0],
          o = [16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78],
          a = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0],
          s = [16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 64, 64];

      t.exports = function (t, e, r, u, f, c, l, h) {
        var d,
            p,
            g,
            v,
            y,
            m,
            b,
            _,
            w,
            x = h.bits,
            E = 0,
            S = 0,
            k = 0,
            A = 0,
            C = 0,
            T = 0,
            R = 0,
            O = 0,
            I = 0,
            L = 0,
            B = null,
            P = 0,
            F = new n.Buf16(16),
            N = new n.Buf16(16),
            M = null,
            j = 0;

        for (E = 0; E <= 15; E++) F[E] = 0;

        for (S = 0; S < u; S++) F[e[r + S]]++;

        for (C = x, A = 15; A >= 1 && 0 === F[A]; A--);

        if (C > A && (C = A), 0 === A) return f[c++] = 20971520, f[c++] = 20971520, h.bits = 1, 0;

        for (k = 1; k < A && 0 === F[k]; k++);

        for (C < k && (C = k), O = 1, E = 1; E <= 15; E++) if (O <<= 1, (O -= F[E]) < 0) return -1;

        if (O > 0 && (0 === t || 1 !== A)) return -1;

        for (N[1] = 0, E = 1; E < 15; E++) N[E + 1] = N[E] + F[E];

        for (S = 0; S < u; S++) 0 !== e[r + S] && (l[N[e[r + S]]++] = S);

        if (0 === t ? (B = M = l, m = 19) : 1 === t ? (B = i, P -= 257, M = o, j -= 257, m = 256) : (B = a, M = s, m = -1), L = 0, S = 0, E = k, y = c, T = C, R = 0, g = -1, v = (I = 1 << C) - 1, 1 === t && I > 852 || 2 === t && I > 592) return 1;

        for (;;) {
          b = E - R, l[S] < m ? (_ = 0, w = l[S]) : l[S] > m ? (_ = M[j + l[S]], w = B[P + l[S]]) : (_ = 96, w = 0), d = 1 << E - R, k = p = 1 << T;

          do {
            f[y + (L >> R) + (p -= d)] = b << 24 | _ << 16 | w | 0;
          } while (0 !== p);

          for (d = 1 << E - 1; L & d;) d >>= 1;

          if (0 !== d ? (L &= d - 1, L += d) : L = 0, S++, 0 == --F[E]) {
            if (E === A) break;
            E = e[r + l[S]];
          }

          if (E > C && (L & v) !== g) {
            for (0 === R && (R = C), y += k, O = 1 << (T = E - R); T + R < A && !((O -= F[T + R]) <= 0);) T++, O <<= 1;

            if (I += 1 << T, 1 === t && I > 852 || 2 === t && I > 592) return 1;
            f[g = L & v] = C << 24 | T << 16 | y - c | 0;
          }
        }

        return 0 !== L && (f[y + L] = E - R << 24 | 64 << 16 | 0), h.bits = C, 0;
      };
    }, function (t, e, r) {

      t.exports = {
        Z_NO_FLUSH: 0,
        Z_PARTIAL_FLUSH: 1,
        Z_SYNC_FLUSH: 2,
        Z_FULL_FLUSH: 3,
        Z_FINISH: 4,
        Z_BLOCK: 5,
        Z_TREES: 6,
        Z_OK: 0,
        Z_STREAM_END: 1,
        Z_NEED_DICT: 2,
        Z_ERRNO: -1,
        Z_STREAM_ERROR: -2,
        Z_DATA_ERROR: -3,
        Z_BUF_ERROR: -5,
        Z_NO_COMPRESSION: 0,
        Z_BEST_SPEED: 1,
        Z_BEST_COMPRESSION: 9,
        Z_DEFAULT_COMPRESSION: -1,
        Z_FILTERED: 1,
        Z_HUFFMAN_ONLY: 2,
        Z_RLE: 3,
        Z_FIXED: 4,
        Z_DEFAULT_STRATEGY: 0,
        Z_BINARY: 0,
        Z_TEXT: 1,
        Z_UNKNOWN: 2,
        Z_DEFLATED: 8
      };
    }, function (t, e, r) {
      var n = r(19).Buffer,
          i = [0, 1996959894, 3993919788, 2567524794, 124634137, 1886057615, 3915621685, 2657392035, 249268274, 2044508324, 3772115230, 2547177864, 162941995, 2125561021, 3887607047, 2428444049, 498536548, 1789927666, 4089016648, 2227061214, 450548861, 1843258603, 4107580753, 2211677639, 325883990, 1684777152, 4251122042, 2321926636, 335633487, 1661365465, 4195302755, 2366115317, 997073096, 1281953886, 3579855332, 2724688242, 1006888145, 1258607687, 3524101629, 2768942443, 901097722, 1119000684, 3686517206, 2898065728, 853044451, 1172266101, 3705015759, 2882616665, 651767980, 1373503546, 3369554304, 3218104598, 565507253, 1454621731, 3485111705, 3099436303, 671266974, 1594198024, 3322730930, 2970347812, 795835527, 1483230225, 3244367275, 3060149565, 1994146192, 31158534, 2563907772, 4023717930, 1907459465, 112637215, 2680153253, 3904427059, 2013776290, 251722036, 2517215374, 3775830040, 2137656763, 141376813, 2439277719, 3865271297, 1802195444, 476864866, 2238001368, 4066508878, 1812370925, 453092731, 2181625025, 4111451223, 1706088902, 314042704, 2344532202, 4240017532, 1658658271, 366619977, 2362670323, 4224994405, 1303535960, 984961486, 2747007092, 3569037538, 1256170817, 1037604311, 2765210733, 3554079995, 1131014506, 879679996, 2909243462, 3663771856, 1141124467, 855842277, 2852801631, 3708648649, 1342533948, 654459306, 3188396048, 3373015174, 1466479909, 544179635, 3110523913, 3462522015, 1591671054, 702138776, 2966460450, 3352799412, 1504918807, 783551873, 3082640443, 3233442989, 3988292384, 2596254646, 62317068, 1957810842, 3939845945, 2647816111, 81470997, 1943803523, 3814918930, 2489596804, 225274430, 2053790376, 3826175755, 2466906013, 167816743, 2097651377, 4027552580, 2265490386, 503444072, 1762050814, 4150417245, 2154129355, 426522225, 1852507879, 4275313526, 2312317920, 282753626, 1742555852, 4189708143, 2394877945, 397917763, 1622183637, 3604390888, 2714866558, 953729732, 1340076626, 3518719985, 2797360999, 1068828381, 1219638859, 3624741850, 2936675148, 906185462, 1090812512, 3747672003, 2825379669, 829329135, 1181335161, 3412177804, 3160834842, 628085408, 1382605366, 3423369109, 3138078467, 570562233, 1426400815, 3317316542, 2998733608, 733239954, 1555261956, 3268935591, 3050360625, 752459403, 1541320221, 2607071920, 3965973030, 1969922972, 40735498, 2617837225, 3943577151, 1913087877, 83908371, 2512341634, 3803740692, 2075208622, 213261112, 2463272603, 3855990285, 2094854071, 198958881, 2262029012, 4057260610, 1759359992, 534414190, 2176718541, 4139329115, 1873836001, 414664567, 2282248934, 4279200368, 1711684554, 285281116, 2405801727, 4167216745, 1634467795, 376229701, 2685067896, 3608007406, 1308918612, 956543938, 2808555105, 3495958263, 1231636301, 1047427035, 2932959818, 3654703836, 1088359270, 936918e3, 2847714899, 3736837829, 1202900863, 817233897, 3183342108, 3401237130, 1404277552, 615818150, 3134207493, 3453421203, 1423857449, 601450431, 3009837614, 3294710456, 1567103746, 711928724, 3020668471, 3272380065, 1510334235, 755167117];

      function o(t) {
        if (n.isBuffer(t)) return t;
        var e = "function" == typeof n.alloc && "function" == typeof n.from;
        if ("number" == typeof t) return e ? n.alloc(t) : new n(t);
        if ("string" == typeof t) return e ? n.from(t) : new n(t);
        throw new Error("input must be buffer, number, or string, received " + typeof t);
      }

      function a(t, e) {
        t = o(t), n.isBuffer(e) && (e = e.readUInt32BE(0));

        for (var r = -1 ^ ~~e, a = 0; a < t.length; a++) r = i[255 & (r ^ t[a])] ^ r >>> 8;

        return -1 ^ r;
      }

      function s() {
        return t = a.apply(null, arguments), (e = o(4)).writeInt32BE(t, 0), e;
        var t, e;
      }

      "undefined" != typeof Int32Array && (i = new Int32Array(i)), s.signed = function () {
        return a.apply(null, arguments);
      }, s.unsigned = function () {
        return a.apply(null, arguments) >>> 0;
      }, t.exports = s;
    }, function (t, e, r) {
      var n = r(226),
          i = r(229);

      function o(t) {
        return (o = "function" == typeof i && "symbol" == typeof n ? function (t) {
          return typeof t;
        } : function (t) {
          return t && "function" == typeof i && t.constructor === i && t !== i.prototype ? "symbol" : typeof t;
        })(t);
      }

      function a(e) {
        return "function" == typeof i && "symbol" === o(n) ? t.exports = a = function (t) {
          return o(t);
        } : t.exports = a = function (t) {
          return t && "function" == typeof i && t.constructor === i && t !== i.prototype ? "symbol" : o(t);
        }, a(e);
      }

      t.exports = a;
    }, function (t, e, r) {
      t.exports = r(227);
    }, function (t, e, r) {
      t.exports = r(228);
    }, function (t, e, r) {
      r(136), r(46), r(41);
      var n = r(95);
      t.exports = n.f("iterator");
    }, function (t, e, r) {
      t.exports = r(230);
    }, function (t, e, r) {
      t.exports = r(231), r(249), r(250), r(251), r(252), r(253);
    }, function (t, e, r) {
      r(114), r(88), r(232), r(234), r(235), r(236), r(237), r(136), r(238), r(239), r(240), r(241), r(242), r(243), r(244), r(245), r(246), r(247), r(248);
      var n = r(3);
      t.exports = n.Symbol;
    }, function (t, e, r) {

      var n = r(0),
          i = r(13),
          o = r(4),
          a = r(17),
          s = r(110),
          u = r(12),
          f = r(24),
          c = r(53),
          l = r(14),
          h = r(2),
          d = r(22),
          p = r(28),
          g = r(62),
          v = r(43),
          y = r(44),
          m = r(51),
          b = r(137),
          _ = r(233),
          w = r(138),
          x = r(81),
          E = r(21),
          S = r(65),
          k = r(20),
          A = r(52),
          C = r(61),
          T = r(63),
          R = r(50),
          O = r(64),
          I = r(6),
          L = r(95),
          B = r(11),
          P = r(36),
          F = r(42),
          N = r(45).forEach,
          M = T("hidden"),
          j = I("toPrimitive"),
          z = F.set,
          U = F.getterFor("Symbol"),
          D = Object.prototype,
          q = i.Symbol,
          Z = i.JSON,
          H = Z && Z.stringify,
          V = x.f,
          G = E.f,
          Y = _.f,
          W = S.f,
          K = C("symbols"),
          Q = C("op-symbols"),
          X = C("string-to-symbol-registry"),
          $ = C("symbol-to-string-registry"),
          J = C("wks"),
          tt = i.QObject,
          et = !tt || !tt.prototype || !tt.prototype.findChild,
          rt = a && u(function () {
        return 7 != y(G({}, "a", {
          get: function () {
            return G(this, "a", {
              value: 7
            }).a;
          }
        })).a;
      }) ? function (t, e, r) {
        var n = V(D, e);
        n && delete D[e], G(t, e, r), n && t !== D && G(D, e, n);
      } : G,
          nt = function (t, e) {
        var r = K[t] = y(q.prototype);
        return z(r, {
          type: "Symbol",
          tag: t,
          description: e
        }), a || (r.description = e), r;
      },
          it = s && "symbol" == typeof q.iterator ? function (t) {
        return "symbol" == typeof t;
      } : function (t) {
        return Object(t) instanceof q;
      },
          ot = function (t, e, r) {
        t === D && ot(Q, e, r), h(t);
        var n = g(e, !0);
        return h(r), f(K, n) ? (r.enumerable ? (f(t, M) && t[M][n] && (t[M][n] = !1), r = y(r, {
          enumerable: v(0, !1)
        })) : (f(t, M) || G(t, M, v(1, {})), t[M][n] = !0), rt(t, n, r)) : G(t, n, r);
      },
          at = function (t, e) {
        h(t);
        var r = p(e),
            n = m(r).concat(ct(r));
        return N(n, function (e) {
          a && !st.call(r, e) || ot(t, e, r[e]);
        }), t;
      },
          st = function (t) {
        var e = g(t, !0),
            r = W.call(this, e);
        return !(this === D && f(K, e) && !f(Q, e)) && (!(r || !f(this, e) || !f(K, e) || f(this, M) && this[M][e]) || r);
      },
          ut = function (t, e) {
        var r = p(t),
            n = g(e, !0);

        if (r !== D || !f(K, n) || f(Q, n)) {
          var i = V(r, n);
          return !i || !f(K, n) || f(r, M) && r[M][n] || (i.enumerable = !0), i;
        }
      },
          ft = function (t) {
        var e = Y(p(t)),
            r = [];
        return N(e, function (t) {
          f(K, t) || f(R, t) || r.push(t);
        }), r;
      },
          ct = function (t) {
        var e = t === D,
            r = Y(e ? Q : p(t)),
            n = [];
        return N(r, function (t) {
          !f(K, t) || e && !f(D, t) || n.push(K[t]);
        }), n;
      };

      s || (A((q = function () {
        if (this instanceof q) throw TypeError("Symbol is not a constructor");

        var t = arguments.length && void 0 !== arguments[0] ? String(arguments[0]) : void 0,
            e = O(t),
            r = function (t) {
          this === D && r.call(Q, t), f(this, M) && f(this[M], e) && (this[M][e] = !1), rt(this, e, v(1, t));
        };

        return a && et && rt(D, e, {
          configurable: !0,
          set: r
        }), nt(e, t);
      }).prototype, "toString", function () {
        return U(this).tag;
      }), S.f = st, E.f = ot, x.f = ut, b.f = _.f = ft, w.f = ct, a && (G(q.prototype, "description", {
        configurable: !0,
        get: function () {
          return U(this).description;
        }
      }), o || A(D, "propertyIsEnumerable", st, {
        unsafe: !0
      })), L.f = function (t) {
        return nt(I(t), t);
      }), n({
        global: !0,
        wrap: !0,
        forced: !s,
        sham: !s
      }, {
        Symbol: q
      }), N(m(J), function (t) {
        B(t);
      }), n({
        target: "Symbol",
        stat: !0,
        forced: !s
      }, {
        for: function (t) {
          var e = String(t);
          if (f(X, e)) return X[e];
          var r = q(e);
          return X[e] = r, $[r] = e, r;
        },
        keyFor: function (t) {
          if (!it(t)) throw TypeError(t + " is not a symbol");
          if (f($, t)) return $[t];
        },
        useSetter: function () {
          et = !0;
        },
        useSimple: function () {
          et = !1;
        }
      }), n({
        target: "Object",
        stat: !0,
        forced: !s,
        sham: !a
      }, {
        create: function (t, e) {
          return void 0 === e ? y(t) : at(y(t), e);
        },
        defineProperty: ot,
        defineProperties: at,
        getOwnPropertyDescriptor: ut
      }), n({
        target: "Object",
        stat: !0,
        forced: !s
      }, {
        getOwnPropertyNames: ft,
        getOwnPropertySymbols: ct
      }), n({
        target: "Object",
        stat: !0,
        forced: u(function () {
          w.f(1);
        })
      }, {
        getOwnPropertySymbols: function (t) {
          return w.f(d(t));
        }
      }), Z && n({
        target: "JSON",
        stat: !0,
        forced: !s || u(function () {
          var t = q();
          return "[null]" != H([t]) || "{}" != H({
            a: t
          }) || "{}" != H(Object(t));
        })
      }, {
        stringify: function (t) {
          for (var e, r, n = [t], i = 1; arguments.length > i;) n.push(arguments[i++]);

          if (r = e = n[1], (l(e) || void 0 !== t) && !it(t)) return c(e) || (e = function (t, e) {
            if ("function" == typeof r && (e = r.call(this, t, e)), !it(e)) return e;
          }), n[1] = e, H.apply(Z, n);
        }
      }), q.prototype[j] || k(q.prototype, j, q.prototype.valueOf), P(q, "Symbol"), R[M] = !0;
    }, function (t, e, r) {
      var n = r(28),
          i = r(137).f,
          o = {}.toString,
          a = "object" == typeof window && window && Object.getOwnPropertyNames ? Object.getOwnPropertyNames(window) : [];

      t.exports.f = function (t) {
        return a && "[object Window]" == o.call(t) ? function (t) {
          try {
            return i(t);
          } catch (t) {
            return a.slice();
          }
        }(t) : i(n(t));
      };
    }, function (t, e, r) {
      r(11)("asyncIterator");
    }, function (t, e) {}, function (t, e, r) {
      r(11)("hasInstance");
    }, function (t, e, r) {
      r(11)("isConcatSpreadable");
    }, function (t, e, r) {
      r(11)("match");
    }, function (t, e, r) {
      r(11)("matchAll");
    }, function (t, e, r) {
      r(11)("replace");
    }, function (t, e, r) {
      r(11)("search");
    }, function (t, e, r) {
      r(11)("species");
    }, function (t, e, r) {
      r(11)("split");
    }, function (t, e, r) {
      r(11)("toPrimitive");
    }, function (t, e, r) {
      r(11)("toStringTag");
    }, function (t, e, r) {
      r(11)("unscopables");
    }, function (t, e, r) {
      r(36)(Math, "Math", !0);
    }, function (t, e, r) {
      var n = r(13);
      r(36)(n.JSON, "JSON", !0);
    }, function (t, e, r) {
      r(11)("asyncDispose");
    }, function (t, e, r) {
      r(11)("dispose");
    }, function (t, e, r) {
      r(11)("observable");
    }, function (t, e, r) {
      r(11)("patternMatch");
    }, function (t, e, r) {
      r(11)("replaceAll");
    }, function (t, e) {
      t.exports = function (t) {
        if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
        return t;
      };
    }, function (t, e, r) {
      t.exports = r(256);
    }, function (t, e, r) {
      t.exports = r(257);
    }, function (t, e, r) {
      r(258);
      var n = r(3);
      t.exports = n.Object.getPrototypeOf;
    }, function (t, e, r) {
      var n = r(0),
          i = r(12),
          o = r(22),
          a = r(66),
          s = r(109);
      n({
        target: "Object",
        stat: !0,
        forced: i(function () {
          a(1);
        }),
        sham: !s
      }, {
        getPrototypeOf: function (t) {
          return a(o(t));
        }
      });
    }, function (t, e, r) {
      t.exports = r(260);
    }, function (t, e, r) {
      r(261);
      var n = r(3);
      t.exports = n.Object.setPrototypeOf;
    }, function (t, e, r) {
      r(0)({
        target: "Object",
        stat: !0
      }, {
        setPrototypeOf: r(86)
      });
    }, function (t, e, r) {
      t.exports = r(263);
    }, function (t, e, r) {
      r(264);
      var n = r(3).Object;

      t.exports = function (t, e) {
        return n.create(t, e);
      };
    }, function (t, e, r) {
      r(0)({
        target: "Object",
        stat: !0,
        sham: !r(17)
      }, {
        create: r(44)
      });
    }, function (t, e, r) {
      var n = r(140),
          i = r(266),
          o = r(27),
          a = r(96),
          s = r(295),
          u = r(300);

      function f(e) {
        var r = "function" == typeof i ? new i() : void 0;
        return t.exports = f = function (t) {
          if (null === t || !s(t)) return t;
          if ("function" != typeof t) throw new TypeError("Super expression must either be null or a function");

          if (void 0 !== r) {
            if (r.has(t)) return r.get(t);
            r.set(t, e);
          }

          function e() {
            return u(t, arguments, o(this).constructor);
          }

          return e.prototype = n(t.prototype, {
            constructor: {
              value: e,
              enumerable: !1,
              writable: !0,
              configurable: !0
            }
          }), a(e, t);
        }, f(e);
      }

      t.exports = f;
    }, function (t, e, r) {
      t.exports = r(267);
    }, function (t, e, r) {
      t.exports = r(268), r(273), r(275), r(277), r(279), r(280), r(281), r(282), r(283), r(284), r(286), r(287), r(288), r(289), r(290), r(291), r(292), r(293), r(294);
    }, function (t, e, r) {
      r(269), r(88), r(46), r(41);
      var n = r(3);
      t.exports = n.Map;
    }, function (t, e, r) {

      var n = r(270),
          i = r(272);
      t.exports = n("Map", function (t) {
        return function () {
          return t(this, arguments.length ? arguments[0] : void 0);
        };
      }, i, !0);
    }, function (t, e, r) {

      var n = r(0),
          i = r(13),
          o = r(141),
          a = r(12),
          s = r(20),
          u = r(8),
          f = r(89),
          c = r(14),
          l = r(36),
          h = r(21).f,
          d = r(45).forEach,
          p = r(17),
          g = r(42),
          v = g.set,
          y = g.getterFor;

      t.exports = function (t, e, r, g, m) {
        var b,
            _ = i[t],
            w = _ && _.prototype,
            x = g ? "set" : "add",
            E = {};

        if (p && "function" == typeof _ && (m || w.forEach && !a(function () {
          new _().entries().next();
        }))) {
          b = e(function (e, r) {
            v(f(e, b, t), {
              type: t,
              collection: new _()
            }), null != r && u(r, e[x], e, g);
          });
          var S = y(t);
          d(["add", "clear", "delete", "forEach", "get", "has", "set", "keys", "values", "entries"], function (t) {
            var e = "add" == t || "set" == t;
            t in w && (!m || "clear" != t) && s(b.prototype, t, function (r, n) {
              var i = S(this).collection;
              if (!e && m && !c(r)) return "get" == t && void 0;
              var o = i[t](0 === r ? 0 : r, n);
              return e ? this : o;
            });
          }), m || h(b.prototype, "size", {
            get: function () {
              return S(this).collection.size;
            }
          });
        } else b = r.getConstructor(e, t, g, x), o.REQUIRED = !0;

        return l(b, t, !1, !0), E[t] = b, n({
          global: !0,
          forced: !0
        }, E), m || r.setStrong(b, t, g), b;
      };
    }, function (t, e, r) {
      var n = r(12);
      t.exports = !n(function () {
        return Object.isExtensible(Object.preventExtensions({}));
      });
    }, function (t, e, r) {

      var n = r(21).f,
          i = r(44),
          o = r(117),
          a = r(15),
          s = r(89),
          u = r(8),
          f = r(80),
          c = r(118),
          l = r(17),
          h = r(141).fastKey,
          d = r(42),
          p = d.set,
          g = d.getterFor;
      t.exports = {
        getConstructor: function (t, e, r, f) {
          var c = t(function (t, n) {
            s(t, c, e), p(t, {
              type: e,
              index: i(null),
              first: void 0,
              last: void 0,
              size: 0
            }), l || (t.size = 0), null != n && u(n, t[f], t, r);
          }),
              d = g(e),
              v = function (t, e, r) {
            var n,
                i,
                o = d(t),
                a = y(t, e);
            return a ? a.value = r : (o.last = a = {
              index: i = h(e, !0),
              key: e,
              value: r,
              previous: n = o.last,
              next: void 0,
              removed: !1
            }, o.first || (o.first = a), n && (n.next = a), l ? o.size++ : t.size++, "F" !== i && (o.index[i] = a)), t;
          },
              y = function (t, e) {
            var r,
                n = d(t),
                i = h(e);
            if ("F" !== i) return n.index[i];

            for (r = n.first; r; r = r.next) if (r.key == e) return r;
          };

          return o(c.prototype, {
            clear: function () {
              for (var t = d(this), e = t.index, r = t.first; r;) r.removed = !0, r.previous && (r.previous = r.previous.next = void 0), delete e[r.index], r = r.next;

              t.first = t.last = void 0, l ? t.size = 0 : this.size = 0;
            },
            delete: function (t) {
              var e = d(this),
                  r = y(this, t);

              if (r) {
                var n = r.next,
                    i = r.previous;
                delete e.index[r.index], r.removed = !0, i && (i.next = n), n && (n.previous = i), e.first == r && (e.first = n), e.last == r && (e.last = i), l ? e.size-- : this.size--;
              }

              return !!r;
            },
            forEach: function (t) {
              for (var e, r = d(this), n = a(t, arguments.length > 1 ? arguments[1] : void 0, 3); e = e ? e.next : r.first;) for (n(e.value, e.key, this); e && e.removed;) e = e.previous;
            },
            has: function (t) {
              return !!y(this, t);
            }
          }), o(c.prototype, r ? {
            get: function (t) {
              var e = y(this, t);
              return e && e.value;
            },
            set: function (t, e) {
              return v(this, 0 === t ? 0 : t, e);
            }
          } : {
            add: function (t) {
              return v(this, t = 0 === t ? 0 : t, t);
            }
          }), l && n(c.prototype, "size", {
            get: function () {
              return d(this).size;
            }
          }), c;
        },
        setStrong: function (t, e, r) {
          var n = e + " Iterator",
              i = g(e),
              o = g(n);
          f(t, e, function (t, e) {
            p(this, {
              type: n,
              target: t,
              state: i(t),
              kind: e,
              last: void 0
            });
          }, function () {
            for (var t = o(this), e = t.kind, r = t.last; r && r.removed;) r = r.previous;

            return t.target && (t.last = r = r ? r.next : t.state.first) ? "keys" == e ? {
              value: r.key,
              done: !1
            } : "values" == e ? {
              value: r.value,
              done: !1
            } : {
              value: [r.key, r.value],
              done: !1
            } : (t.target = void 0, {
              value: void 0,
              done: !0
            });
          }, r ? "entries" : "values", !r, !0), c(e);
        }
      };
    }, function (t, e, r) {
      r(0)({
        target: "Map",
        stat: !0
      }, {
        from: r(274)
      });
    }, function (t, e, r) {

      var n = r(5),
          i = r(15),
          o = r(8);

      t.exports = function (t) {
        var e,
            r,
            a,
            s,
            u = arguments.length,
            f = u > 1 ? arguments[1] : void 0;
        return n(this), (e = void 0 !== f) && n(f), null == t ? new this() : (r = [], e ? (a = 0, s = i(f, u > 2 ? arguments[2] : void 0, 2), o(t, function (t) {
          r.push(s(t, a++));
        })) : o(t, r.push, r), new this(r));
      };
    }, function (t, e, r) {
      r(0)({
        target: "Map",
        stat: !0
      }, {
        of: r(276)
      });
    }, function (t, e, r) {

      t.exports = function () {
        for (var t = arguments.length, e = new Array(t); t--;) e[t] = arguments[t];

        return new this(e);
      };
    }, function (t, e, r) {

      var n = r(0),
          i = r(4),
          o = r(278);
      n({
        target: "Map",
        proto: !0,
        real: !0,
        forced: i
      }, {
        deleteAll: function () {
          return o.apply(this, arguments);
        }
      });
    }, function (t, e, r) {

      var n = r(2),
          i = r(5);

      t.exports = function () {
        for (var t, e = n(this), r = i(e.delete), o = !0, a = 0, s = arguments.length; a < s; a++) t = r.call(e, arguments[a]), o = o && t;

        return !!o;
      };
    }, function (t, e, r) {

      var n = r(0),
          i = r(4),
          o = r(2),
          a = r(15),
          s = r(25),
          u = r(8);
      n({
        target: "Map",
        proto: !0,
        real: !0,
        forced: i
      }, {
        every: function (t) {
          var e = o(this),
              r = s(e),
              n = a(t, arguments.length > 1 ? arguments[1] : void 0, 3);
          return !u(r, function (t, r) {
            if (!n(r, t, e)) return u.stop();
          }, void 0, !0, !0).stopped;
        }
      });
    }, function (t, e, r) {

      var n = r(0),
          i = r(4),
          o = r(26),
          a = r(2),
          s = r(5),
          u = r(15),
          f = r(55),
          c = r(25),
          l = r(8);
      n({
        target: "Map",
        proto: !0,
        real: !0,
        forced: i
      }, {
        filter: function (t) {
          var e = a(this),
              r = c(e),
              n = u(t, arguments.length > 1 ? arguments[1] : void 0, 3),
              i = new (f(e, o("Map")))(),
              h = s(i.set);
          return l(r, function (t, r) {
            n(r, t, e) && h.call(i, t, r);
          }, void 0, !0, !0), i;
        }
      });
    }, function (t, e, r) {

      var n = r(0),
          i = r(4),
          o = r(2),
          a = r(15),
          s = r(25),
          u = r(8);
      n({
        target: "Map",
        proto: !0,
        real: !0,
        forced: i
      }, {
        find: function (t) {
          var e = o(this),
              r = s(e),
              n = a(t, arguments.length > 1 ? arguments[1] : void 0, 3);
          return u(r, function (t, r) {
            if (n(r, t, e)) return u.stop(r);
          }, void 0, !0, !0).result;
        }
      });
    }, function (t, e, r) {

      var n = r(0),
          i = r(4),
          o = r(2),
          a = r(15),
          s = r(25),
          u = r(8);
      n({
        target: "Map",
        proto: !0,
        real: !0,
        forced: i
      }, {
        findKey: function (t) {
          var e = o(this),
              r = s(e),
              n = a(t, arguments.length > 1 ? arguments[1] : void 0, 3);
          return u(r, function (t, r) {
            if (n(r, t, e)) return u.stop(t);
          }, void 0, !0, !0).result;
        }
      });
    }, function (t, e, r) {

      var n = r(0),
          i = r(8),
          o = r(5);
      n({
        target: "Map",
        stat: !0
      }, {
        groupBy: function (t, e) {
          var r = new this();
          o(e);
          var n = o(r.has),
              a = o(r.get),
              s = o(r.set);
          return i(t, function (t) {
            var i = e(t);
            n.call(r, i) ? a.call(r, i).push(t) : s.call(r, i, [t]);
          }), r;
        }
      });
    }, function (t, e, r) {

      var n = r(0),
          i = r(4),
          o = r(2),
          a = r(25),
          s = r(285),
          u = r(8);
      n({
        target: "Map",
        proto: !0,
        real: !0,
        forced: i
      }, {
        includes: function (t) {
          return u(a(o(this)), function (e, r) {
            if (s(r, t)) return u.stop();
          }, void 0, !0, !0).stopped;
        }
      });
    }, function (t, e) {
      t.exports = function (t, e) {
        return t === e || t != t && e != e;
      };
    }, function (t, e, r) {

      var n = r(0),
          i = r(8),
          o = r(5);
      n({
        target: "Map",
        stat: !0
      }, {
        keyBy: function (t, e) {
          var r = new this();
          o(e);
          var n = o(r.set);
          return i(t, function (t) {
            n.call(r, e(t), t);
          }), r;
        }
      });
    }, function (t, e, r) {

      var n = r(0),
          i = r(4),
          o = r(2),
          a = r(25),
          s = r(8);
      n({
        target: "Map",
        proto: !0,
        real: !0,
        forced: i
      }, {
        keyOf: function (t) {
          return s(a(o(this)), function (e, r) {
            if (r === t) return s.stop(e);
          }, void 0, !0, !0).result;
        }
      });
    }, function (t, e, r) {

      var n = r(0),
          i = r(4),
          o = r(26),
          a = r(2),
          s = r(5),
          u = r(15),
          f = r(55),
          c = r(25),
          l = r(8);
      n({
        target: "Map",
        proto: !0,
        real: !0,
        forced: i
      }, {
        mapKeys: function (t) {
          var e = a(this),
              r = c(e),
              n = u(t, arguments.length > 1 ? arguments[1] : void 0, 3),
              i = new (f(e, o("Map")))(),
              h = s(i.set);
          return l(r, function (t, r) {
            h.call(i, n(r, t, e), r);
          }, void 0, !0, !0), i;
        }
      });
    }, function (t, e, r) {

      var n = r(0),
          i = r(4),
          o = r(26),
          a = r(2),
          s = r(5),
          u = r(15),
          f = r(55),
          c = r(25),
          l = r(8);
      n({
        target: "Map",
        proto: !0,
        real: !0,
        forced: i
      }, {
        mapValues: function (t) {
          var e = a(this),
              r = c(e),
              n = u(t, arguments.length > 1 ? arguments[1] : void 0, 3),
              i = new (f(e, o("Map")))(),
              h = s(i.set);
          return l(r, function (t, r) {
            h.call(i, t, n(r, t, e));
          }, void 0, !0, !0), i;
        }
      });
    }, function (t, e, r) {

      var n = r(0),
          i = r(4),
          o = r(2),
          a = r(5),
          s = r(8);
      n({
        target: "Map",
        proto: !0,
        real: !0,
        forced: i
      }, {
        merge: function (t) {
          for (var e = o(this), r = a(e.set), n = 0; n < arguments.length;) s(arguments[n++], r, e, !0);

          return e;
        }
      });
    }, function (t, e, r) {

      var n = r(0),
          i = r(4),
          o = r(2),
          a = r(5),
          s = r(25),
          u = r(8);
      n({
        target: "Map",
        proto: !0,
        real: !0,
        forced: i
      }, {
        reduce: function (t) {
          var e,
              r,
              n = o(this),
              i = s(n);
          if (a(t), arguments.length > 1) e = arguments[1];else {
            if ((r = i.next()).done) throw TypeError("Reduce of empty map with no initial value");
            e = r.value[1];
          }
          return u(i, function (r, i) {
            e = t(e, i, r, n);
          }, void 0, !0, !0), e;
        }
      });
    }, function (t, e, r) {

      var n = r(0),
          i = r(4),
          o = r(2),
          a = r(15),
          s = r(25),
          u = r(8);
      n({
        target: "Map",
        proto: !0,
        real: !0,
        forced: i
      }, {
        some: function (t) {
          var e = o(this),
              r = s(e),
              n = a(t, arguments.length > 1 ? arguments[1] : void 0, 3);
          return u(r, function (t, r) {
            if (n(r, t, e)) return u.stop();
          }, void 0, !0, !0).stopped;
        }
      });
    }, function (t, e, r) {

      var n = r(0),
          i = r(4),
          o = r(2),
          a = r(5);
      n({
        target: "Map",
        proto: !0,
        real: !0,
        forced: i
      }, {
        update: function (t, e) {
          var r = o(this),
              n = arguments.length;
          a(e);
          var i = r.has(t);
          if (!i && n < 3) throw TypeError("Updating absent value");
          var s = i ? r.get(t) : a(n > 2 ? arguments[2] : void 0)(t, r);
          return r.set(t, e(s, t, r)), r;
        }
      });
    }, function (t, e, r) {

      var n = r(0),
          i = r(4),
          o = r(2),
          a = r(5);
      n({
        target: "Map",
        proto: !0,
        real: !0,
        forced: i
      }, {
        updateOrInsert: function (t, e, r) {
          var n = o(this);
          a(e), a(r);
          var i = n.has(t) ? e(n.get(t)) : r();
          return n.set(t, i), i;
        }
      });
    }, function (t, e, r) {
      var n = r(296);

      t.exports = function (t) {
        var e;
        return -1 !== n(e = Function.toString.call(t)).call(e, "[native code]");
      };
    }, function (t, e, r) {
      t.exports = r(297);
    }, function (t, e, r) {
      t.exports = r(143);
    }, function (t, e, r) {
      r(299);
      var n = r(18);
      t.exports = n("Array").indexOf;
    }, function (t, e, r) {

      var n = r(0),
          i = r(82).indexOf,
          o = r(54),
          a = [].indexOf,
          s = !!a && 1 / [1].indexOf(1, -0) < 0,
          u = o("indexOf");
      n({
        target: "Array",
        proto: !0,
        forced: s || u
      }, {
        indexOf: function (t) {
          return s ? a.apply(this, arguments) || 0 : i(this, t, arguments.length > 1 ? arguments[1] : void 0);
        }
      });
    }, function (t, e, r) {
      var n = r(301),
          i = r(305),
          o = r(96);

      function a(e, r, s) {
        return !function () {
          if ("undefined" == typeof Reflect || !i) return !1;
          if (i.sham) return !1;
          if ("function" == typeof Proxy) return !0;

          try {
            return Date.prototype.toString.call(i(Date, [], function () {})), !0;
          } catch (t) {
            return !1;
          }
        }() ? t.exports = a = function (t, e, r) {
          var i = [null];
          i.push.apply(i, e);
          var a = new (n(Function).apply(t, i))();
          return r && o(a, r.prototype), a;
        } : t.exports = a = i, a.apply(null, arguments);
      }

      t.exports = a;
    }, function (t, e, r) {
      t.exports = r(302);
    }, function (t, e, r) {
      t.exports = r(144);
    }, function (t, e, r) {
      r(304);
      var n = r(18);
      t.exports = n("Function").bind;
    }, function (t, e, r) {
      r(0)({
        target: "Function",
        proto: !0
      }, {
        bind: r(145)
      });
    }, function (t, e, r) {
      t.exports = r(306);
    }, function (t, e, r) {
      t.exports = r(307);
    }, function (t, e, r) {
      r(308);
      var n = r(3);
      t.exports = n.Reflect.construct;
    }, function (t, e, r) {
      var n = r(0),
          i = r(26),
          o = r(5),
          a = r(2),
          s = r(14),
          u = r(44),
          f = r(145),
          c = r(12),
          l = i("Reflect", "construct"),
          h = c(function () {
        function t() {}

        return !(l(function () {}, [], t) instanceof t);
      }),
          d = !c(function () {
        l(function () {});
      }),
          p = h || d;
      n({
        target: "Reflect",
        stat: !0,
        forced: p,
        sham: p
      }, {
        construct: function (t, e) {
          o(t), a(e);
          var r = arguments.length < 3 ? t : o(arguments[2]);
          if (d && !h) return l(t, e, r);

          if (t == r) {
            switch (e.length) {
              case 0:
                return new t();

              case 1:
                return new t(e[0]);

              case 2:
                return new t(e[0], e[1]);

              case 3:
                return new t(e[0], e[1], e[2]);

              case 4:
                return new t(e[0], e[1], e[2], e[3]);
            }

            var n = [null];
            return n.push.apply(n, e), new (f.apply(t, n))();
          }

          var i = r.prototype,
              c = u(s(i) ? i : Object.prototype),
              p = Function.apply.call(t, c, e);
          return s(p) ? p : c;
        }
      });
    }, function (t, e, r) {

      (function (e) {
        var n = r(1),
            i = n(r(97)),
            o = n(r(7)),
            a = n(r(10)),
            s = r(9).CramMalformedError,
            u = r(73),
            f = r(315),
            c = r(316),
            l = r(317),
            h = 4;

        var d = function () {
          function t(e) {
            var r = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : 0;
            (0, o.default)(this, t), this._buffer = e, this._position = r, this.length = e.length;
          }

          return (0, a.default)(t, [{
            key: "get",
            value: function () {
              var t = this._buffer[this._position];
              return this._position += 1, t;
            }
          }, {
            key: "getByte",
            value: function () {
              return this.get();
            }
          }, {
            key: "getByteAt",
            value: function (t) {
              return this._buffer[t];
            }
          }, {
            key: "position",
            value: function () {
              return this._position;
            }
          }, {
            key: "put",
            value: function (t) {
              return this._buffer[this._position] = t, this._position += 1, t;
            }
          }, {
            key: "putAt",
            value: function (t, e) {
              return this._buffer[t] = e, e;
            }
          }, {
            key: "setPosition",
            value: function (t) {
              return this._position = t, t;
            }
          }, {
            key: "getInt",
            value: function () {
              var t = this._buffer.readInt32LE(this._position);

              return this._position += 4, t;
            }
          }, {
            key: "remaining",
            value: function () {
              return this._buffer.length - this._position;
            }
          }]), t;
        }();

        t.exports = {
          uncompress: function (t, r) {
            var n = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : 0;
            if (0 === t.length) return (0, i.default)(r).call(r, 0), r;
            var o = new d(t, n),
                a = o.get();
            if (0 !== a && 1 !== a) throw new s("Invalid rANS order ".concat(a));
            if (o.getInt() !== o.remaining() - h) throw new s("Incorrect input length.");
            var p = o.getInt(),
                g = new d(r || e.allocUnsafe(p));
            if (g.length < p) throw new s("Output buffer too small to fit ".concat(p, " bytes."));

            switch (a) {
              case 0:
                return function (t, e) {
                  for (var r = new u.AriDecoder(), n = new Array(256), i = 0; i < n.length; i += 1) n[i] = new u.Symbol();

                  return f.readStatsO0(t, r, n), c.uncompress(t, r, n, e), e;
                }(o, g);

              case 1:
                return function (t, e) {
                  for (var r = new Array(256), n = 0; n < r.length; n += 1) r[n] = new u.AriDecoder();

                  for (var i = new Array(256), o = 0; o < i.length; o += 1) {
                    i[o] = new Array(256);

                    for (var a = 0; a < i[o].length; a += 1) i[o][a] = new u.Symbol();
                  }

                  return f.readStatsO1(t, r, i), l.uncompress(t, e, r, i), e;
                }(o, g);

              default:
                throw new s("Invalid rANS order: ".concat(a));
            }
          }
        };
      }).call(this, r(19).Buffer);
    }, function (t, e, r) {
      t.exports = r(311);
    }, function (t, e, r) {
      var n = r(312),
          i = Array.prototype;

      t.exports = function (t) {
        var e = t.fill;
        return t === i || t instanceof Array && e === i.fill ? n : e;
      };
    }, function (t, e, r) {
      r(313);
      var n = r(18);
      t.exports = n("Array").fill;
    }, function (t, e, r) {
      var n = r(0),
          i = r(314),
          o = r(78);
      n({
        target: "Array",
        proto: !0
      }, {
        fill: i
      }), o("fill");
    }, function (t, e, r) {

      var n = r(22),
          i = r(84),
          o = r(29);

      t.exports = function (t) {
        for (var e = n(this), r = o(e.length), a = arguments.length, s = i(a > 1 ? arguments[1] : void 0, r), u = a > 2 ? arguments[2] : void 0, f = void 0 === u ? r : i(u, r); f > s;) e[s++] = t;

        return e;
      };
    }, function (t, e, r) {

      var n = r(1)(r(97)),
          i = r(9).CramMalformedError,
          o = r(74),
          a = r(73);

      function s(t) {
        if (!t) throw new i("assertion failed");
      }

      t.exports = {
        readStatsO0: function (t, e, r) {
          var i = 0,
              u = 0,
              f = 255 & t.get();

          do {
            var c;
            null == e.fc[f] && (e.fc[f] = new a.FC()), e.fc[f].F = 255 & t.get(), e.fc[f].F >= 128 && (e.fc[f].F &= -129, e.fc[f].F = (127 & e.fc[f].F) << 8 | 255 & t.get()), e.fc[f].C = u, a.symbolInit(r[f], e.fc[f].C, e.fc[f].F), e.R || (e.R = new Array(o.TOTFREQ)), (0, n.default)(c = e.R).call(c, f, u, u + e.fc[f].F), u += e.fc[f].F, 0 === i && f + 1 === (255 & t.getByteAt(t.position())) ? (f = 255 & t.get(), i = 255 & t.get()) : 0 !== i ? (i -= 1, f += 1) : f = 255 & t.get();
          } while (0 !== f);

          s(u < o.TOTFREQ);
        },
        readStatsO1: function (t, e, r) {
          var i = 0,
              u = 255 & t.get();

          do {
            var f = 0,
                c = 0,
                l = 255 & t.get();
            null == e[u] && (e[u] = new a.AriDecoder());

            do {
              var h;
              null == e[u].fc[l] && (e[u].fc[l] = new a.FC()), e[u].fc[l].F = 255 & t.get(), e[u].fc[l].F >= 128 && (e[u].fc[l].F &= -129, e[u].fc[l].F = (127 & e[u].fc[l].F) << 8 | 255 & t.get()), e[u].fc[l].C = c, 0 === e[u].fc[l].F && (e[u].fc[l].F = o.TOTFREQ), null == r[u][l] && (r[u][l] = new a.RansDecSymbol()), a.symbolInit(r[u][l], e[u].fc[l].C, e[u].fc[l].F), null == e[u].R && (e[u].R = new Array(o.TOTFREQ)), (0, n.default)(h = e[u].R).call(h, l, c, c + e[u].fc[l].F), s((c += e[u].fc[l].F) <= o.TOTFREQ), 0 === f && l + 1 === (255 & t.getByteAt(t.position())) ? (l = 255 & t.get(), f = 255 & t.get()) : 0 !== f ? (f -= 1, l += 1) : l = 255 & t.get();
            } while (0 !== l);

            0 === i && u + 1 === (255 & t.getByteAt(t.position())) ? (u = 255 & t.get(), i = 255 & t.get()) : 0 !== i ? (i -= 1, u += 1) : u = 255 & t.get();
          } while (0 !== u);
        }
      };
    }, function (t, e, r) {

      var n = r(9).CramMalformedError,
          i = r(74),
          o = r(73);
      t.exports = {
        uncompress: function (t, e, r, a) {
          for (var s, u = t.getInt(), f = t.getInt(), c = t.getInt(), l = t.getInt(), h = a.remaining(), d = -4 & h, p = 0; p < d; p += 4) {
            var g = e.R[o.get(u, i.TF_SHIFT)],
                v = e.R[o.get(f, i.TF_SHIFT)],
                y = e.R[o.get(c, i.TF_SHIFT)],
                m = e.R[o.get(l, i.TF_SHIFT)];
            a.putAt(p, g), a.putAt(p + 1, v), a.putAt(p + 2, y), a.putAt(p + 3, m), u = o.advanceSymbolStep(u, r[255 & g], i.TF_SHIFT), f = o.advanceSymbolStep(f, r[255 & v], i.TF_SHIFT), c = o.advanceSymbolStep(c, r[255 & y], i.TF_SHIFT), l = o.advanceSymbolStep(l, r[255 & m], i.TF_SHIFT), u = o.renormalize(u, t), f = o.renormalize(f, t), c = o.renormalize(c, t), l = o.renormalize(l, t);
          }

          switch (a.setPosition(d), 3 & h) {
            case 0:
              break;

            case 1:
              s = e.R[o.get(u, i.TF_SHIFT)], o.advanceSymbol(u, t, r[255 & s], i.TF_SHIFT), a.put(s);
              break;

            case 2:
              s = e.R[o.get(u, i.TF_SHIFT)], o.advanceSymbol(u, t, r[255 & s], i.TF_SHIFT), a.put(s), s = e.R[o.get(f, i.TF_SHIFT)], o.advanceSymbol(f, t, r[255 & s], i.TF_SHIFT), a.put(s);
              break;

            case 3:
              s = e.R[o.get(u, i.TF_SHIFT)], o.advanceSymbol(u, t, r[255 & s], i.TF_SHIFT), a.put(s), s = e.R[o.get(f, i.TF_SHIFT)], o.advanceSymbol(f, t, r[255 & s], i.TF_SHIFT), a.put(s), s = e.R[o.get(c, i.TF_SHIFT)], o.advanceSymbol(c, t, r[255 & s], i.TF_SHIFT), a.put(s);
              break;

            default:
              throw new n("invalid output size encountered during rANS decoding");
          }

          a.setPosition(0);
        }
      };
    }, function (t, e, r) {

      var n = r(74),
          i = r(73);
      t.exports = {
        uncompress: function (t, e, r, o) {
          for (var a = e.remaining(), s = t.getInt(), u = t.getInt(), f = t.getInt(), c = t.getInt(), l = a >> 2, h = 0, d = l, p = 2 * l, g = 3 * l, v = 0, y = 0, m = 0, b = 0; h < l; h += 1, d += 1, p += 1, g += 1) {
            var _ = 255 & r[v].R[i.get(s, n.TF_SHIFT)],
                w = 255 & r[y].R[i.get(u, n.TF_SHIFT)],
                x = 255 & r[m].R[i.get(f, n.TF_SHIFT)],
                E = 255 & r[b].R[i.get(c, n.TF_SHIFT)];

            e.putAt(h, _), e.putAt(d, w), e.putAt(p, x), e.putAt(g, E), s = i.advanceSymbolStep(s, o[v][_], n.TF_SHIFT), u = i.advanceSymbolStep(u, o[y][w], n.TF_SHIFT), f = i.advanceSymbolStep(f, o[m][x], n.TF_SHIFT), c = i.advanceSymbolStep(c, o[b][E], n.TF_SHIFT), s = i.renormalize(s, t), u = i.renormalize(u, t), f = i.renormalize(f, t), c = i.renormalize(c, t), v = _, y = w, m = x, b = E;
          }

          for (; g < a; g += 1) {
            var S = 255 & r[b].R[i.get(c, n.TF_SHIFT)];
            e.putAt(g, S), c = i.advanceSymbol(c, t, o[b][S], n.TF_SHIFT), b = S;
          }
        }
      };
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(98)),
          o = n(r(16)),
          a = n(r(75)),
          s = r(326).Parser,
          u = new s().itf8(),
          f = {
        parser: new s().string("magic", {
          length: 4
        }).uint8("majorVersion").uint8("minorVersion").string("fileId", {
          length: 20,
          stripNull: !0
        }),
        maxLength: 26
      },
          c = {
        parser: new s().uint8("compressionMethod", {
          formatter: function (t) {
            var e = ["raw", "gzip", "bzip2", "lzma", "rans"][t];
            if (!e) throw new Error("compression method number ".concat(t, " not implemented"));
            return e;
          }
        }).uint8("contentType", {
          formatter: function (t) {
            var e = ["FILE_HEADER", "COMPRESSION_HEADER", "MAPPED_SLICE_HEADER", "UNMAPPED_SLICE_HEADER", "EXTERNAL_DATA", "CORE_DATA"][t];
            if (!e) throw new Error("invalid block content type id ".concat(t));
            return e;
          }
        }).itf8("contentId").itf8("compressedSize").itf8("uncompressedSize"),
        maxLength: 17
      },
          l = {
        parser: new s().uint32("crc32"),
        maxLength: 4
      },
          h = new s().itf8("size").buffer("ents", {
        length: "size",
        formatter: function (t) {
          function e(e, r) {
            for (var n = t.toString("utf8", e, r), i = [], o = 0; o < n.length; o += 3) i.push(n.substr(o, 3));

            return i;
          }

          var r,
              n = [],
              i = 0;

          for (r = 0; r < t.length; r += 1) t[r] || (n.push(e(i, r)), i = r + 1);

          return r > i && n.push(e(i, r)), n;
        }
      }),
          d = new s().uint8(null, {
        formatter: function (t) {
          return !!t;
        }
      }),
          p = new s().itf8("mapSize").itf8("mapCount").array("ents", {
        length: "mapCount",
        type: new s().string("key", {
          length: 2,
          stripNull: !1
        }).choice("value", {
          tag: "key",
          choices: {
            MI: d,
            UI: d,
            PI: d,
            RN: d,
            AP: d,
            RR: d,
            SM: new s().array(null, {
              type: "uint8",
              length: 5
            }),
            TD: new s().nest(null, {
              type: h,
              formatter: function (t) {
                return t.ents;
              }
            })
          }
        })
      });

      function g(t) {
        for (var e = {}, r = 0; r < t.ents.length; r += 1) {
          var n = t.ents[r],
              i = n.key,
              o = n.value;
          e[i] && console.warn("duplicate key ".concat(i, " in map")), e[i] = o;
        }

        return e;
      }

      var v = {
        cramFileDefinition: f,
        cramBlockHeader: c,
        cramBlockCrc32: l
      },
          y = {
        cramUnmappedSliceHeader: function (t) {
          var e = 0,
              r = new s().itf8("numRecords");
          e += 5, t >= 3 ? (r = r.ltf8("recordCounter"), e += 9) : 2 === t && (r = r.itf8("recordCounter"), e += 5), r = r.itf8("numBlocks").itf8("numContentIds").array("contentIds", {
            type: u,
            length: "numContentIds"
          }), e += 10, t >= 2 && (r = r.array("md5", {
            type: "uint8",
            length: 16
          }), e += 16);
          return {
            parser: r,
            maxLength: function (t) {
              return e + 5 * t;
            }
          };
        },
        cramMappedSliceHeader: function (t) {
          var e = new s().itf8("refSeqId").itf8("refSeqStart").itf8("refSeqSpan").itf8("numRecords"),
              r = 20;
          t >= 3 ? (e = e.ltf8("recordCounter"), r += 9) : 2 === t && (e = e.itf8("recordCounter"), r += 5), e = e.itf8("numBlocks").itf8("numContentIds").array("contentIds", {
            type: u,
            length: "numContentIds"
          }).itf8("refBaseBlockId"), r += 15, t >= 2 && (e = e.array("md5", {
            type: "uint8",
            length: 16
          }), r += 16);
          return {
            parser: e,
            maxLength: function (t) {
              return r + 5 * t;
            }
          };
        },
        cramEncoding: function (t) {
          return {
            parser: new s().namely("cramEncoding").itf8("codecId").itf8("parametersBytes").choice("parameters", {
              tag: "codecId",
              choices: {
                0: new s(),
                1: new s().itf8("blockContentId"),
                2: new s().itf8("offset").itf8("M"),
                3: s.start().itf8("numCodes").array("symbols", {
                  length: "numCodes",
                  type: u
                }).itf8("numLengths").array("bitLengths", {
                  length: "numLengths",
                  type: u
                }),
                4: s.start().nest("lengthsEncoding", {
                  type: "cramEncoding"
                }).nest("valuesEncoding", {
                  type: "cramEncoding"
                }),
                5: new s().uint8("stopByte")[t > 1 ? "itf8" : "int"]("blockContentId"),
                6: new s().itf8("offset").itf8("length"),
                7: new s().itf8("offset").itf8("K"),
                8: new s().itf8("offset").itf8("log2m"),
                9: new s().itf8("offset")
              }
            })
          };
        },
        cramDataSeriesEncodingMap: function (t) {
          return new s().itf8("mapSize").itf8("mapCount").array("ents", {
            length: "mapCount",
            type: new s().string("key", {
              length: 2,
              stripNull: !1
            }).nest("value", {
              type: this.cramEncoding(t).parser
            })
          });
        },
        cramTagEncodingMap: function (t) {
          return new s().itf8("mapSize").itf8("mapCount").array("ents", {
            length: "mapCount",
            type: new s().itf8("key", {
              formatter: function (t) {
                return String.fromCharCode(t >> 16 & 255) + String.fromCharCode(t >> 8 & 255) + String.fromCharCode(255 & t);
              }
            }).nest("value", {
              type: this.cramEncoding(t).parser
            })
          });
        },
        cramCompressionHeader: function (t) {
          var e = new s();
          return {
            parser: e = e.nest("preservation", {
              type: p,
              formatter: g
            }).nest("dataSeriesEncoding", {
              type: this.cramDataSeriesEncodingMap(t),
              formatter: g
            }).nest("tagEncoding", {
              type: this.cramTagEncodingMap(t),
              formatter: g
            })
          };
        },
        cramContainerHeader1: function (t) {
          var e = new s().int32("length").itf8("refSeqId").itf8("refSeqStart").itf8("alignmentSpan").itf8("numRecords"),
              r = 24;
          return t >= 3 ? (e = e.ltf8("recordCounter"), r += 9) : 2 === t && (e = e.itf8("recordCounter"), r += 5), t > 1 && (e = e.ltf8("numBases"), r += 9), {
            parser: e = e.itf8("numBlocks").itf8("numLandmarks"),
            maxLength: r += 10
          };
        },
        cramContainerHeader2: function (t) {
          var e = new s().itf8("numLandmarks").array("landmarks", {
            type: new s().itf8(),
            length: "numLandmarks"
          }),
              r = 0;
          return t >= 3 && (e = e.uint32("crc32"), r = 4), {
            parser: e,
            maxLength: function (t) {
              return 5 + 5 * t + r;
            }
          };
        }
      };
      t.exports = {
        cramFileDefinition: f,
        getSectionParsers: function (t) {
          var e,
              r = (0, a.default)({}, v);
          return (0, o.default)(e = (0, i.default)(y)).call(e, function (e) {
            r[e] = y[e](t);
          }), r;
        }
      };
    }, function (t, e, r) {
      t.exports = r(320);
    }, function (t, e, r) {
      r(321);
      var n = r(3);
      t.exports = n.Object.keys;
    }, function (t, e, r) {
      var n = r(0),
          i = r(22),
          o = r(51);
      n({
        target: "Object",
        stat: !0,
        forced: r(12)(function () {
          o(1);
        })
      }, {
        keys: function (t) {
          return o(i(t));
        }
      });
    }, function (t, e, r) {
      t.exports = r(323);
    }, function (t, e, r) {
      r(324);
      var n = r(3);
      t.exports = n.Object.assign;
    }, function (t, e, r) {
      var n = r(0),
          i = r(325);
      n({
        target: "Object",
        stat: !0,
        forced: Object.assign !== i
      }, {
        assign: i
      });
    }, function (t, e, r) {

      var n = r(17),
          i = r(12),
          o = r(51),
          a = r(138),
          s = r(65),
          u = r(22),
          f = r(60),
          c = Object.assign;
      t.exports = !c || i(function () {
        var t = {},
            e = {},
            r = Symbol();
        return t[r] = 7, "abcdefghijklmnopqrst".split("").forEach(function (t) {
          e[t] = t;
        }), 7 != c({}, t)[r] || "abcdefghijklmnopqrst" != o(c({}, e)).join("");
      }) ? function (t, e) {
        for (var r = u(t), i = arguments.length, c = 1, l = a.f, h = s.f; i > c;) for (var d, p = f(arguments[c++]), g = l ? o(p).concat(l(p)) : o(p), v = g.length, y = 0; v > y;) d = g[y++], n && !h.call(p, d) || (r[d] = p[d]);

        return r;
      } : c;
    }, function (t, e, r) {

      (function (t) {
        function n(t) {
          return (n = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (t) {
            return typeof t;
          } : function (t) {
            return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t;
          })(t);
        }

        var i = r(327),
            o = r(328).Context,
            a = r(146);
        "undefined" != typeof window && (window.Buffer = t), "undefined" != typeof self && (self.Buffer = t);
        var s = {
          UInt8: 1,
          UInt16LE: 2,
          UInt16BE: 2,
          UInt32LE: 4,
          UInt32BE: 4,
          Int8: 1,
          Int16LE: 2,
          Int16BE: 2,
          Int32LE: 4,
          Int32BE: 4,
          FloatLE: 4,
          FloatBE: 4,
          DoubleLE: 8,
          DoubleBE: 8,
          UInt64: 8,
          Int64: 8
        },
            u = {},
            f = [];
        !function () {
          var t;

          for (t = 1; t <= 32; t++) f.push(t);
        }();
        var c = {};
        Object.keys(s).concat(Object.keys({
          String: null,
          Buffer: null,
          Array: null,
          Skip: null,
          Choice: null,
          Nest: null,
          Bit: null,
          Itf8: null,
          Ltf8: null
        })).forEach(function (t) {
          c[t.toLowerCase()] = t;
        });

        var l = function () {
          this.varName = "", this.type = "", this.options = {}, this.next = null, this.head = null, this.compiled = null, this.endian = "le", this.constructorFn = null, this.alias = null;
        };

        l.start = function () {
          return new l();
        }, Object.keys(s).forEach(function (t) {
          l.prototype[t.toLowerCase()] = function (e, r) {
            return this.setNextParser(t.toLowerCase(), e, r);
          };

          var e = t.replace(/BE|LE/, "").toLowerCase();
          e in l.prototype || (l.prototype[e] = function (t, r) {
            return this[e + this.endian](t, r);
          });
        }), f.forEach(function (t) {
          l.prototype["bit".concat(t.toString())] = function (e, r) {
            return r || (r = {}), r.length = t, this.setNextParser("bit", e, r);
          };
        }), l.prototype.namely = function (t) {
          return u[t] = this, this.alias = t, this;
        }, l.prototype.skip = function (t, e) {
          if (e && e.assert) throw new Error("assert option on skip is not allowed.");
          return this.setNextParser("skip", "", {
            length: t
          });
        }, l.prototype.string = function (t, e) {
          if (!e.zeroTerminated && !e.length && !e.greedy) throw new Error("Neither length, zeroTerminated, nor greedy is defined for string.");
          if ((e.zeroTerminated || e.length) && e.greedy) throw new Error("greedy is mutually exclusive with length and zeroTerminated for string.");
          if (e.stripNull && !e.length && !e.greedy) throw new Error("Length or greedy must be defined if stripNull is defined.");
          return e.encoding = e.encoding || "utf8", this.setNextParser("string", t, e);
        }, l.prototype.buffer = function (t, e) {
          if (!e.length && !e.readUntil) throw new Error("Length nor readUntil is defined in buffer parser");
          return this.setNextParser("buffer", t, e);
        }, l.prototype.array = function (t, e) {
          if (!e.readUntil && !e.length && !e.lengthInBytes) throw new Error("Length option of array is not defined.");
          if (!e.type) throw new Error("Type option of array is not defined.");
          if ("string" == typeof e.type && !u[e.type] && Object.keys(s).indexOf(c[e.type]) < 0) throw new Error('Specified primitive type "'.concat(e.type, '" is not supported.'));
          return this.setNextParser("array", t, e);
        }, l.prototype.choice = function (t, e) {
          if (1 === arguments.length && "object" === n(t) && (e = t, t = null), !e.tag) throw new Error("Tag option of array is not defined.");
          if (!e.choices) throw new Error("Choices option of array is not defined.");
          return Object.keys(e.choices).forEach(function (r) {
            if (!e.choices[r]) throw new Error("Choice Case ".concat(r, " of ").concat(t, " is not valid."));
            if ("string" == typeof e.choices[r] && !u[e.choices[r]] && Object.keys(s).indexOf(c[e.choices[r]]) < 0) throw new Error('Specified primitive type "'.concat(e.choices[r], '" is not supported.'));
          }, this), this.setNextParser("choice", t, e);
        }, l.prototype.nest = function (t, e) {
          if (1 === arguments.length && "object" === n(t) && (e = t, t = null), !e.type) throw new Error("Type option of nest is not defined.");
          if (!(e.type instanceof l || u[e.type])) throw new Error("Type option of nest must be a Parser object.");
          if (!(e.type instanceof l || t)) throw new Error("options.type must be a object if variable name is omitted.");
          return this.setNextParser("nest", t, e);
        }, l.prototype.endianess = function (t) {
          switch (t.toLowerCase()) {
            case "little":
              this.endian = "le";
              break;

            case "big":
              this.endian = "be";
              break;

            default:
              throw new Error("Invalid endianess: ".concat(t));
          }

          return this;
        }, l.prototype.create = function (t) {
          if (!(t instanceof Function)) throw new Error("Constructor must be a Function object.");
          return this.constructorFn = t, this;
        }, l.prototype.getCode = function () {
          var t = new o();
          return t.pushCode("if (!Buffer.isBuffer(buffer)) {"), t.generateError('"argument buffer is not a Buffer object"'), t.pushCode("}"), this.alias ? this.addAliasedCode(t) : this.addRawCode(t), this.alias ? t.pushCode("return {0}(0)", "___parser_" + this.alias) : t.pushCode("return { offset: offset, result: vars };"), t.code;
        }, l.prototype.addRawCode = function (t) {
          t.pushCode("var offset = 0;"), this.constructorFn ? t.pushCode("var vars = new constructorFn();") : t.pushCode("var vars = {};"), this.generate(t), this.resolveReferences(t), t.pushCode("return { offset: offset, result: vars };");
        }, l.prototype.addAliasedCode = function (t) {
          return t.pushCode("function {0}(offset) {", "___parser_" + this.alias), this.constructorFn ? t.pushCode("var vars = new constructorFn();") : t.pushCode("var vars = {};"), this.generate(t), t.markResolved(this.alias), this.resolveReferences(t), t.pushCode("return { offset: offset, result: vars };"), t.pushCode("}"), t;
        }, l.prototype.resolveReferences = function (t) {
          var e = t.getUnresolvedReferences();
          t.markRequested(e), e.forEach(function (e) {
            u[e].addAliasedCode(t);
          });
        }, l.prototype.compile = function () {
          var t = "(function(buffer, constructorFn, Long) { ".concat(this.getCode(), " })");
          this.compiled = i.runInThisContext(t);
        }, l.prototype.sizeOf = function () {
          var t = NaN;
          if (Object.keys(s).indexOf(this.type) >= 0) t = s[this.type];else if ("String" === this.type && "number" == typeof this.options.length) t = this.options.length;else if ("Buffer" === this.type && "number" == typeof this.options.length) t = this.options.length;else if ("Array" === this.type && "number" == typeof this.options.length) {
            var e = NaN;
            "string" == typeof this.options.type ? e = s[c[this.options.type]] : this.options.type instanceof l && (e = this.options.type.sizeOf()), t = this.options.length * e;
          } else "Skip" === this.type ? t = this.options.length : "Nest" === this.type ? t = this.options.type.sizeOf() : this.type || (t = 0);
          return this.next && (t += this.next.sizeOf()), t;
        }, l.prototype.parse = function (t) {
          return this.compiled || this.compile(), this.compiled(t, this.constructorFn, a);
        }, l.prototype.setNextParser = function (t, e, r) {
          var n = new l();
          return n.type = c[t], n.varName = e, n.options = r || n.options, n.endian = this.endian, this.head ? this.head.next = n : this.next = n, this.head = n, this;
        }, l.prototype.generate = function (t) {
          this.type && (this["generate".concat(this.type)](t), this.generateAssert(t));
          var e = t.generateVariable(this.varName);
          return this.options.formatter && this.generateFormatter(t, e, this.options.formatter), this.generateNext(t);
        }, l.prototype.generateAssert = function (t) {
          if (this.options.assert) {
            var e = t.generateVariable(this.varName);

            switch (n(this.options.assert)) {
              case "function":
                t.pushCode("if (!({0}).call(vars, {1})) {", this.options.assert, e);
                break;

              case "number":
                t.pushCode("if ({0} !== {1}) {", this.options.assert, e);
                break;

              case "string":
                t.pushCode('if ("{0}" !== {1}) {', this.options.assert, e);
                break;

              default:
                throw new Error("Assert option supports only strings, numbers and assert functions.");
            }

            t.generateError('"Assert error: {0} is " + {0}', e), t.pushCode("}");
          }
        }, l.prototype.generateNext = function (t) {
          return this.next && (t = this.next.generate(t)), t;
        }, Object.keys(s).forEach(function (t) {
          l.prototype["generate".concat(t)] = function (e) {
            "UInt64" === t ? e.pushCode("{0} = Long.fromBytes(buffer.slice(offset,offset+8), true, this.endian === 'le').toNumber();", e.generateVariable(this.varName), t) : "Int64" === t ? e.pushCode("{0} = Long.fromBytes(buffer.slice(offset,offset+8), false, this.endian === 'le').toNumber();", e.generateVariable(this.varName), t) : e.pushCode("{0} = buffer.read{1}(offset);", e.generateVariable(this.varName), t), e.pushCode("offset += {0};", s[t]);
          };
        }), l.prototype.generateBit = function (t) {
          var e = JSON.parse(JSON.stringify(this));

          if (e.varName = t.generateVariable(e.varName), t.bitFields.push(e), !this.next || this.next && ["Bit", "Nest"].indexOf(this.next.type) < 0) {
            var r = 0;
            t.bitFields.forEach(function (t) {
              r += t.options.length;
            });
            var n = t.generateTmpVariable();
            if (r <= 8) t.pushCode("var {0} = buffer.readUInt8(offset);", n), r = 8;else if (r <= 16) t.pushCode("var {0} = buffer.readUInt16BE(offset);", n), r = 16;else if (r <= 24) {
              var i = t.generateTmpVariable(),
                  o = t.generateTmpVariable();
              t.pushCode("var {0} = buffer.readUInt16BE(offset);", i), t.pushCode("var {0} = buffer.readUInt8(offset + 2);", o), t.pushCode("var {2} = ({0} << 8) | {1};", i, o, n), r = 24;
            } else {
              if (!(r <= 32)) throw new Error("Currently, bit field sequence longer than 4-bytes is not supported.");
              t.pushCode("var {0} = buffer.readUInt32BE(offset);", n), r = 32;
            }
            t.pushCode("offset += {0};", r / 8);
            var a = 0,
                s = "be" === this.endian;
            t.bitFields.forEach(function (e) {
              t.pushCode("{0} = {1} >> {2} & {3};", e.varName, n, s ? r - a - e.options.length : a, (1 << e.options.length) - 1), a += e.options.length;
            }), t.bitFields = [];
          }
        }, l.prototype.generateSkip = function (t) {
          var e = t.generateOption(this.options.length);
          t.pushCode("offset += {0};", e);
        }, l.prototype.generateString = function (t) {
          var e = t.generateVariable(this.varName),
              r = t.generateTmpVariable();
          this.options.length && this.options.zeroTerminated ? (t.pushCode("var {0} = offset;", r), t.pushCode("while(buffer.readUInt8(offset++) !== 0 && offset - {0}  < {1});", r, this.options.length), t.pushCode("{0} = buffer.toString('{1}', {2}, offset - {2} < {3} ? offset - 1 : offset);", e, this.options.encoding, r, this.options.length)) : this.options.length ? (t.pushCode("{0} = buffer.toString('{1}', offset, offset + {2});", e, this.options.encoding, t.generateOption(this.options.length)), t.pushCode("offset += {0};", t.generateOption(this.options.length))) : this.options.zeroTerminated ? (t.pushCode("var {0} = offset;", r), t.pushCode("while(buffer.readUInt8(offset++) !== 0);"), t.pushCode("{0} = buffer.toString('{1}', {2}, offset - 1);", e, this.options.encoding, r)) : this.options.greedy && (t.pushCode("var {0} = offset;", r), t.pushCode("while(buffer.length > offset++);"), t.pushCode("{0} = buffer.toString('{1}', {2}, offset);", e, this.options.encoding, r)), this.options.stripNull && t.pushCode("{0} = {0}.replace(/\\x00+$/g, '')", e);
        }, l.prototype.generateBuffer = function (t) {
          "eof" === this.options.readUntil ? t.pushCode("{0} = buffer.slice(offset);", t.generateVariable(this.varName)) : (t.pushCode("{0} = buffer.slice(offset, offset + {1});", t.generateVariable(this.varName), t.generateOption(this.options.length)), t.pushCode("offset += {0};", t.generateOption(this.options.length))), this.options.clone && t.pushCode("{0} = Buffer.from({0});", t.generateVariable(this.varName));
        }, l.prototype.generateArray = function (t) {
          var e = t.generateOption(this.options.length),
              r = t.generateOption(this.options.lengthInBytes),
              n = this.options.type,
              i = t.generateTmpVariable(),
              o = t.generateVariable(this.varName),
              a = t.generateTmpVariable(),
              f = this.options.key,
              h = "string" == typeof f;
          if (h ? t.pushCode("{0} = {};", o) : t.pushCode("{0} = [];", o), "function" == typeof this.options.readUntil ? t.pushCode("do {") : "eof" === this.options.readUntil ? t.pushCode("for (var {0} = 0; offset < buffer.length; {0}++) {", i) : void 0 !== r ? t.pushCode("for (var {0} = offset; offset - {0} < {1}; ) {", i, r) : t.pushCode("for (var {0} = 0; {0} < {1}; {0}++) {", i, e), "string" == typeof n) {
            if (u[n]) {
              var d = t.generateTmpVariable();
              t.pushCode("var {0} = {1}(offset);", d, "___parser_" + n), t.pushCode("var {0} = {1}.result; offset = {1}.offset;", a, d), n !== this.alias && t.addReference(n);
            } else t.pushCode("var {0} = buffer.read{1}(offset);", a, c[n]), t.pushCode("offset += {0};", s[c[n]]);
          } else n instanceof l && (t.pushCode("var {0} = {};", a), t.pushScope(a), n.generate(t), t.popScope());
          h ? t.pushCode("{0}[{2}.{1}] = {2};", o, f, a) : t.pushCode("{0}.push({1});", o, a), t.pushCode("}"), "function" == typeof this.options.readUntil && t.pushCode(" while (!({0}).call(this, {1}, buffer.slice(offset)));", this.options.readUntil, a);
        }, l.prototype.generateChoiceCase = function (t, e, r) {
          if ("string" == typeof r) {
            if (u[r]) {
              var n = t.generateTmpVariable();
              t.pushCode("var {0} = {1}(offset);", n, "___parser_" + r), t.pushCode("{0} = {1}.result; offset = {1}.offset;", t.generateVariable(this.varName), n), r !== this.alias && t.addReference(r);
            } else t.pushCode("{0} = buffer.read{1}(offset);", t.generateVariable(this.varName), c[r]), t.pushCode("offset += {0};", s[c[r]]);
          } else r instanceof l && (t.pushPath(e), r.generate(t), t.popPath(e));
        }, l.prototype.generateChoice = function (t) {
          var e = t.generateOption(this.options.tag);
          this.varName && t.pushCode("{0} = {};", t.generateVariable(this.varName)), t.pushCode("switch({0}) {", e), Object.keys(this.options.choices).forEach(function (e) {
            var r = this.options.choices[e];
            Number.isNaN(parseInt(e, 10)) ? t.pushCode("case '{0}':", e) : t.pushCode("case {0}:", e), this.generateChoiceCase(t, this.varName, r), t.pushCode("break;");
          }, this), t.pushCode("default:"), this.options.defaultChoice ? this.generateChoiceCase(t, this.varName, this.options.defaultChoice) : t.generateError('"Met undefined tag value " + {0} + " at choice"', e), t.pushCode("}");
        }, l.prototype.generateNest = function (t) {
          var e = t.generateVariable(this.varName);
          if (this.options.type instanceof l) this.varName && t.pushCode("{0} = {};", e), t.pushPath(this.varName), this.options.type.generate(t), t.popPath(this.varName);else if (u[this.options.type]) {
            var r = t.generateTmpVariable();
            t.pushCode("var {0} = {1}(offset);", r, "___parser_" + this.options.type), t.pushCode("{0} = {1}.result; offset = {1}.offset;", e, r), this.options.type !== this.alias && t.addReference(this.options.type);
          }
        }, l.prototype.generateFormatter = function (t, e, r) {
          "function" == typeof r && t.pushCode("{0} = ({1}).call(this, {0});", e, r);
        }, l.prototype.isInteger = function () {
          return !!this.type.match(/U?Int[8|16|32][BE|LE]?|Bit\d+/);
        }, l.prototype.itf8 = function (t, e) {
          return this.setNextParser("itf8", t, e);
        }, l.prototype.itf8 = function (t, e) {
          return this.setNextParser("itf8", t, e);
        }, l.prototype.generateItf8 = function (t) {
          var e = t.generateVariable(this.varName),
              r = t.generateTmpVariable();
          t.pushCode("\n    var ".concat(r, " = buffer[offset];\n    if (").concat(r, " < 0x80) {\n      ").concat(e, " = ").concat(r, ";\n      offset += 1;\n    } else if (").concat(r, " < 0xc0) {\n      ").concat(e, " = ((").concat(r, "<<8) | buffer[offset+1]) & 0x3fff;\n      offset += 2;\n    } else if (").concat(r, " < 0xe0) {\n      ").concat(e, " = ((").concat(r, "<<16) | (buffer[offset+1]<< 8) |  buffer[offset+2]) & 0x1fffff;\n      offset += 3;\n    } else if (").concat(r, " < 0xf0) {\n      ").concat(e, " = ((").concat(r, "<<24) | (buffer[offset+1]<<16) | (buffer[offset+2]<<8) | buffer[offset+3]) & 0x0fffffff;\n      offset += 4\n    } else {\n      ").concat(e, " = ((").concat(r, " & 0x0f)<<28) | (buffer[offset+1]<<20) | (buffer[offset+2]<<12) | (buffer[offset+3]<<4) | (buffer[offset+4] & 0x0f);\n      // x=((0xff & 0x0f)<<28) | (0xff<<20) | (0xff<<12) | (0xff<<4) | (0x0f & 0x0f);\n      // TODO *val_p = uv < 0x80000000UL ? uv : -((int32_t) (0xffffffffUL - uv)) - 1;\n      offset += 5\n    }\n  "));
        }, l.prototype.ltf8 = function (t, e) {
          return this.setNextParser("ltf8", t, e);
        }, l.prototype.generateLtf8 = function (t) {
          var e = t.generateVariable(this.varName),
              r = t.generateTmpVariable();
          t.pushCode("\n  var ".concat(r, " = buffer[offset];\n  if (").concat(r, " < 0x80) {\n    ").concat(e, " = ").concat(r, ";\n    offset += 1;\n  } else if (").concat(r, " < 0xc0) {\n    ").concat(e, " = ((buffer[offset]<<8) | buffer[offset+1]) & 0x3fff;\n    offset += 2;\n  } else if (").concat(r, " < 0xe0) {\n    ").concat(e, " = ((buffer[offset]<<16) | (buffer[offset+1]<<8) | buffer[offset+2]) & 0x1fffff;\n    ").concat(e, " = (((").concat(r, " & 63) << 16) | buffer.readUInt16LE(offset + 1));\n    offset += 3;\n  } else if (").concat(r, " < 0xf0) {\n    ").concat(e, " = ((buffer[offset]<<24) | (buffer[offset+1]<<16) | (buffer[offset+2]<<8) | buffer[offset+3]) & 0x0fffffff;\n    offset += 4;\n  } else if (").concat(r, " < 0xf8) {\n    ").concat(e, " = (((buffer[offset] & 15) * Math.pow(2,32))) +\n      (buffer[offset+1]<<24) | (buffer[offset+2]<<16 | buffer[offset+3]<<8 | buffer[offset+4])\n    // TODO *val_p = uv < 0x80000000UL ? uv : -((int32_t) (0xffffffffUL - uv)) - 1;\n    offset += 5;\n  } else if (").concat(r, " < 0xfc) {\n    ").concat(e, " = ((((buffer[offset] & 7) << 8) | buffer[offset+1] )) * Math.pow(2,32) +\n      (buffer[offset+2]<<24) | (buffer[offset+3]<<16 | buffer[offset+4]<<8 | buffer[offset+5])\n    offset += 6;\n  } else if (").concat(r, " < 0xfe) {\n    ").concat(e, " = ((((buffer[offset] & 3) << 16) | buffer[offset+1]<<8 | buffer[offset+2])) * Math.pow(2,32) +\n      (buffer[offset+3]<<24) | (buffer[offset+4]<<16 | buffer[offset+5]<<8 | buffer[offset+6])\n    offset += 7;\n  } else if (").concat(r, " < 0xff) {\n    ").concat(e, " = Long.fromBytesBE(buffer.slice(offset+1,offset+8));\n    if (").concat(e, ".greaterThan(Number.MAX_SAFE_INTEGER) || ").concat(e, ".lessThan(Number.MIN_SAFE_INTEGER))\n      throw new Error('integer overflow')\n    ").concat(e, " = ").concat(e, ".toNumber()\n    offset += 8;\n  } else {\n    ").concat(e, " = Long.fromBytesBE(buffer.slice(offset+1,offset+9));\n    if (").concat(e, ".greaterThan(Number.MAX_SAFE_INTEGER) || ").concat(e, ".lessThan(Number.MIN_SAFE_INTEGER))\n      throw new Error('integer overflow')\n    ").concat(e, " = ").concat(e, ".toNumber()\n    offset += 9;\n  }\n  "));
        }, e.Parser = l;
      }).call(this, r(19).Buffer);
    }, function (module, exports) {
      var indexOf = function (t, e) {
        if (t.indexOf) return t.indexOf(e);

        for (var r = 0; r < t.length; r++) if (t[r] === e) return r;

        return -1;
      },
          Object_keys = function (t) {
        if (Object.keys) return Object.keys(t);
        var e = [];

        for (var r in t) e.push(r);

        return e;
      },
          forEach = function (t, e) {
        if (t.forEach) return t.forEach(e);

        for (var r = 0; r < t.length; r++) e(t[r], r, t);
      },
          defineProp = function () {
        try {
          return Object.defineProperty({}, "_", {}), function (t, e, r) {
            Object.defineProperty(t, e, {
              writable: !0,
              enumerable: !1,
              configurable: !0,
              value: r
            });
          };
        } catch (t) {
          return function (t, e, r) {
            t[e] = r;
          };
        }
      }(),
          globals = ["Array", "Boolean", "Date", "Error", "EvalError", "Function", "Infinity", "JSON", "Math", "NaN", "Number", "Object", "RangeError", "ReferenceError", "RegExp", "String", "SyntaxError", "TypeError", "URIError", "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "escape", "eval", "isFinite", "isNaN", "parseFloat", "parseInt", "undefined", "unescape"];

      function Context() {}

      Context.prototype = {};

      var Script = exports.Script = function (t) {
        if (!(this instanceof Script)) return new Script(t);
        this.code = t;
      };

      Script.prototype.runInContext = function (t) {
        if (!(t instanceof Context)) throw new TypeError("needs a 'context' argument.");
        var e = document.createElement("iframe");
        e.style || (e.style = {}), e.style.display = "none", document.body.appendChild(e);
        var r = e.contentWindow,
            n = r.eval,
            i = r.execScript;
        !n && i && (i.call(r, "null"), n = r.eval), forEach(Object_keys(t), function (e) {
          r[e] = t[e];
        }), forEach(globals, function (e) {
          t[e] && (r[e] = t[e]);
        });
        var o = Object_keys(r),
            a = n.call(r, this.code);
        return forEach(Object_keys(r), function (e) {
          (e in t || -1 === indexOf(o, e)) && (t[e] = r[e]);
        }), forEach(globals, function (e) {
          e in t || defineProp(t, e, r[e]);
        }), document.body.removeChild(e), a;
      }, Script.prototype.runInThisContext = function () {
        return eval2(this.code);
      }, Script.prototype.runInNewContext = function (t) {
        var e = Script.createContext(t),
            r = this.runInContext(e);
        return t && forEach(Object_keys(e), function (r) {
          t[r] = e[r];
        }), r;
      }, forEach(Object_keys(Script.prototype), function (t) {
        exports[t] = Script[t] = function (e) {
          var r = Script(e);
          return r[t].apply(r, [].slice.call(arguments, 1));
        };
      }), exports.isContext = function (t) {
        return t instanceof Context;
      }, exports.createScript = function (t) {
        return exports.Script(t);
      }, exports.createContext = Script.createContext = function (t) {
        var e = new Context();
        return "object" == typeof t && forEach(Object_keys(t), function (r) {
          e[r] = t[r];
        }), e;
      };
    }, function (t, e, r) {

      function n(t) {
        return (n = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (t) {
          return typeof t;
        } : function (t) {
          return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t;
        })(t);
      }

      var i = function () {
        this.code = "", this.scopes = [["vars"]], this.isAsync = !1, this.bitFields = [], this.tmpVariableCount = 0, this.references = {};
      };

      i.prototype.generateVariable = function (t) {
        var e = [];

        for (Array.prototype.push.apply(e, this.scopes[this.scopes.length - 1]); /^\$parent\./.test(t);) e.pop(), t = t.replace(/^\$parent\./, "");

        return t && e.push(t), e.join(".");
      }, i.prototype.generateOption = function (t) {
        switch (n(t)) {
          case "number":
            return t.toString();

          case "string":
            return this.generateVariable(t);

          case "function":
            return "(".concat(t, ").call(").concat(this.generateVariable(), ", vars)");

          default:
            return;
        }
      }, i.prototype.generateError = function () {
        var t = Array.prototype.slice.call(arguments),
            e = i.interpolate.apply(this, t);
        this.isAsync ? this.pushCode("return process.nextTick(function() { callback(new Error(".concat(e, "), vars); });")) : this.pushCode("throw new Error(".concat(e, ");"));
      }, i.prototype.generateTmpVariable = function () {
        return "$tmp".concat(this.tmpVariableCount++);
      }, i.prototype.pushCode = function () {
        var t = Array.prototype.slice.call(arguments);
        this.code += "".concat(i.interpolate.apply(this, t), "\n");
      }, i.prototype.pushPath = function (t) {
        t && this.scopes[this.scopes.length - 1].push(t);
      }, i.prototype.popPath = function (t) {
        t && this.scopes[this.scopes.length - 1].pop();
      }, i.prototype.pushScope = function (t) {
        this.scopes.push([t]);
      }, i.prototype.popScope = function () {
        this.scopes.pop();
      }, i.prototype.addReference = function (t) {
        this.references[t] || (this.references[t] = {
          resolved: !1,
          requested: !1
        });
      }, i.prototype.markResolved = function (t) {
        this.references[t].resolved = !0;
      }, i.prototype.markRequested = function (t) {
        t.forEach(function (t) {
          this.references[t].requested = !0;
        }.bind(this));
      }, i.prototype.getUnresolvedReferences = function () {
        var t = this.references;
        return Object.keys(this.references).filter(function (e) {
          return !t[e].resolved && !t[e].requested;
        });
      }, i.interpolate = function (t) {
        var e = /{\d+}/g,
            r = t.match(e),
            n = Array.prototype.slice.call(arguments, 1);
        return r && r.forEach(function (e) {
          var r = parseInt(e.substr(1, e.length - 2), 10);
          t = t.replace(e, n[r].toString());
        }), t;
      }, e.Context = i;
    }, function (t, e, r) {

      (function (e) {
        var n,
            i = r(1),
            o = i(r(16)),
            a = i(r(75)),
            s = i(r(37)),
            u = i(r(30)),
            f = i(r(31)),
            c = i(r(7)),
            l = i(r(10)),
            h = r(9).CramMalformedError,
            d = r(58),
            p = d.itf8Size,
            g = d.parseItem,
            v = d.tinyMemoize,
            y = r(334),
            m = r(390),
            b = function () {
          function t(e, r) {
            (0, c.default)(this, t), this.file = e, this.filePosition = r;
          }

          var r, n, i, o;
          return (0, l.default)(t, [{
            key: "getHeader",
            value: function () {
              return this._readContainerHeader(this.filePosition);
            }
          }, {
            key: "getCompressionHeaderBlock",
            value: (o = (0, f.default)(u.default.mark(function t() {
              var e, r, n;
              return u.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    return t.next = 2, this.getHeader();

                  case 2:
                    if (t.sent.numRecords) {
                      t.next = 5;
                      break;
                    }

                    return t.abrupt("return", null);

                  case 5:
                    return t.next = 7, this.file.getSectionParsers();

                  case 7:
                    return e = t.sent, t.next = 10, this.getFirstBlock();

                  case 10:
                    if ("COMPRESSION_HEADER" === (r = t.sent).contentType) {
                      t.next = 13;
                      break;
                    }

                    throw new h("invalid content type ".concat(r.contentType, " in what is supposed to be the compression header block"));

                  case 13:
                    return n = g(r.content, e.cramCompressionHeader.parser, 0, r.contentPosition), r.content = n, t.abrupt("return", r);

                  case 16:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function () {
              return o.apply(this, arguments);
            })
          }, {
            key: "getFirstBlock",
            value: (i = (0, f.default)(u.default.mark(function t() {
              var e;
              return u.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    return t.next = 2, this.getHeader();

                  case 2:
                    return e = t.sent, t.abrupt("return", this.file.readBlock(e._endPosition));

                  case 4:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function () {
              return i.apply(this, arguments);
            })
          }, {
            key: "getCompressionScheme",
            value: (n = (0, f.default)(u.default.mark(function t() {
              var e;
              return u.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    return t.next = 2, this.getCompressionHeaderBlock();

                  case 2:
                    if (e = t.sent) {
                      t.next = 5;
                      break;
                    }

                    return t.abrupt("return", void 0);

                  case 5:
                    return t.abrupt("return", new m(e.content));

                  case 6:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function () {
              return n.apply(this, arguments);
            })
          }, {
            key: "getSlice",
            value: function (t, e) {
              return new y(this, t, e);
            }
          }, {
            key: "_readContainerHeader",
            value: (r = (0, f.default)(u.default.mark(function t(r) {
              var n, i, o, f, c, l, h, d, v, y, m, b, _;

              return u.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    return t.next = 2, this.file.getSectionParsers();

                  case 2:
                    return n = t.sent, i = n.cramContainerHeader1, o = n.cramContainerHeader2, t.next = 6, this.file.stat();

                  case 6:
                    if (f = t.sent, c = f.size, !(r >= c)) {
                      t.next = 10;
                      break;
                    }

                    return t.abrupt("return", void 0);

                  case 10:
                    return l = e.allocUnsafe(i.maxLength), t.next = 13, this.file.read(l, 0, i.maxLength, r);

                  case 13:
                    if (h = g(l, i.parser), d = p(h.numLandmarks), !(r + h.length >= c)) {
                      t.next = 18;
                      break;
                    }

                    return console.warn((0, s.default)(v = (0, s.default)(y = "".concat(this.file, ": container header at ")).call(y, r, " indicates that the container has length ")).call(v, h.length, ", which extends beyond the length of the file. Skipping this container.")), t.abrupt("return", void 0);

                  case 18:
                    return m = e.allocUnsafe(o.maxLength(h.numLandmarks)), t.next = 21, this.file.read(m, 0, o.maxLength(h.numLandmarks), r + h._size - d);

                  case 21:
                    if (b = g(m, o.parser), !this.file.validateChecksums || void 0 === b.crc32) {
                      t.next = 25;
                      break;
                    }

                    return t.next = 25, this.file.checkCrc32(r, h._size + b._size - d - 4, b.crc32, "container header beginning at position ".concat(r));

                  case 25:
                    return _ = (0, a.default)(h, b, {
                      _size: h._size + b._size - d,
                      _endPosition: h._size + b._size - d + r
                    }), t.abrupt("return", _);

                  case 27:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function (t) {
              return r.apply(this, arguments);
            })
          }]), t;
        }();

        (0, o.default)(n = "getHeader getCompressionHeaderBlock getCompressionScheme".split(" ")).call(n, function (t) {
          return v(b, t);
        }), t.exports = b;
      }).call(this, r(19).Buffer);
    }, function (t, e, r) {
      t.exports = r(115);
    }, function (t, e, r) {
      var n, i, o, a, s;
      n = r(332), i = r(147).utf8, o = r(333), a = r(147).bin, (s = function (t, e) {
        t.constructor == String ? t = e && "binary" === e.encoding ? a.stringToBytes(t) : i.stringToBytes(t) : o(t) ? t = Array.prototype.slice.call(t, 0) : Array.isArray(t) || (t = t.toString());

        for (var r = n.bytesToWords(t), u = 8 * t.length, f = 1732584193, c = -271733879, l = -1732584194, h = 271733878, d = 0; d < r.length; d++) r[d] = 16711935 & (r[d] << 8 | r[d] >>> 24) | 4278255360 & (r[d] << 24 | r[d] >>> 8);

        r[u >>> 5] |= 128 << u % 32, r[14 + (u + 64 >>> 9 << 4)] = u;
        var p = s._ff,
            g = s._gg,
            v = s._hh,
            y = s._ii;

        for (d = 0; d < r.length; d += 16) {
          var m = f,
              b = c,
              _ = l,
              w = h;
          f = p(f, c, l, h, r[d + 0], 7, -680876936), h = p(h, f, c, l, r[d + 1], 12, -389564586), l = p(l, h, f, c, r[d + 2], 17, 606105819), c = p(c, l, h, f, r[d + 3], 22, -1044525330), f = p(f, c, l, h, r[d + 4], 7, -176418897), h = p(h, f, c, l, r[d + 5], 12, 1200080426), l = p(l, h, f, c, r[d + 6], 17, -1473231341), c = p(c, l, h, f, r[d + 7], 22, -45705983), f = p(f, c, l, h, r[d + 8], 7, 1770035416), h = p(h, f, c, l, r[d + 9], 12, -1958414417), l = p(l, h, f, c, r[d + 10], 17, -42063), c = p(c, l, h, f, r[d + 11], 22, -1990404162), f = p(f, c, l, h, r[d + 12], 7, 1804603682), h = p(h, f, c, l, r[d + 13], 12, -40341101), l = p(l, h, f, c, r[d + 14], 17, -1502002290), f = g(f, c = p(c, l, h, f, r[d + 15], 22, 1236535329), l, h, r[d + 1], 5, -165796510), h = g(h, f, c, l, r[d + 6], 9, -1069501632), l = g(l, h, f, c, r[d + 11], 14, 643717713), c = g(c, l, h, f, r[d + 0], 20, -373897302), f = g(f, c, l, h, r[d + 5], 5, -701558691), h = g(h, f, c, l, r[d + 10], 9, 38016083), l = g(l, h, f, c, r[d + 15], 14, -660478335), c = g(c, l, h, f, r[d + 4], 20, -405537848), f = g(f, c, l, h, r[d + 9], 5, 568446438), h = g(h, f, c, l, r[d + 14], 9, -1019803690), l = g(l, h, f, c, r[d + 3], 14, -187363961), c = g(c, l, h, f, r[d + 8], 20, 1163531501), f = g(f, c, l, h, r[d + 13], 5, -1444681467), h = g(h, f, c, l, r[d + 2], 9, -51403784), l = g(l, h, f, c, r[d + 7], 14, 1735328473), f = v(f, c = g(c, l, h, f, r[d + 12], 20, -1926607734), l, h, r[d + 5], 4, -378558), h = v(h, f, c, l, r[d + 8], 11, -2022574463), l = v(l, h, f, c, r[d + 11], 16, 1839030562), c = v(c, l, h, f, r[d + 14], 23, -35309556), f = v(f, c, l, h, r[d + 1], 4, -1530992060), h = v(h, f, c, l, r[d + 4], 11, 1272893353), l = v(l, h, f, c, r[d + 7], 16, -155497632), c = v(c, l, h, f, r[d + 10], 23, -1094730640), f = v(f, c, l, h, r[d + 13], 4, 681279174), h = v(h, f, c, l, r[d + 0], 11, -358537222), l = v(l, h, f, c, r[d + 3], 16, -722521979), c = v(c, l, h, f, r[d + 6], 23, 76029189), f = v(f, c, l, h, r[d + 9], 4, -640364487), h = v(h, f, c, l, r[d + 12], 11, -421815835), l = v(l, h, f, c, r[d + 15], 16, 530742520), f = y(f, c = v(c, l, h, f, r[d + 2], 23, -995338651), l, h, r[d + 0], 6, -198630844), h = y(h, f, c, l, r[d + 7], 10, 1126891415), l = y(l, h, f, c, r[d + 14], 15, -1416354905), c = y(c, l, h, f, r[d + 5], 21, -57434055), f = y(f, c, l, h, r[d + 12], 6, 1700485571), h = y(h, f, c, l, r[d + 3], 10, -1894986606), l = y(l, h, f, c, r[d + 10], 15, -1051523), c = y(c, l, h, f, r[d + 1], 21, -2054922799), f = y(f, c, l, h, r[d + 8], 6, 1873313359), h = y(h, f, c, l, r[d + 15], 10, -30611744), l = y(l, h, f, c, r[d + 6], 15, -1560198380), c = y(c, l, h, f, r[d + 13], 21, 1309151649), f = y(f, c, l, h, r[d + 4], 6, -145523070), h = y(h, f, c, l, r[d + 11], 10, -1120210379), l = y(l, h, f, c, r[d + 2], 15, 718787259), c = y(c, l, h, f, r[d + 9], 21, -343485551), f = f + m >>> 0, c = c + b >>> 0, l = l + _ >>> 0, h = h + w >>> 0;
        }

        return n.endian([f, c, l, h]);
      })._ff = function (t, e, r, n, i, o, a) {
        var s = t + (e & r | ~e & n) + (i >>> 0) + a;
        return (s << o | s >>> 32 - o) + e;
      }, s._gg = function (t, e, r, n, i, o, a) {
        var s = t + (e & n | r & ~n) + (i >>> 0) + a;
        return (s << o | s >>> 32 - o) + e;
      }, s._hh = function (t, e, r, n, i, o, a) {
        var s = t + (e ^ r ^ n) + (i >>> 0) + a;
        return (s << o | s >>> 32 - o) + e;
      }, s._ii = function (t, e, r, n, i, o, a) {
        var s = t + (r ^ (e | ~n)) + (i >>> 0) + a;
        return (s << o | s >>> 32 - o) + e;
      }, s._blocksize = 16, s._digestsize = 16, t.exports = function (t, e) {
        if (null == t) throw new Error("Illegal argument " + t);
        var r = n.wordsToBytes(s(t, e));
        return e && e.asBytes ? r : e && e.asString ? a.bytesToString(r) : n.bytesToHex(r);
      };
    }, function (t, e) {
      var r, n;
      r = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", n = {
        rotl: function (t, e) {
          return t << e | t >>> 32 - e;
        },
        rotr: function (t, e) {
          return t << 32 - e | t >>> e;
        },
        endian: function (t) {
          if (t.constructor == Number) return 16711935 & n.rotl(t, 8) | 4278255360 & n.rotl(t, 24);

          for (var e = 0; e < t.length; e++) t[e] = n.endian(t[e]);

          return t;
        },
        randomBytes: function (t) {
          for (var e = []; t > 0; t--) e.push(Math.floor(256 * Math.random()));

          return e;
        },
        bytesToWords: function (t) {
          for (var e = [], r = 0, n = 0; r < t.length; r++, n += 8) e[n >>> 5] |= t[r] << 24 - n % 32;

          return e;
        },
        wordsToBytes: function (t) {
          for (var e = [], r = 0; r < 32 * t.length; r += 8) e.push(t[r >>> 5] >>> 24 - r % 32 & 255);

          return e;
        },
        bytesToHex: function (t) {
          for (var e = [], r = 0; r < t.length; r++) e.push((t[r] >>> 4).toString(16)), e.push((15 & t[r]).toString(16));

          return e.join("");
        },
        hexToBytes: function (t) {
          for (var e = [], r = 0; r < t.length; r += 2) e.push(parseInt(t.substr(r, 2), 16));

          return e;
        },
        bytesToBase64: function (t) {
          for (var e = [], n = 0; n < t.length; n += 3) for (var i = t[n] << 16 | t[n + 1] << 8 | t[n + 2], o = 0; o < 4; o++) 8 * n + 6 * o <= 8 * t.length ? e.push(r.charAt(i >>> 6 * (3 - o) & 63)) : e.push("=");

          return e.join("");
        },
        base64ToBytes: function (t) {
          t = t.replace(/[^A-Z0-9+\/]/gi, "");

          for (var e = [], n = 0, i = 0; n < t.length; i = ++n % 4) 0 != i && e.push((r.indexOf(t.charAt(n - 1)) & Math.pow(2, -2 * i + 8) - 1) << 2 * i | r.indexOf(t.charAt(n)) >>> 6 - 2 * i);

          return e;
        }
      }, t.exports = n;
    }, function (t, e) {
      function r(t) {
        return !!t.constructor && "function" == typeof t.constructor.isBuffer && t.constructor.isBuffer(t);
      }
      /*!
       * Determine if an object is a Buffer
       *
       * @author   Feross Aboukhadijeh <https://feross.org>
       * @license  MIT
       */


      t.exports = function (t) {
        return null != t && (r(t) || function (t) {
          return "function" == typeof t.readFloatLE && "function" == typeof t.slice && r(t.slice(0, 0));
        }(t) || !!t._isBuffer);
      };
    }, function (t, e, r) {

      var n,
          i = r(1),
          o = i(r(148)),
          a = i(r(76)),
          s = i(r(150)),
          u = i(r(37)),
          f = i(r(30)),
          c = i(r(31)),
          l = i(r(7)),
          h = i(r(10)),
          d = i(r(99)),
          p = i(r(16)),
          g = i(r(77)),
          v = i(r(59)),
          y = r(9),
          m = y.CramMalformedError,
          b = y.CramBufferOverrunError,
          _ = y.CramArgumentError,
          w = r(58),
          x = w.parseItem,
          E = w.tinyMemoize,
          S = w.sequenceMD5,
          k = r(100),
          A = r(363);

      function C(t, e, r, n) {
        if (!n) throw new m("could not resolve intra-slice mate pairs, file seems truncated or malformed");
        var i = !!(n.mate || void 0 !== n.mateRecordNumber && n.mateRecordNumber !== e);
        r.readName || (r.readName = String(r.uniqueId), n.readName = r.readName), r.mate = {
          sequenceId: n.sequenceId,
          alignmentStart: n.alignmentStart,
          uniqueId: n.uniqueId
        }, n.readName && (r.mate.readName = n.readName), n.mate || void 0 !== n.mateRecordNumber || (n.mate = {
          sequenceId: r.sequenceId,
          alignmentStart: r.alignmentStart,
          uniqueId: r.uniqueId
        }, r.readName && (n.mate.readName = r.readName)), r.flags |= k.BAM_FPAIRED, (0, d.default)(n) & k.BAM_FUNMAP && (r.flags |= k.BAM_FMUNMAP), (0, d.default)(r) & k.BAM_FUNMAP && (n.flags |= k.BAM_FMUNMAP), (0, d.default)(n) & k.BAM_FREVERSE && (r.flags |= k.BAM_FMREVERSE), (0, d.default)(r) & k.BAM_FREVERSE && (n.flags |= k.BAM_FMREVERSE), void 0 === r.templateLength && (i ? function (t, e, r) {
          var n = function e(r) {
            var n = [r];

            if (r.mateRecordNumber >= 0) {
              var i = t[r.mateRecordNumber];
              if (!i) throw new m("intra-slice mate record not found, this file seems malformed");
              n.push.apply(n, (0, v.default)(e(i)));
            }

            return n;
          }(r),
              i = (0, g.default)(n).call(n, function (t) {
            return t.alignmentStart;
          }),
              o = (0, g.default)(n).call(n, function (t) {
            return t.alignmentStart + t.readLength - 1;
          }),
              a = Math.max.apply(Math, (0, v.default)(o)) - Math.min.apply(Math, (0, v.default)(i)) + 1;

          a >= 0 && (0, p.default)(n).call(n, function (t) {
            if (void 0 !== t.templateLength) throw new m("mate pair group has some members that have template lengths already, this file seems malformed");
            t.templateLength = a;
          });
        }(t, 0, r) : function (t, e) {
          var r = Math.min(t.alignmentStart, e.alignmentStart),
              n = Math.max(t.alignmentStart + t.readLength - 1, e.alignmentStart + e.readLength - 1) - r + 1;
          t.templateLength = n, e.templateLength = n;
        }(r, n)), delete r.mateRecordNumber;
      }

      var T = function () {
        function t(e, r) {
          (0, l.default)(this, t), this.container = e, this.file = e.file, this.containerPosition = r;
        }

        var e, r, n, i, d, v, y, w;
        return (0, h.default)(t, [{
          key: "getHeader",
          value: (w = (0, c.default)(f.default.mark(function t() {
            var e, r, n;
            return f.default.wrap(function (t) {
              for (;;) switch (t.prev = t.next) {
                case 0:
                  return t.next = 2, this.file.getSectionParsers();

                case 2:
                  return e = t.sent, t.next = 5, this.container.getHeader();

                case 5:
                  return r = t.sent, t.next = 8, this.file.readBlock(r._endPosition + this.containerPosition);

                case 8:
                  if ("MAPPED_SLICE_HEADER" !== (n = t.sent).contentType) {
                    t.next = 13;
                    break;
                  }

                  n.content = x(n.content, e.cramMappedSliceHeader.parser, 0, r._endPosition), t.next = 18;
                  break;

                case 13:
                  if ("UNMAPPED_SLICE_HEADER" !== n.contentType) {
                    t.next = 17;
                    break;
                  }

                  n.content = x(n.content, e.cramUnmappedSliceHeader.parser, 0, r._endPosition), t.next = 18;
                  break;

                case 17:
                  throw new m("error reading slice header block, invalid content type ".concat(n._contentType));

                case 18:
                  return t.abrupt("return", n);

                case 19:
                case "end":
                  return t.stop();
              }
            }, t, this);
          })), function () {
            return w.apply(this, arguments);
          })
        }, {
          key: "getBlocks",
          value: (y = (0, c.default)(f.default.mark(function t() {
            var e, r, n, i;
            return f.default.wrap(function (t) {
              for (;;) switch (t.prev = t.next) {
                case 0:
                  return t.next = 2, this.getHeader();

                case 2:
                  e = t.sent, r = e._endPosition, n = new Array(e.content.numBlocks), i = 0;

                case 6:
                  if (!(i < n.length)) {
                    t.next = 14;
                    break;
                  }

                  return t.next = 9, this.file.readBlock(r);

                case 9:
                  n[i] = t.sent, r = n[i]._endPosition;

                case 11:
                  i += 1, t.next = 6;
                  break;

                case 14:
                  return t.abrupt("return", n);

                case 15:
                case "end":
                  return t.stop();
              }
            }, t, this);
          })), function () {
            return y.apply(this, arguments);
          })
        }, {
          key: "getCoreDataBlock",
          value: (v = (0, c.default)(f.default.mark(function t() {
            var e;
            return f.default.wrap(function (t) {
              for (;;) switch (t.prev = t.next) {
                case 0:
                  return t.next = 2, this.getBlocks();

                case 2:
                  return e = t.sent, t.abrupt("return", e[0]);

                case 4:
                case "end":
                  return t.stop();
              }
            }, t, this);
          })), function () {
            return v.apply(this, arguments);
          })
        }, {
          key: "_getBlocksContentIdIndex",
          value: (d = (0, c.default)(f.default.mark(function t() {
            var e, r;
            return f.default.wrap(function (t) {
              for (;;) switch (t.prev = t.next) {
                case 0:
                  return t.next = 2, this.getBlocks();

                case 2:
                  return e = t.sent, r = {}, (0, p.default)(e).call(e, function (t) {
                    "EXTERNAL_DATA" === t.contentType && (r[t.contentId] = t);
                  }), t.abrupt("return", r);

                case 6:
                case "end":
                  return t.stop();
              }
            }, t, this);
          })), function () {
            return d.apply(this, arguments);
          })
        }, {
          key: "getBlockByContentId",
          value: (i = (0, c.default)(f.default.mark(function t(e) {
            var r;
            return f.default.wrap(function (t) {
              for (;;) switch (t.prev = t.next) {
                case 0:
                  return t.next = 2, this._getBlocksContentIdIndex();

                case 2:
                  return r = t.sent, t.abrupt("return", r[e]);

                case 4:
                case "end":
                  return t.stop();
              }
            }, t, this);
          })), function (t) {
            return i.apply(this, arguments);
          })
        }, {
          key: "getReferenceRegion",
          value: (n = (0, c.default)(f.default.mark(function t() {
            var e, r, n, i;
            return f.default.wrap(function (t) {
              for (;;) switch (t.prev = t.next) {
                case 0:
                  return t.next = 2, this.getHeader();

                case 2:
                  if (!((e = t.sent.content).refSeqId < 0)) {
                    t.next = 5;
                    break;
                  }

                  return t.abrupt("return", void 0);

                case 5:
                  return t.next = 7, this.container.getCompressionScheme();

                case 7:
                  if (r = t.sent, !(e.refBaseBlockId >= 0)) {
                    t.next = 15;
                    break;
                  }

                  if (n = this.getBlockByContentId(e.refBaseBlockId)) {
                    t.next = 12;
                    break;
                  }

                  throw new m("embedded reference specified, but reference block does not exist");

                case 12:
                  if (!(e.span > n.uncompressedSize)) {
                    t.next = 14;
                    break;
                  }

                  throw new m("Embedded reference is too small");

                case 14:
                  return t.abrupt("return", {
                    seq: n.data.toString("utf8"),
                    start: e.refSeqStart,
                    end: e.refSeqStart + e.refSeqSpan - 1,
                    span: e.refSeqSpan
                  });

                case 15:
                  if (!r.referenceRequired && !this.file.fetchReferenceSequenceCallback) {
                    t.next = 24;
                    break;
                  }

                  if (this.file.fetchReferenceSequenceCallback) {
                    t.next = 18;
                    break;
                  }

                  throw new Error("reference sequence not embedded, and seqFetch callback not provided, cannot fetch reference sequence");

                case 18:
                  return t.next = 20, this.file.fetchReferenceSequenceCallback(e.refSeqId, e.refSeqStart, e.refSeqStart + e.refSeqSpan - 1);

                case 20:
                  if ((i = t.sent).length === e.refSeqSpan) {
                    t.next = 23;
                    break;
                  }

                  throw new _("seqFetch callback returned a reference sequence of the wrong length");

                case 23:
                  return t.abrupt("return", {
                    seq: i,
                    start: e.refSeqStart,
                    end: e.refSeqStart + e.refSeqSpan - 1,
                    span: e.refSeqSpan
                  });

                case 24:
                  return t.abrupt("return", void 0);

                case 25:
                case "end":
                  return t.stop();
              }
            }, t, this);
          })), function () {
            return n.apply(this, arguments);
          })
        }, {
          key: "getAllRecords",
          value: function () {
            return this.getRecords(function () {
              return !0;
            });
          }
        }, {
          key: "_fetchRecords",
          value: (r = (0, c.default)(f.default.mark(function t() {
            var e,
                r,
                n,
                i,
                o,
                a,
                c,
                l,
                h,
                d,
                p,
                v,
                y,
                _,
                w,
                x,
                E,
                k,
                T,
                R,
                O,
                I,
                L,
                B = this;

            return f.default.wrap(function (t) {
              for (;;) switch (t.prev = t.next) {
                case 0:
                  return t.next = 2, this.file.getDefinition();

                case 2:
                  return e = t.sent, r = e.majorVersion, t.next = 6, this.container.getCompressionScheme();

                case 6:
                  return n = t.sent, t.next = 9, this.getHeader();

                case 9:
                  return i = t.sent, t.next = 12, this._getBlocksContentIdIndex();

                case 12:
                  if (o = t.sent, !(r > 1 && this.file.options.checkSequenceMD5 && i.content.refSeqId >= 0 && "0000000000000000" !== i.content.md5.join(""))) {
                    t.next = 23;
                    break;
                  }

                  return t.next = 16, this.getReferenceRegion();

                case 16:
                  if (!(a = t.sent)) {
                    t.next = 23;
                    break;
                  }

                  if (v = a.seq, y = a.start, _ = a.end, w = S(v), x = (0, g.default)(c = i.content.md5).call(c, function (t) {
                    return (t < 16 ? "0" : "") + t.toString(16);
                  }).join(""), w === x) {
                    t.next = 23;
                    break;
                  }

                  throw new m((0, u.default)(l = (0, u.default)(h = (0, u.default)(d = (0, u.default)(p = "MD5 checksum reference mismatch for ref ".concat(i.content.refSeqId, " pos ")).call(p, y, "..")).call(d, _, ". recorded MD5: ")).call(h, x, ", calculated MD5: ")).call(l, w));

                case 23:
                  return t.next = 25, this.getCoreDataBlock();

                case 25:
                  E = t.sent, k = {
                    lastAlignmentStart: i.content.refSeqStart || 0,
                    coreBlock: {
                      bitPosition: 7,
                      bytePosition: 0
                    },
                    externalBlocks: {
                      getCursor: function (t) {
                        return this[t] || (this[t] = {
                          bitPosition: 7,
                          bytePosition: 0
                        }), this[t];
                      }
                    }
                  }, T = function (t) {
                    var e = n.getCodecForDataSeries(t);
                    if (!e) throw new m("no codec defined for ".concat(t, " data series"));
                    return e.decode(B, E, o, k);
                  }, R = new Array(i.content.numRecords), O = 0;

                case 30:
                  if (!(O < R.length)) {
                    t.next = 48;
                    break;
                  }

                  t.prev = 31, R[O] = A(this, T, n, i, E, o, k, r, O), R[O].uniqueId = i.contentPosition + i.content.recordCounter + O + 1, t.next = 45;
                  break;

                case 36:
                  if (t.prev = 36, t.t0 = t.catch(31), !(t.t0 instanceof b)) {
                    t.next = 44;
                    break;
                  }

                  return console.warn("read attempted beyond end of buffer, file seems truncated."), R = (0, s.default)(R).call(R, function (t) {
                    return !!t;
                  }), t.abrupt("break", 48);

                case 44:
                  throw t.t0;

                case 45:
                  O += 1, t.next = 30;
                  break;

                case 48:
                  for (I = 0; I < R.length; I += 1) (L = R[I].mateRecordNumber) >= 0 && C(R, I, R[I], R[L]);

                  return t.abrupt("return", R);

                case 50:
                case "end":
                  return t.stop();
              }
            }, t, this, [[31, 36]]);
          })), function () {
            return r.apply(this, arguments);
          })
        }, {
          key: "getRecords",
          value: (e = (0, c.default)(f.default.mark(function t(e) {
            var r,
                n,
                i,
                u,
                l,
                h,
                d,
                p,
                v,
                y,
                m,
                b,
                _,
                w,
                x,
                E,
                S = this;

            return f.default.wrap(function (t) {
              for (;;) switch (t.prev = t.next) {
                case 0:
                  return n = this.container.filePosition + this.containerPosition, (i = this.file.featureCache.get(n)) || (i = this._fetchRecords(), this.file.featureCache.set(n, i)), t.t0 = s.default, t.next = 6, i;

                case 6:
                  if (t.t1 = r = t.sent, t.t2 = r, t.t3 = e, !(u = (0, t.t0)(t.t1).call(t.t2, t.t3)).length || !this.file.fetchReferenceSequenceCallback) {
                    t.next = 24;
                    break;
                  }

                  return t.next = 13, this.getHeader();

                case 13:
                  if (!((l = t.sent).content.refSeqId >= 0 || -2 === l.content.refSeqId)) {
                    t.next = 24;
                    break;
                  }

                  return d = l.content.refSeqId >= 0 ? l.content.refSeqId : void 0, t.next = 18, this.container.getCompressionScheme();

                case 18:
                  for (p = t.sent, v = {}, y = 0; y < u.length; y += 1) m = void 0 !== d ? d : u[y].sequenceId, (b = v[m]) || (b = {
                    id: m,
                    start: u[y].alignmentStart,
                    end: -1 / 0
                  }, v[m] = b), (_ = u[y].alignmentStart + (u[y].lengthOnRef || u[y].readLength) - 1) > b.end && (b.end = _), u[y].alignmentStart < b.start && (b.start = u[y].alignmentStart);

                  return t.next = 23, a.default.all((0, g.default)(h = (0, o.default)(v)).call(h, function () {
                    var t = (0, c.default)(f.default.mark(function t(e) {
                      return f.default.wrap(function (t) {
                        for (;;) switch (t.prev = t.next) {
                          case 0:
                            if (!(-1 !== e.id && e.start <= e.end)) {
                              t.next = 4;
                              break;
                            }

                            return t.next = 3, S.file.fetchReferenceSequenceCallback(e.id, e.start, e.end);

                          case 3:
                            e.seq = t.sent;

                          case 4:
                          case "end":
                            return t.stop();
                        }
                      }, t);
                    }));
                    return function (e) {
                      return t.apply(this, arguments);
                    };
                  }()));

                case 23:
                  for (w = 0; w < u.length; w += 1) x = void 0 !== d ? d : u[w].sequenceId, (E = v[x]) && E.seq && u[w].addReferenceSequence(E, p);

                case 24:
                  return t.abrupt("return", u);

                case 25:
                case "end":
                  return t.stop();
              }
            }, t, this);
          })), function (t) {
            return e.apply(this, arguments);
          })
        }]), t;
      }();

      (0, p.default)(n = "getHeader getBlocks _getBlocksContentIdIndex".split(" ")).call(n, function (t) {
        return E(T, t);
      }), t.exports = T;
    }, function (t, e, r) {
      t.exports = r(336);
    }, function (t, e, r) {
      r(337);
      var n = r(3);
      t.exports = n.Object.values;
    }, function (t, e, r) {
      var n = r(0),
          i = r(149).values;
      n({
        target: "Object",
        stat: !0
      }, {
        values: function (t) {
          return i(t);
        }
      });
    }, function (t, e, r) {
      t.exports = r(339);
    }, function (t, e, r) {
      var n = r(340),
          i = Array.prototype;

      t.exports = function (t) {
        var e = t.filter;
        return t === i || t instanceof Array && e === i.filter ? n : e;
      };
    }, function (t, e, r) {
      r(341);
      var n = r(18);
      t.exports = n("Array").filter;
    }, function (t, e, r) {

      var n = r(0),
          i = r(45).filter;
      n({
        target: "Array",
        proto: !0,
        forced: !r(68)("filter")
      }, {
        filter: function (t) {
          return i(this, t, arguments.length > 1 ? arguments[1] : void 0);
        }
      });
    }, function (t, e, r) {
      t.exports = r(343);
    }, function (t, e, r) {
      var n = r(344),
          i = RegExp.prototype;

      t.exports = function (t) {
        return !(t === i || t instanceof RegExp) || "flags" in t ? t.flags : n(t);
      };
    }, function (t, e, r) {
      r(345);
      var n = r(346);

      t.exports = function (t) {
        return n.call(t);
      };
    }, function (t, e) {}, function (t, e, r) {

      var n = r(2);

      t.exports = function () {
        var t = n(this),
            e = "";
        return t.global && (e += "g"), t.ignoreCase && (e += "i"), t.multiline && (e += "m"), t.dotAll && (e += "s"), t.unicode && (e += "u"), t.sticky && (e += "y"), e;
      };
    }, function (t, e, r) {
      t.exports = r(348);
    }, function (t, e, r) {
      var n = r(349),
          i = Array.prototype;

      t.exports = function (t) {
        var e = t.map;
        return t === i || t instanceof Array && e === i.map ? n : e;
      };
    }, function (t, e, r) {
      r(350);
      var n = r(18);
      t.exports = n("Array").map;
    }, function (t, e, r) {

      var n = r(0),
          i = r(45).map;
      n({
        target: "Array",
        proto: !0,
        forced: !r(68)("map")
      }, {
        map: function (t) {
          return i(this, t, arguments.length > 1 ? arguments[1] : void 0);
        }
      });
    }, function (t, e, r) {
      var n = r(151);

      t.exports = function (t) {
        if (n(t)) {
          for (var e = 0, r = new Array(t.length); e < t.length; e++) r[e] = t[e];

          return r;
        }
      };
    }, function (t, e, r) {
      t.exports = r(353);
    }, function (t, e, r) {
      r(354);
      var n = r(3);
      t.exports = n.Array.isArray;
    }, function (t, e, r) {
      r(0)({
        target: "Array",
        stat: !0
      }, {
        isArray: r(53)
      });
    }, function (t, e, r) {
      t.exports = r(356);
    }, function (t, e, r) {
      t.exports = r(357);
    }, function (t, e, r) {
      r(46), r(358);
      var n = r(3);
      t.exports = n.Array.from;
    }, function (t, e, r) {
      var n = r(0),
          i = r(359);
      n({
        target: "Array",
        stat: !0,
        forced: !r(121)(function (t) {
          Array.from(t);
        })
      }, {
        from: i
      });
    }, function (t, e, r) {

      var n = r(15),
          i = r(22),
          o = r(120),
          a = r(119),
          s = r(29),
          u = r(87),
          f = r(90);

      t.exports = function (t) {
        var e,
            r,
            c,
            l,
            h = i(t),
            d = "function" == typeof this ? this : Array,
            p = arguments.length,
            g = p > 1 ? arguments[1] : void 0,
            v = void 0 !== g,
            y = 0,
            m = f(h);
        if (v && (g = n(g, p > 2 ? arguments[2] : void 0, 2)), null == m || d == Array && a(m)) for (r = new d(e = s(h.length)); e > y; y++) u(r, y, v ? g(h[y], y) : h[y]);else for (l = m.call(h), r = new d(); !(c = l.next()).done; y++) u(r, y, v ? o(l, g, [c.value, y], !0) : c.value);
        return r.length = y, r;
      };
    }, function (t, e, r) {
      r(41), r(46), t.exports = r(361);
    }, function (t, e, r) {
      var n = r(67),
          i = r(6),
          o = r(35),
          a = i("iterator");

      t.exports = function (t) {
        var e = Object(t);
        return void 0 !== e[a] || "@@iterator" in e || o.hasOwnProperty(n(e));
      };
    }, function (t, e) {
      t.exports = function () {
        throw new TypeError("Invalid attempt to spread non-iterable instance");
      };
    }, function (t, e, r) {

      (function (e) {
        var n = r(1),
            i = n(r(37)),
            o = n(r(364)),
            a = n(r(16)),
            s = n(r(99)),
            u = n(r(101)),
            f = n(r(59)),
            c = n(r(154)),
            l = n(r(376)),
            h = n(r(380)),
            d = n(r(48)),
            p = n(r(387)),
            g = r(146),
            v = r(9),
            y = v.CramMalformedError,
            m = v.CramUnimplementedError,
            b = r(389),
            _ = r(100);

        function w(t) {
          var e = (0, p.default)(t).call(t, 0);
          return -1 === e ? t.toString("utf8") : t.toString("utf8", 0, e);
        }

        function x(t, r) {
          if (r.readInt32LE || (r = e.from(r)), "Z" === t) return w(r);
          if ("A" === t) return String.fromCharCode(r[0]);

          if ("I" === t) {
            var n = g.fromBytesLE(r);
            if (n.greaterThan(h.default) || n.lessThan(l.default)) throw new m("integer overflow");
            return n.toNumber();
          }

          if ("i" === t) return r.readInt32LE(0);
          if ("s" === t) return r.readInt16LE(0);
          if ("S" === t) return r.readUInt16LE(0);
          if ("c" === t) return r.readInt8(0);
          if ("C" === t) return r.readUInt8(0);
          if ("f" === t) return r.readFloatLE(0);

          if ("H" === t) {
            var i = w(r);
            return (0, c.default)(i.replace(/^0x/, ""), 16);
          }

          if ("B" === t) return function (t) {
            var e = String.fromCharCode(t[0]),
                r = t.readInt32LE(1),
                n = {
              c: ["readInt8", 1],
              C: ["readUInt8", 1],
              s: ["readInt16LE", 2],
              S: ["readUInt16LE", 2],
              i: ["readInt32LE", 4],
              I: ["readUInt32LE", 4],
              f: ["readFloatLE", 4]
            }[e];
            if (!n) throw new y("invalid tag value array type '".concat(e, "'"));

            for (var i = (0, d.default)(n, 2), o = i[0], a = i[1], s = new Array(r), u = 5, f = 0; f < r; f += 1) s[f] = t[o](u), u += a;

            return s;
          }(r);
          throw new y("Unrecognized tag type ".concat(t));
        }

        function E(t) {
          if (t instanceof e) return w(t);

          if (t.length && (0, p.default)(t)) {
            if (!t[t.length - 1]) {
              var r = (0, p.default)(t).call(t, 0);
              return String.fromCharCode.apply(String, (0, f.default)((0, u.default)(t).call(t, 0, r)));
            }

            return String.fromCharCode.apply(String, (0, f.default)(t));
          }

          return String(t);
        }

        t.exports = function (t, e, r, n, u, f, c, l, h) {
          var p = new b();

          if (p.flags = e("BF"), p.cramFlags = e("CF"), l > 1 && -2 === n.content.refSeqId ? p.sequenceId = e("RI") : p.sequenceId = n.content.refSeqId, p.readLength = e("RL"), p.alignmentStart = e("AP"), r.APdelta && (p.alignmentStart += c.lastAlignmentStart), c.lastAlignmentStart = p.alignmentStart, p.readGroupId = e("RG"), r.readNamesIncluded && (p.readName = E(e("RN"))), p.isDetached()) {
            var g = {};
            g.flags = e("MF"), r.readNamesIncluded || (g.readName = E(e("RN")), p.readName = g.readName), g.sequenceId = e("NS"), g.alignmentStart = e("NP"), ((0, s.default)(g) || g.sequenceId > -1) && (p.mate = g), p.templateSize = e("TS"), (0, s.default)(g) & _.CRAM_M_UNMAP && (p.flags |= _.BAM_FMUNMAP), (0, s.default)(g) & _.CRAM_M_REVERSE && (p.flags |= _.BAM_FMREVERSE);
          } else p.hasMateDownStream() && (p.mateRecordNumber = e("NF") + h + 1);

          var v = e("TL");
          if (v < 0) throw new y("invalid TL index");

          for (var m = r.getTagNames(v), w = m.length, S = 0; S < w; S += 1) {
            var k = m[S],
                A = k.substr(0, 2),
                C = k.substr(2, 1),
                T = r.getCodecForTag(k);
            if (!T) throw new y("no codec defined for auxiliary tag ".concat(k));
            var R = T.decode(t, u, f, c);
            p.tags[A] = x(C, R);
          }

          if (p.isSegmentUnmapped()) {
            if (p.isUnknownBases()) p.readBases = null, p.qualityScores = null;else {
              for (var O = new Array(p.readLength), I = 0; I < O.length; I += 1) O[I] = e("BA");

              if (p.readBases = String.fromCharCode.apply(String, O), p.isPreservingQualityScores()) {
                for (var L = 0; L < O.length; L += 1) O[L] = e("QS");

                p.qualityScores = O;
              }
            }
          } else {
            var B,
                P = e("FN");
            P && (p.readFeatures = function (t, e, r, n, i) {
              var o = 0,
                  a = t.alignmentStart - 1,
                  s = new Array(e);

              function u(t) {
                var e = (0, d.default)(t, 2),
                    n = e[0],
                    i = e[1],
                    o = r(i);
                return "character" === n ? String.fromCharCode(o) : "string" === n ? o.toString("utf8") : "numArray" === n ? o.toArray() : o;
              }

              for (var f = 0; f < e; f += 1) {
                var c = String.fromCharCode(r("FC")),
                    l = r("FP"),
                    h = {
                  code: c
                },
                    p = {
                  B: ["character", "BA"],
                  S: ["string", i > 1 ? "SC" : "IN"],
                  X: ["number", "BS"],
                  D: ["number", "DL"],
                  I: ["string", "IN"],
                  i: ["character", "BA"],
                  b: ["string", "BB"],
                  q: ["numArray", "QQ"],
                  Q: ["number", "QS"],
                  H: ["number", "HC"],
                  P: ["number", "PD"],
                  N: ["number", "RS"]
                }[c];
                if (!p) throw new y('invalid read feature code "'.concat(c, '"'));
                h.data = u(p);
                var g = {
                  B: ["number", "QS"]
                }[c];
                g && (h.data = [h.data, u(g)]), o += l, h.pos = o, a += l, h.refPos = a, "D" === c || "N" === c ? a += h.data : "I" === c || "S" === c ? a -= h.data.length : "i" === c && (a -= 1), s[f] = h;
              }

              return s;
            }(p, P, e, 0, l));
            var F,
                N = p.readLength;
            if (p.readFeatures && (0, a.default)(B = p.readFeatures).call(B, function (t) {
              var e = t.code,
                  r = t.data;
              "D" === e || "N" === e ? N += r : "I" === e || "S" === e ? N -= r.length : "i" === e && (N -= 1);
            }), (0, o.default)(N)) console.warn("".concat(p.readName || (0, i.default)(F = "".concat(p.sequenceId, ":")).call(F, p.alignmentStart), " record has invalid read features")), N = p.readLength;

            if (p.lengthOnRef = N, p.mappingQuality = e("MQ"), p.isPreservingQualityScores()) {
              for (var M = new Array(p.readLength), j = 0; j < M.length; j += 1) M[j] = e("QS");

              p.qualityScores = M;
            }
          }
          return p;
        };
      }).call(this, r(19).Buffer);
    }, function (t, e, r) {
      t.exports = r(365);
    }, function (t, e, r) {
      t.exports = r(366);
    }, function (t, e, r) {
      r(367);
      var n = r(3);
      t.exports = n.Number.isNaN;
    }, function (t, e, r) {
      r(0)({
        target: "Number",
        stat: !0
      }, {
        isNaN: function (t) {
          return t != t;
        }
      });
    }, function (t, e, r) {
      t.exports = r(369);
    }, function (t, e, r) {
      var n = r(370),
          i = Array.prototype;

      t.exports = function (t) {
        var e = t.slice;
        return t === i || t instanceof Array && e === i.slice ? n : e;
      };
    }, function (t, e, r) {
      r(371);
      var n = r(18);
      t.exports = n("Array").slice;
    }, function (t, e, r) {

      var n = r(0),
          i = r(14),
          o = r(53),
          a = r(84),
          s = r(29),
          u = r(28),
          f = r(87),
          c = r(68),
          l = r(6)("species"),
          h = [].slice,
          d = Math.max;
      n({
        target: "Array",
        proto: !0,
        forced: !c("slice")
      }, {
        slice: function (t, e) {
          var r,
              n,
              c,
              p = u(this),
              g = s(p.length),
              v = a(t, g),
              y = a(void 0 === e ? g : e, g);
          if (o(p) && ("function" != typeof (r = p.constructor) || r !== Array && !o(r.prototype) ? i(r) && null === (r = r[l]) && (r = void 0) : r = void 0, r === Array || void 0 === r)) return h.call(p, v, y);

          for (n = new (void 0 === r ? Array : r)(d(y - v, 0)), c = 0; v < y; v++, c++) v in p && f(n, c, p[v]);

          return n.length = c, n;
        }
      });
    }, function (t, e, r) {
      t.exports = r(373);
    }, function (t, e, r) {
      r(374);
      var n = r(3);
      t.exports = n.Number.parseInt;
    }, function (t, e, r) {
      var n = r(0),
          i = r(155);
      n({
        target: "Number",
        stat: !0,
        forced: Number.parseInt != i
      }, {
        parseInt: i
      });
    }, function (t, e, r) {
      var n = r(49),
          i = "[" + r(156) + "]",
          o = RegExp("^" + i + i + "*"),
          a = RegExp(i + i + "*$"),
          s = function (t) {
        return function (e) {
          var r = String(n(e));
          return 1 & t && (r = r.replace(o, "")), 2 & t && (r = r.replace(a, "")), r;
        };
      };

      t.exports = {
        start: s(1),
        end: s(2),
        trim: s(3)
      };
    }, function (t, e, r) {
      t.exports = r(377);
    }, function (t, e, r) {
      t.exports = r(378);
    }, function (t, e, r) {
      r(379), t.exports = -9007199254740991;
    }, function (t, e, r) {
      r(0)({
        target: "Number",
        stat: !0
      }, {
        MIN_SAFE_INTEGER: -9007199254740991
      });
    }, function (t, e, r) {
      t.exports = r(381);
    }, function (t, e, r) {
      t.exports = r(382);
    }, function (t, e, r) {
      r(383), t.exports = 9007199254740991;
    }, function (t, e, r) {
      r(0)({
        target: "Number",
        stat: !0
      }, {
        MAX_SAFE_INTEGER: 9007199254740991
      });
    }, function (t, e, r) {
      var n = r(385),
          i = r(153);

      t.exports = function (t, e) {
        if (i(Object(t)) || "[object Arguments]" === Object.prototype.toString.call(t)) {
          var r = [],
              o = !0,
              a = !1,
              s = void 0;

          try {
            for (var u, f = n(t); !(o = (u = f.next()).done) && (r.push(u.value), !e || r.length !== e); o = !0);
          } catch (t) {
            a = !0, s = t;
          } finally {
            try {
              o || null == f.return || f.return();
            } finally {
              if (a) throw s;
            }
          }

          return r;
        }
      };
    }, function (t, e, r) {
      t.exports = r(386);
    }, function (t, e, r) {
      r(41), r(46), t.exports = r(142);
    }, function (t, e, r) {
      t.exports = r(388);
    }, function (t, e, r) {
      t.exports = r(143);
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(98)),
          o = n(r(16)),
          a = n(r(99)),
          s = n(r(7)),
          u = n(r(10)),
          f = n(r(59)),
          c = r(100);
      var l = {
        a: 0,
        A: 0,
        c: 1,
        C: 1,
        g: 2,
        G: 2,
        t: 3,
        T: 3,
        n: 4,
        N: 4
      };

      var h = function () {
        function t() {
          (0, s.default)(this, t), this.tags = {};
        }

        return (0, u.default)(t, [{
          key: "isPaired",
          value: function () {
            return !!((0, a.default)(this) & c.BAM_FPAIRED);
          }
        }, {
          key: "isProperlyPaired",
          value: function () {
            return !!((0, a.default)(this) & c.BAM_FPROPER_PAIR);
          }
        }, {
          key: "isSegmentUnmapped",
          value: function () {
            return !!((0, a.default)(this) & c.BAM_FUNMAP);
          }
        }, {
          key: "isMateUnmapped",
          value: function () {
            return !!((0, a.default)(this) & c.BAM_FMUNMAP);
          }
        }, {
          key: "isReverseComplemented",
          value: function () {
            return !!((0, a.default)(this) & c.BAM_FREVERSE);
          }
        }, {
          key: "isMateReverseComplemented",
          value: function () {
            return !!((0, a.default)(this) & c.BAM_FMREVERSE);
          }
        }, {
          key: "isRead1",
          value: function () {
            return !!((0, a.default)(this) & c.BAM_FREAD1);
          }
        }, {
          key: "isRead2",
          value: function () {
            return !!((0, a.default)(this) & c.BAM_FREAD2);
          }
        }, {
          key: "isSecondary",
          value: function () {
            return !!((0, a.default)(this) & c.BAM_FSECONDARY);
          }
        }, {
          key: "isFailedQc",
          value: function () {
            return !!((0, a.default)(this) & c.BAM_FQCFAIL);
          }
        }, {
          key: "isDuplicate",
          value: function () {
            return !!((0, a.default)(this) & c.BAM_FDUP);
          }
        }, {
          key: "isSupplementary",
          value: function () {
            return !!((0, a.default)(this) & c.BAM_FSUPPLEMENTARY);
          }
        }, {
          key: "isDetached",
          value: function () {
            return !!(this.cramFlags & c.CRAM_FLAG_DETACHED);
          }
        }, {
          key: "hasMateDownStream",
          value: function () {
            return !!(this.cramFlags & c.CRAM_FLAG_MATE_DOWNSTREAM);
          }
        }, {
          key: "isPreservingQualityScores",
          value: function () {
            return !!(this.cramFlags & c.CRAM_FLAG_PRESERVE_QUAL_SCORES);
          }
        }, {
          key: "isUnknownBases",
          value: function () {
            return !!(this.cramFlags & c.CRAM_FLAG_NO_SEQ);
          }
        }, {
          key: "getReadBases",
          value: function () {
            return !this.readBases && this._refRegion && (this.readBases = function (t, e) {
              if ((t.lengthOnRef || t.readLength) && !t.isUnknownBases()) {
                var r = t.alignmentStart - e.start;
                if (!t.readFeatures) return e.seq.substr(r, t.lengthOnRef).toUpperCase();

                for (var n = "", i = r, o = 0; n.length < t.readLength;) if (o < t.readFeatures.length) {
                  var a = t.readFeatures[o];
                  if ("Q" === a.code || "q" === a.code) o += 1;else if (a.pos === n.length + 1) {
                    if (o += 1, "b" === a.code) {
                      var s = a.data.split(","),
                          u = String.fromCharCode.apply(String, (0, f.default)(s));
                      n += u, i += u.length;
                    } else "B" === a.code ? (n += a.data[0], i += 1) : "X" === a.code ? (n += a.sub, i += 1) : "I" === a.code ? n += a.data : "D" === a.code ? i += a.data : "i" === a.code ? n += a.data : "N" === a.code ? i += a.data : "S" === a.code ? n += a.data : "P" === a.code || a.code;
                  } else if (o < t.readFeatures.length) {
                    var c = e.seq.substr(i, t.readFeatures[o].pos - n.length - 1);
                    n += c, i += c.length;
                  }
                } else {
                  var l = e.seq.substr(i, t.readLength - n.length);
                  n += l, i += l.length;
                }

                return n.toUpperCase();
              }
            }(this, this._refRegion)), this.readBases;
          }
        }, {
          key: "getPairOrientation",
          value: function () {
            if (!this.isSegmentUnmapped() && this.isPaired() && !this.isMateUnmapped() && this.mate && this.sequenceId === this.mate.sequenceId) {
              var t = this.isReverseComplemented() ? "R" : "F",
                  e = this.isMateReverseComplemented() ? "R" : "F",
                  r = " ",
                  n = " ";
              this.isRead1() ? (r = "1", n = "2") : this.isRead2() && (r = "2", n = "1");
              var i = [],
                  o = this.templateLength || this.templateSize;
              return this.alignmentStart > this.mate.alignmentStart && o > 0 && (o = -o), o > 0 ? (i[0] = t, i[1] = r, i[2] = e, i[3] = n) : (i[2] = t, i[3] = r, i[0] = e, i[1] = n), i.join("");
            }

            return null;
          }
        }, {
          key: "addReferenceSequence",
          value: function (t, e) {
            var r;
            this.readFeatures && (0, o.default)(r = this.readFeatures).call(r, function (r) {
              "X" === r.code && function (t, e, r, n) {
                if (e) {
                  var i = n.refPos - e.start,
                      o = e.seq.charAt(i);
                  o && (n.ref = o);
                  var a = l[o];
                  void 0 === a && (a = 4);
                  var s = r.substitutionMatrix[a][n.data];
                  s && (n.sub = s);
                }
              }(0, t, e, r);
            });
            !this.readBases && t.start <= this.alignmentStart && t.end >= this.alignmentStart + (this.lengthOnRef || this.readLength) - 1 && (this._refRegion = t);
          }
        }, {
          key: "toJSON",
          value: function () {
            var t,
                e = this,
                r = {};
            return (0, o.default)(t = (0, i.default)(this)).call(t, function (t) {
              "_" !== t.charAt(0) && (r[t] = e[t]);
            }), r.readBases = this.getReadBases(), r;
          }
        }]), t;
      }();

      t.exports = h;
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(98)),
          o = n(r(16)),
          a = n(r(75)),
          s = n(r(7)),
          u = n(r(10)),
          f = r(9).CramMalformedError,
          c = r(391).instantiateCodec,
          l = {
        BF: "int",
        CF: "int",
        RI: "int",
        RL: "int",
        AP: "int",
        RG: "int",
        MF: "int",
        NS: "int",
        NP: "int",
        TS: "int",
        NF: "int",
        TC: "byte",
        TN: "int",
        FN: "int",
        FC: "byte",
        FP: "int",
        BS: "byte",
        IN: "byteArray",
        SC: "byteArray",
        DL: "int",
        BA: "byte",
        BB: "byteArray",
        RS: "int",
        PD: "int",
        HC: "int",
        MQ: "int",
        RN: "byteArray",
        QS: "byte",
        QQ: "byteArray",
        TL: "int",
        TM: "ignore",
        TV: "ignore"
      };

      var h = function () {
        function t(e) {
          (0, s.default)(this, t), (0, a.default)(this, e), this.readNamesIncluded = e.preservation.RN, this.APdelta = e.preservation.AP, this.referenceRequired = !!e.preservation.RR, this.tagIdsDictionary = e.preservation.TD, this.substitutionMatrix = function (t) {
            for (var e = new Array(5), r = 0; r < 5; r += 1) e[r] = new Array(4);

            return e[0][t[0] >> 6 & 3] = "C", e[0][t[0] >> 4 & 3] = "G", e[0][t[0] >> 2 & 3] = "T", e[0][t[0] >> 0 & 3] = "N", e[1][t[1] >> 6 & 3] = "A", e[1][t[1] >> 4 & 3] = "G", e[1][t[1] >> 2 & 3] = "T", e[1][t[1] >> 0 & 3] = "N", e[2][t[2] >> 6 & 3] = "A", e[2][t[2] >> 4 & 3] = "C", e[2][t[2] >> 2 & 3] = "T", e[2][t[2] >> 0 & 3] = "N", e[3][t[3] >> 6 & 3] = "A", e[3][t[3] >> 4 & 3] = "C", e[3][t[3] >> 2 & 3] = "G", e[3][t[3] >> 0 & 3] = "N", e[4][t[4] >> 6 & 3] = "A", e[4][t[4] >> 4 & 3] = "C", e[4][t[4] >> 2 & 3] = "G", e[4][t[4] >> 0 & 3] = "T", e;
          }(e.preservation.SM), this.dataSeriesCodecCache = {}, this.tagCodecCache = {};
        }

        return (0, u.default)(t, [{
          key: "getCodecForTag",
          value: function (t) {
            if (!this.tagCodecCache[t]) {
              var e = this.tagEncoding[t];
              e && (this.tagCodecCache[t] = c(e, "byteArray"));
            }

            return this.tagCodecCache[t];
          }
        }, {
          key: "getTagNames",
          value: function (t) {
            return this.tagIdsDictionary[t];
          }
        }, {
          key: "getCodecForDataSeries",
          value: function (t) {
            if (!this.dataSeriesCodecCache[t]) {
              var e = this.dataSeriesEncoding[t];

              if (e) {
                var r = l[t];
                if (!r) throw new f("data series name ".concat(t, " not defined in file compression header"));
                this.dataSeriesCodecCache[t] = c(e, r);
              }
            }

            return this.dataSeriesCodecCache[t];
          }
        }, {
          key: "toJSON",
          value: function () {
            var t,
                e = this,
                r = {};
            return (0, o.default)(t = (0, i.default)(this)).call(t, function (t) {
              /Cache$/.test(t) || (r[t] = e[t]);
            }), r;
          }
        }]), t;
      }();

      t.exports = h;
    }, function (t, e, r) {

      var n = r(9).CramUnimplementedError,
          i = r(392),
          o = r(413),
          a = r(414),
          s = r(415),
          u = r(416),
          f = r(417),
          c = {
        1: o,
        3: i,
        4: s,
        5: a,
        6: u,
        7: r(418),
        9: f
      };

      function l(t) {
        return c[t];
      }

      t.exports = {
        getCodecClassWithId: l,
        instantiateCodec: function t(e, r) {
          var i = l("ignore" === r ? 0 : e.codecId);
          if (!i) throw new n("no codec implemented for codec ID ".concat(e.codecId));
          return new i(e.parameters, r, t);
        }
      };
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(97)),
          o = n(r(59)),
          a = n(r(77)),
          s = n(r(148)),
          u = n(r(159)),
          f = n(r(48)),
          c = n(r(102)),
          l = n(r(16)),
          h = n(r(103)),
          d = n(r(403)),
          p = n(r(7)),
          g = n(r(10)),
          v = n(r(32)),
          y = n(r(27)),
          m = n(r(33)),
          b = r(9).CramMalformedError;

      var _ = function (t) {
        function e() {
          var t,
              r,
              n = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
              i = arguments.length > 1 ? arguments[1] : void 0;
          if ((0, p.default)(this, e), r = (0, v.default)(this, (0, y.default)(e).call(this, n, i)), !(0, d.default)(t = ["byte", "int"]).call(t, r.dataType)) throw new TypeError("".concat(r.dataType, " decoding not yet implemented by HUFFMAN_INT codec"));
          return r.buildCodeBook(), r.buildCodes(), r.buildCaches(), 0 === r.sortedCodes[0].bitLength && (r._decode = r._decodeZeroLengthCode), r;
        }

        return (0, m.default)(e, t), (0, g.default)(e, [{
          key: "buildCodeBook",
          value: function () {
            for (var t = this, e = new Array(this.parameters.numCodes), r = 0; r < this.parameters.numCodes; r += 1) e[r] = {
              symbol: this.parameters.symbols[r],
              bitLength: this.parameters.bitLengths[r]
            };

            e = (0, h.default)(e).call(e, function (t, e) {
              return t.bitLength - e.bitLength || t.symbol - e.symbol;
            }), this.codeBook = {}, (0, l.default)(e).call(e, function (e) {
              t.codeBook[e.bitLength] || (t.codeBook[e.bitLength] = []), t.codeBook[e.bitLength].push(e.symbol);
            });
          }
        }, {
          key: "buildCodes",
          value: function () {
            var t,
                e = this;
            this.codes = {};
            var r = 0,
                n = -1;
            (0, l.default)(t = (0, c.default)(this.codeBook)).call(t, function (t) {
              var i = (0, f.default)(t, 2),
                  o = i[0],
                  a = i[1];
              o = (0, u.default)(o, 10), (0, l.default)(a).call(a, function (t) {
                var i = {
                  bitLength: o,
                  value: t
                };
                n += 1;
                var a,
                    s,
                    u = o - r;
                if (n <<= u, i.bitCode = n, r += u, 16843009 * ((s = (858993459 & (s = (a = n) - (a >> 1) & 1431655765)) + (s >> 2 & 858993459)) + (s >> 4) & 252645135) >> 24 > o) throw new b("Symbol out of range");
                e.codes[t] = i;
              });
            });
          }
        }, {
          key: "buildCaches",
          value: function () {
            var t, e, r, n, u, f;
            this.sortedCodes = (0, h.default)(t = (0, s.default)(this.codes)).call(t, function (t, e) {
              return t.bitLength - e.bitLength || t.bitCode - e.bitCode;
            }), this.sortedByValue = (0, h.default)(e = (0, s.default)(this.codes)).call(e, function (t, e) {
              return t.value - e.value;
            }), this.sortedValuesByBitCode = (0, a.default)(r = this.sortedCodes).call(r, function (t) {
              return t.value;
            }), this.sortedBitCodes = (0, a.default)(n = this.sortedCodes).call(n, function (t) {
              return t.bitCode;
            }), this.sortedBitLengthsByBitCode = (0, a.default)(u = this.sortedCodes).call(u, function (t) {
              return t.bitLength;
            });
            var c = Math.max.apply(Math, (0, o.default)(this.sortedBitCodes));
            this.bitCodeToValue = (0, i.default)(f = new Array(c + 1)).call(f, -1);

            for (var l = 0; l < this.sortedBitCodes.length; l += 1) this.bitCodeToValue[this.sortedCodes[l].bitCode] = l;
          }
        }, {
          key: "decode",
          value: function (t, e, r, n) {
            return this._decode(t, e, n.coreBlock);
          }
        }, {
          key: "_decodeZeroLengthCode",
          value: function () {
            return this.sortedCodes[0].value;
          }
        }, {
          key: "_decode",
          value: function (t, e, r) {
            for (var n = e.content, i = 0, o = 0, a = 0; a < this.sortedCodes.length; a += 1) {
              var s = this.sortedCodes[a].bitLength;
              o <<= s - i, o |= this._getBits(n, r, s - i), i = s;
              var u = this.bitCodeToValue[o];
              if (u > -1 && this.sortedBitLengthsByBitCode[u] === s) return this.sortedValuesByBitCode[u];

              for (var f = a; this.sortedCodes[f + 1].bitLength === s && f < this.sortedCodes.length; f += 1) a += 1;
            }

            throw new b("Huffman symbol not found.");
          }
        }]), e;
      }(r(40));

      t.exports = _;
    }, function (t, e, r) {
      t.exports = r(394);
    }, function (t, e, r) {
      r(395);
      var n = r(3);
      t.exports = n.parseInt;
    }, function (t, e, r) {
      var n = r(0),
          i = r(155);
      n({
        global: !0,
        forced: parseInt != i
      }, {
        parseInt: i
      });
    }, function (t, e, r) {
      t.exports = r(397);
    }, function (t, e, r) {
      r(398);
      var n = r(3);
      t.exports = n.Object.entries;
    }, function (t, e, r) {
      var n = r(0),
          i = r(149).entries;
      n({
        target: "Object",
        stat: !0
      }, {
        entries: function (t) {
          return i(t);
        }
      });
    }, function (t, e, r) {
      t.exports = r(400);
    }, function (t, e, r) {
      var n = r(401),
          i = Array.prototype;

      t.exports = function (t) {
        var e = t.sort;
        return t === i || t instanceof Array && e === i.sort ? n : e;
      };
    }, function (t, e, r) {
      r(402);
      var n = r(18);
      t.exports = n("Array").sort;
    }, function (t, e, r) {

      var n = r(0),
          i = r(5),
          o = r(22),
          a = r(12),
          s = r(54),
          u = [].sort,
          f = [1, 2, 3],
          c = a(function () {
        f.sort(void 0);
      }),
          l = a(function () {
        f.sort(null);
      }),
          h = s("sort");
      n({
        target: "Array",
        proto: !0,
        forced: c || !l || h
      }, {
        sort: function (t) {
          return void 0 === t ? u.call(o(this)) : u.call(o(this), i(t));
        }
      });
    }, function (t, e, r) {
      t.exports = r(404);
    }, function (t, e, r) {
      t.exports = r(405);
    }, function (t, e, r) {
      var n = r(406),
          i = r(408),
          o = Array.prototype,
          a = String.prototype;

      t.exports = function (t) {
        var e = t.includes;
        return t === o || t instanceof Array && e === o.includes ? n : "string" == typeof t || t === a || t instanceof String && e === a.includes ? i : e;
      };
    }, function (t, e, r) {
      r(407);
      var n = r(18);
      t.exports = n("Array").includes;
    }, function (t, e, r) {

      var n = r(0),
          i = r(82).includes,
          o = r(78);
      n({
        target: "Array",
        proto: !0
      }, {
        includes: function (t) {
          return i(this, t, arguments.length > 1 ? arguments[1] : void 0);
        }
      }), o("includes");
    }, function (t, e, r) {
      r(409);
      var n = r(18);
      t.exports = n("String").includes;
    }, function (t, e, r) {

      var n = r(0),
          i = r(410),
          o = r(49);
      n({
        target: "String",
        proto: !0,
        forced: !r(412)("includes")
      }, {
        includes: function (t) {
          return !!~String(o(this)).indexOf(i(t), arguments.length > 1 ? arguments[1] : void 0);
        }
      });
    }, function (t, e, r) {
      var n = r(411);

      t.exports = function (t) {
        if (n(t)) throw TypeError("The method doesn't accept regular expressions");
        return t;
      };
    }, function (t, e, r) {
      var n = r(14),
          i = r(34),
          o = r(6)("match");

      t.exports = function (t) {
        var e;
        return n(t) && (void 0 !== (e = t[o]) ? !!e : "RegExp" == i(t));
      };
    }, function (t, e, r) {
      var n = r(6)("match");

      t.exports = function (t) {
        var e = /./;

        try {
          "/./"[t](e);
        } catch (r) {
          try {
            return e[n] = !1, "/./"[t](e);
          } catch (t) {}
        }

        return !1;
      };
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(48)),
          o = n(r(7)),
          a = n(r(10)),
          s = n(r(32)),
          u = n(r(27)),
          f = n(r(33)),
          c = r(9),
          l = c.CramUnimplementedError,
          h = c.CramMalformedError,
          d = c.CramBufferOverrunError,
          p = r(40),
          g = r(58).parseItf8,
          v = function (t) {
        function e() {
          var t,
              r = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
              n = arguments.length > 1 ? arguments[1] : void 0;
          if ((0, o.default)(this, e), "int" === (t = (0, s.default)(this, (0, u.default)(e).call(this, r, n))).dataType) t._decodeData = t._decodeInt;else {
            if ("byte" !== t.dataType) throw new l("".concat(t.dataType, " decoding not yet implemented by EXTERNAL codec"));
            t._decodeData = t._decodeByte;
          }
          return t;
        }

        return (0, f.default)(e, t), (0, a.default)(e, [{
          key: "decode",
          value: function (t, e, r, n) {
            var i = this.parameters.blockContentId,
                o = r[i];
            if (!o) throw new h("no block found with content ID ".concat(i));
            var a = n.externalBlocks.getCursor(i);
            return this._decodeData(o, a);
          }
        }, {
          key: "_decodeInt",
          value: function (t, e) {
            var r = g(t.content, e.bytePosition),
                n = (0, i.default)(r, 2),
                o = n[0],
                a = n[1];
            return e.bytePosition += a, o;
          }
        }, {
          key: "_decodeByte",
          value: function (t, e) {
            if (e.bytePosition >= t.content.length) throw new d("attempted to read beyond end of block. this file seems truncated.");
            var r = t.content[e.bytePosition];
            return e.bytePosition += 1, r;
          }
        }]), e;
      }(p);

      t.exports = v;
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(101)),
          o = n(r(7)),
          a = n(r(10)),
          s = n(r(32)),
          u = n(r(27)),
          f = n(r(33)),
          c = r(9),
          l = c.CramBufferOverrunError,
          h = c.CramMalformedError,
          d = function (t) {
        function e() {
          var t,
              r = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
              n = arguments.length > 1 ? arguments[1] : void 0;
          if ((0, o.default)(this, e), t = (0, s.default)(this, (0, u.default)(e).call(this, r, n)), "byteArray" !== n) throw new TypeError("byteArrayStop codec does not support data type ".concat(n));
          return t._decode = t._decodeByteArray, t;
        }

        return (0, f.default)(e, t), (0, a.default)(e, [{
          key: "decode",
          value: function (t, e, r, n) {
            var i = this.parameters.blockContentId,
                o = r[i];
            if (!o) throw new h("no block found with content ID ".concat(i));
            var a = n.externalBlocks.getCursor(i);
            return this._decode(o, a);
          }
        }, {
          key: "_decodeByteArray",
          value: function (t, e) {
            for (var r = t.content, n = this.parameters.stopByte, o = e.bytePosition, a = e.bytePosition; r[a] !== n && a < r.length;) {
              if (a === r.length) throw new l("byteArrayStop reading beyond length of data buffer?");
              a += 1;
            }

            return e.bytePosition = a + 1, (0, i.default)(r).call(r, o, a);
          }
        }]), e;
      }(r(40));

      t.exports = d;
    }, function (t, e, r) {

      var n,
          i = r(1),
          o = i(r(16)),
          a = i(r(7)),
          s = i(r(10)),
          u = i(r(32)),
          f = i(r(27)),
          c = i(r(33)),
          l = r(58).tinyMemoize,
          h = function (t) {
        function e() {
          var t,
              r = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
              n = arguments.length > 1 ? arguments[1] : void 0,
              i = arguments.length > 2 ? arguments[2] : void 0;
          if ((0, a.default)(this, e), (t = (0, u.default)(this, (0, f.default)(e).call(this, r, n))).instantiateCodec = i, "byteArray" !== n) throw new TypeError("byteArrayLength does not support data type ".concat(n));
          return t;
        }

        return (0, c.default)(e, t), (0, s.default)(e, [{
          key: "decode",
          value: function (t, e, r, n) {
            for (var i = this._getLengthCodec().decode(t, e, r, n), o = this._getDataCodec(), a = new Array(i), s = 0; s < i; s += 1) a[s] = o.decode(t, e, r, n);

            return a;
          }
        }, {
          key: "_getLengthCodec",
          value: function () {
            var t = this.parameters.lengthsEncoding;
            return this.instantiateCodec(t, "int");
          }
        }, {
          key: "_getDataCodec",
          value: function () {
            var t = this.parameters.valuesEncoding;
            return this.instantiateCodec(t, "byte");
          }
        }]), e;
      }(r(40));

      (0, o.default)(n = "_getLengthCodec _getDataCodec".split(" ")).call(n, function (t) {
        return l(h, t);
      }), t.exports = h;
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(7)),
          o = n(r(10)),
          a = n(r(32)),
          s = n(r(27)),
          u = n(r(33)),
          f = r(9).CramUnimplementedError,
          c = function (t) {
        function e() {
          var t,
              r = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
              n = arguments.length > 1 ? arguments[1] : void 0;
          if ((0, i.default)(this, e), "int" !== (t = (0, a.default)(this, (0, s.default)(e).call(this, r, n))).dataType) throw new f("".concat(t.dataType, " decoding not yet implemented by BETA codec"));
          return t;
        }

        return (0, u.default)(e, t), (0, o.default)(e, [{
          key: "decode",
          value: function (t, e, r, n) {
            return this._getBits(e.content, n.coreBlock, this.parameters.length) - this.parameters.offset;
          }
        }]), e;
      }(r(40));

      t.exports = c;
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(7)),
          o = n(r(10)),
          a = n(r(32)),
          s = n(r(27)),
          u = n(r(33)),
          f = r(9).CramUnimplementedError,
          c = function (t) {
        function e() {
          var t,
              r = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
              n = arguments.length > 1 ? arguments[1] : void 0;
          if ((0, i.default)(this, e), "int" !== (t = (0, a.default)(this, (0, s.default)(e).call(this, r, n))).dataType) throw new f("".concat(t.dataType, " decoding not yet implemented by GAMMA codec"));
          return t;
        }

        return (0, u.default)(e, t), (0, o.default)(e, [{
          key: "decode",
          value: function (t, e, r, n) {
            for (var i = 1; 0 === this._getBits(e.content, n.coreBlock, 1);) i += 1;

            return (this._getBits(e.content, n.coreBlock, i - 1) | 1 << i - 1) - this.parameters.offset;
          }
        }]), e;
      }(r(40));

      t.exports = c;
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(7)),
          o = n(r(10)),
          a = n(r(32)),
          s = n(r(27)),
          u = n(r(33)),
          f = r(9).CramUnimplementedError,
          c = function (t) {
        function e() {
          var t,
              r = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
              n = arguments.length > 1 ? arguments[1] : void 0;
          if ((0, i.default)(this, e), "int" !== (t = (0, a.default)(this, (0, s.default)(e).call(this, r, n))).dataType) throw new f("".concat(t.dataType, " decoding not yet implemented by SUBEXP codec"));
          return t;
        }

        return (0, u.default)(e, t), (0, o.default)(e, [{
          key: "decode",
          value: function (t, e, r, n) {
            for (var i, o, a = 0; this._getBits(e.content, n.coreBlock, 1);) a += 1;

            return 0 === a ? (i = this.parameters.K, o = this._getBits(e.content, n.coreBlock, i)) : o = 1 << (i = a + this.parameters.K - 1) | this._getBits(e.content, n.coreBlock, i), o - this.parameters.offset;
          }
        }]), e;
      }(r(40));

      t.exports = c;
    }, function (t, e, r) {

      var n = r(420),
          i = r(422);

      function o() {
        this.protocol = null, this.slashes = null, this.auth = null, this.host = null, this.port = null, this.hostname = null, this.hash = null, this.search = null, this.query = null, this.pathname = null, this.path = null, this.href = null;
      }

      e.parse = b, e.resolve = function (t, e) {
        return b(t, !1, !0).resolve(e);
      }, e.resolveObject = function (t, e) {
        return t ? b(t, !1, !0).resolveObject(e) : e;
      }, e.format = function (t) {
        i.isString(t) && (t = b(t));
        return t instanceof o ? t.format() : o.prototype.format.call(t);
      }, e.Url = o;
      var a = /^([a-z0-9.+-]+:)/i,
          s = /:[0-9]*$/,
          u = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,
          f = ["{", "}", "|", "\\", "^", "`"].concat(["<", ">", '"', "`", " ", "\r", "\n", "\t"]),
          c = ["'"].concat(f),
          l = ["%", "/", "?", ";", "#"].concat(c),
          h = ["/", "?", "#"],
          d = /^[+a-z0-9A-Z_-]{0,63}$/,
          p = /^([+a-z0-9A-Z_-]{0,63})(.*)$/,
          g = {
        javascript: !0,
        "javascript:": !0
      },
          v = {
        javascript: !0,
        "javascript:": !0
      },
          y = {
        http: !0,
        https: !0,
        ftp: !0,
        gopher: !0,
        file: !0,
        "http:": !0,
        "https:": !0,
        "ftp:": !0,
        "gopher:": !0,
        "file:": !0
      },
          m = r(423);

      function b(t, e, r) {
        if (t && i.isObject(t) && t instanceof o) return t;
        var n = new o();
        return n.parse(t, e, r), n;
      }

      o.prototype.parse = function (t, e, r) {
        if (!i.isString(t)) throw new TypeError("Parameter 'url' must be a string, not " + typeof t);
        var o = t.indexOf("?"),
            s = -1 !== o && o < t.indexOf("#") ? "?" : "#",
            f = t.split(s);
        f[0] = f[0].replace(/\\/g, "/");
        var b = t = f.join(s);

        if (b = b.trim(), !r && 1 === t.split("#").length) {
          var _ = u.exec(b);

          if (_) return this.path = b, this.href = b, this.pathname = _[1], _[2] ? (this.search = _[2], this.query = e ? m.parse(this.search.substr(1)) : this.search.substr(1)) : e && (this.search = "", this.query = {}), this;
        }

        var w = a.exec(b);

        if (w) {
          var x = (w = w[0]).toLowerCase();
          this.protocol = x, b = b.substr(w.length);
        }

        if (r || w || b.match(/^\/\/[^@\/]+@[^@\/]+/)) {
          var E = "//" === b.substr(0, 2);
          !E || w && v[w] || (b = b.substr(2), this.slashes = !0);
        }

        if (!v[w] && (E || w && !y[w])) {
          for (var S, k, A = -1, C = 0; C < h.length; C++) {
            -1 !== (T = b.indexOf(h[C])) && (-1 === A || T < A) && (A = T);
          }

          -1 !== (k = -1 === A ? b.lastIndexOf("@") : b.lastIndexOf("@", A)) && (S = b.slice(0, k), b = b.slice(k + 1), this.auth = decodeURIComponent(S)), A = -1;

          for (C = 0; C < l.length; C++) {
            var T;
            -1 !== (T = b.indexOf(l[C])) && (-1 === A || T < A) && (A = T);
          }

          -1 === A && (A = b.length), this.host = b.slice(0, A), b = b.slice(A), this.parseHost(), this.hostname = this.hostname || "";
          var R = "[" === this.hostname[0] && "]" === this.hostname[this.hostname.length - 1];
          if (!R) for (var O = this.hostname.split(/\./), I = (C = 0, O.length); C < I; C++) {
            var L = O[C];

            if (L && !L.match(d)) {
              for (var B = "", P = 0, F = L.length; P < F; P++) L.charCodeAt(P) > 127 ? B += "x" : B += L[P];

              if (!B.match(d)) {
                var N = O.slice(0, C),
                    M = O.slice(C + 1),
                    j = L.match(p);
                j && (N.push(j[1]), M.unshift(j[2])), M.length && (b = "/" + M.join(".") + b), this.hostname = N.join(".");
                break;
              }
            }
          }
          this.hostname.length > 255 ? this.hostname = "" : this.hostname = this.hostname.toLowerCase(), R || (this.hostname = n.toASCII(this.hostname));
          var z = this.port ? ":" + this.port : "",
              U = this.hostname || "";
          this.host = U + z, this.href += this.host, R && (this.hostname = this.hostname.substr(1, this.hostname.length - 2), "/" !== b[0] && (b = "/" + b));
        }

        if (!g[x]) for (C = 0, I = c.length; C < I; C++) {
          var D = c[C];

          if (-1 !== b.indexOf(D)) {
            var q = encodeURIComponent(D);
            q === D && (q = escape(D)), b = b.split(D).join(q);
          }
        }
        var Z = b.indexOf("#");
        -1 !== Z && (this.hash = b.substr(Z), b = b.slice(0, Z));
        var H = b.indexOf("?");

        if (-1 !== H ? (this.search = b.substr(H), this.query = b.substr(H + 1), e && (this.query = m.parse(this.query)), b = b.slice(0, H)) : e && (this.search = "", this.query = {}), b && (this.pathname = b), y[x] && this.hostname && !this.pathname && (this.pathname = "/"), this.pathname || this.search) {
          z = this.pathname || "";
          var V = this.search || "";
          this.path = z + V;
        }

        return this.href = this.format(), this;
      }, o.prototype.format = function () {
        var t = this.auth || "";
        t && (t = (t = encodeURIComponent(t)).replace(/%3A/i, ":"), t += "@");
        var e = this.protocol || "",
            r = this.pathname || "",
            n = this.hash || "",
            o = !1,
            a = "";
        this.host ? o = t + this.host : this.hostname && (o = t + (-1 === this.hostname.indexOf(":") ? this.hostname : "[" + this.hostname + "]"), this.port && (o += ":" + this.port)), this.query && i.isObject(this.query) && Object.keys(this.query).length && (a = m.stringify(this.query));
        var s = this.search || a && "?" + a || "";
        return e && ":" !== e.substr(-1) && (e += ":"), this.slashes || (!e || y[e]) && !1 !== o ? (o = "//" + (o || ""), r && "/" !== r.charAt(0) && (r = "/" + r)) : o || (o = ""), n && "#" !== n.charAt(0) && (n = "#" + n), s && "?" !== s.charAt(0) && (s = "?" + s), e + o + (r = r.replace(/[?#]/g, function (t) {
          return encodeURIComponent(t);
        })) + (s = s.replace("#", "%23")) + n;
      }, o.prototype.resolve = function (t) {
        return this.resolveObject(b(t, !1, !0)).format();
      }, o.prototype.resolveObject = function (t) {
        if (i.isString(t)) {
          var e = new o();
          e.parse(t, !1, !0), t = e;
        }

        for (var r = new o(), n = Object.keys(this), a = 0; a < n.length; a++) {
          var s = n[a];
          r[s] = this[s];
        }

        if (r.hash = t.hash, "" === t.href) return r.href = r.format(), r;

        if (t.slashes && !t.protocol) {
          for (var u = Object.keys(t), f = 0; f < u.length; f++) {
            var c = u[f];
            "protocol" !== c && (r[c] = t[c]);
          }

          return y[r.protocol] && r.hostname && !r.pathname && (r.path = r.pathname = "/"), r.href = r.format(), r;
        }

        if (t.protocol && t.protocol !== r.protocol) {
          if (!y[t.protocol]) {
            for (var l = Object.keys(t), h = 0; h < l.length; h++) {
              var d = l[h];
              r[d] = t[d];
            }

            return r.href = r.format(), r;
          }

          if (r.protocol = t.protocol, t.host || v[t.protocol]) r.pathname = t.pathname;else {
            for (var p = (t.pathname || "").split("/"); p.length && !(t.host = p.shift()););

            t.host || (t.host = ""), t.hostname || (t.hostname = ""), "" !== p[0] && p.unshift(""), p.length < 2 && p.unshift(""), r.pathname = p.join("/");
          }

          if (r.search = t.search, r.query = t.query, r.host = t.host || "", r.auth = t.auth, r.hostname = t.hostname || t.host, r.port = t.port, r.pathname || r.search) {
            var g = r.pathname || "",
                m = r.search || "";
            r.path = g + m;
          }

          return r.slashes = r.slashes || t.slashes, r.href = r.format(), r;
        }

        var b = r.pathname && "/" === r.pathname.charAt(0),
            _ = t.host || t.pathname && "/" === t.pathname.charAt(0),
            w = _ || b || r.host && t.pathname,
            x = w,
            E = r.pathname && r.pathname.split("/") || [],
            S = (p = t.pathname && t.pathname.split("/") || [], r.protocol && !y[r.protocol]);

        if (S && (r.hostname = "", r.port = null, r.host && ("" === E[0] ? E[0] = r.host : E.unshift(r.host)), r.host = "", t.protocol && (t.hostname = null, t.port = null, t.host && ("" === p[0] ? p[0] = t.host : p.unshift(t.host)), t.host = null), w = w && ("" === p[0] || "" === E[0])), _) r.host = t.host || "" === t.host ? t.host : r.host, r.hostname = t.hostname || "" === t.hostname ? t.hostname : r.hostname, r.search = t.search, r.query = t.query, E = p;else if (p.length) E || (E = []), E.pop(), E = E.concat(p), r.search = t.search, r.query = t.query;else if (!i.isNullOrUndefined(t.search)) {
          if (S) r.hostname = r.host = E.shift(), (R = !!(r.host && r.host.indexOf("@") > 0) && r.host.split("@")) && (r.auth = R.shift(), r.host = r.hostname = R.shift());
          return r.search = t.search, r.query = t.query, i.isNull(r.pathname) && i.isNull(r.search) || (r.path = (r.pathname ? r.pathname : "") + (r.search ? r.search : "")), r.href = r.format(), r;
        }
        if (!E.length) return r.pathname = null, r.search ? r.path = "/" + r.search : r.path = null, r.href = r.format(), r;

        for (var k = E.slice(-1)[0], A = (r.host || t.host || E.length > 1) && ("." === k || ".." === k) || "" === k, C = 0, T = E.length; T >= 0; T--) "." === (k = E[T]) ? E.splice(T, 1) : ".." === k ? (E.splice(T, 1), C++) : C && (E.splice(T, 1), C--);

        if (!w && !x) for (; C--; C) E.unshift("..");
        !w || "" === E[0] || E[0] && "/" === E[0].charAt(0) || E.unshift(""), A && "/" !== E.join("/").substr(-1) && E.push("");
        var R,
            O = "" === E[0] || E[0] && "/" === E[0].charAt(0);
        S && (r.hostname = r.host = O ? "" : E.length ? E.shift() : "", (R = !!(r.host && r.host.indexOf("@") > 0) && r.host.split("@")) && (r.auth = R.shift(), r.host = r.hostname = R.shift()));
        return (w = w || r.host && E.length) && !O && E.unshift(""), E.length ? r.pathname = E.join("/") : (r.pathname = null, r.path = null), i.isNull(r.pathname) && i.isNull(r.search) || (r.path = (r.pathname ? r.pathname : "") + (r.search ? r.search : "")), r.auth = t.auth || r.auth, r.slashes = r.slashes || t.slashes, r.href = r.format(), r;
      }, o.prototype.parseHost = function () {
        var t = this.host,
            e = s.exec(t);
        e && (":" !== (e = e[0]) && (this.port = e.substr(1)), t = t.substr(0, t.length - e.length)), t && (this.hostname = t);
      };
    }, function (t, e, r) {
      (function (t, n) {
        var i;
        /*! https://mths.be/punycode v1.4.1 by @mathias */

        !function (o) {
          e && e.nodeType, t && t.nodeType;
          var a = "object" == typeof n && n;
          a.global !== a && a.window !== a && a.self;
          var s,
              u = 2147483647,
              f = 36,
              c = 1,
              l = 26,
              h = 38,
              d = 700,
              p = 72,
              g = 128,
              v = "-",
              y = /^xn--/,
              m = /[^\x20-\x7E]/,
              b = /[\x2E\u3002\uFF0E\uFF61]/g,
              _ = {
            overflow: "Overflow: input needs wider integers to process",
            "not-basic": "Illegal input >= 0x80 (not a basic code point)",
            "invalid-input": "Invalid input"
          },
              w = f - c,
              x = Math.floor,
              E = String.fromCharCode;

          function S(t) {
            throw new RangeError(_[t]);
          }

          function k(t, e) {
            for (var r = t.length, n = []; r--;) n[r] = e(t[r]);

            return n;
          }

          function A(t, e) {
            var r = t.split("@"),
                n = "";
            return r.length > 1 && (n = r[0] + "@", t = r[1]), n + k((t = t.replace(b, ".")).split("."), e).join(".");
          }

          function C(t) {
            for (var e, r, n = [], i = 0, o = t.length; i < o;) (e = t.charCodeAt(i++)) >= 55296 && e <= 56319 && i < o ? 56320 == (64512 & (r = t.charCodeAt(i++))) ? n.push(((1023 & e) << 10) + (1023 & r) + 65536) : (n.push(e), i--) : n.push(e);

            return n;
          }

          function T(t) {
            return k(t, function (t) {
              var e = "";
              return t > 65535 && (e += E((t -= 65536) >>> 10 & 1023 | 55296), t = 56320 | 1023 & t), e += E(t);
            }).join("");
          }

          function R(t, e) {
            return t + 22 + 75 * (t < 26) - ((0 != e) << 5);
          }

          function O(t, e, r) {
            var n = 0;

            for (t = r ? x(t / d) : t >> 1, t += x(t / e); t > w * l >> 1; n += f) t = x(t / w);

            return x(n + (w + 1) * t / (t + h));
          }

          function I(t) {
            var e,
                r,
                n,
                i,
                o,
                a,
                s,
                h,
                d,
                y,
                m,
                b = [],
                _ = t.length,
                w = 0,
                E = g,
                k = p;

            for ((r = t.lastIndexOf(v)) < 0 && (r = 0), n = 0; n < r; ++n) t.charCodeAt(n) >= 128 && S("not-basic"), b.push(t.charCodeAt(n));

            for (i = r > 0 ? r + 1 : 0; i < _;) {
              for (o = w, a = 1, s = f; i >= _ && S("invalid-input"), ((h = (m = t.charCodeAt(i++)) - 48 < 10 ? m - 22 : m - 65 < 26 ? m - 65 : m - 97 < 26 ? m - 97 : f) >= f || h > x((u - w) / a)) && S("overflow"), w += h * a, !(h < (d = s <= k ? c : s >= k + l ? l : s - k)); s += f) a > x(u / (y = f - d)) && S("overflow"), a *= y;

              k = O(w - o, e = b.length + 1, 0 == o), x(w / e) > u - E && S("overflow"), E += x(w / e), w %= e, b.splice(w++, 0, E);
            }

            return T(b);
          }

          function L(t) {
            var e,
                r,
                n,
                i,
                o,
                a,
                s,
                h,
                d,
                y,
                m,
                b,
                _,
                w,
                k,
                A = [];

            for (b = (t = C(t)).length, e = g, r = 0, o = p, a = 0; a < b; ++a) (m = t[a]) < 128 && A.push(E(m));

            for (n = i = A.length, i && A.push(v); n < b;) {
              for (s = u, a = 0; a < b; ++a) (m = t[a]) >= e && m < s && (s = m);

              for (s - e > x((u - r) / (_ = n + 1)) && S("overflow"), r += (s - e) * _, e = s, a = 0; a < b; ++a) if ((m = t[a]) < e && ++r > u && S("overflow"), m == e) {
                for (h = r, d = f; !(h < (y = d <= o ? c : d >= o + l ? l : d - o)); d += f) k = h - y, w = f - y, A.push(E(R(y + k % w, 0))), h = x(k / w);

                A.push(E(R(h, 0))), o = O(r, _, n == i), r = 0, ++n;
              }

              ++r, ++e;
            }

            return A.join("");
          }

          s = {
            version: "1.4.1",
            ucs2: {
              decode: C,
              encode: T
            },
            decode: I,
            encode: L,
            toASCII: function (t) {
              return A(t, function (t) {
                return m.test(t) ? "xn--" + L(t) : t;
              });
            },
            toUnicode: function (t) {
              return A(t, function (t) {
                return y.test(t) ? I(t.slice(4).toLowerCase()) : t;
              });
            }
          }, void 0 === (i = function () {
            return s;
          }.call(e, r, e, t)) || (t.exports = i);
        }();
      }).call(this, r(421)(t), r(23));
    }, function (t, e) {
      t.exports = function (t) {
        return t.webpackPolyfill || (t.deprecate = function () {}, t.paths = [], t.children || (t.children = []), Object.defineProperty(t, "loaded", {
          enumerable: !0,
          get: function () {
            return t.l;
          }
        }), Object.defineProperty(t, "id", {
          enumerable: !0,
          get: function () {
            return t.i;
          }
        }), t.webpackPolyfill = 1), t;
      };
    }, function (t, e, r) {

      t.exports = {
        isString: function (t) {
          return "string" == typeof t;
        },
        isObject: function (t) {
          return "object" == typeof t && null !== t;
        },
        isNull: function (t) {
          return null === t;
        },
        isNullOrUndefined: function (t) {
          return null == t;
        }
      };
    }, function (t, e, r) {

      e.decode = e.parse = r(424), e.encode = e.stringify = r(425);
    }, function (t, e, r) {

      function n(t, e) {
        return Object.prototype.hasOwnProperty.call(t, e);
      }

      t.exports = function (t, e, r, o) {
        e = e || "&", r = r || "=";
        var a = {};
        if ("string" != typeof t || 0 === t.length) return a;
        var s = /\+/g;
        t = t.split(e);
        var u = 1e3;
        o && "number" == typeof o.maxKeys && (u = o.maxKeys);
        var f = t.length;
        u > 0 && f > u && (f = u);

        for (var c = 0; c < f; ++c) {
          var l,
              h,
              d,
              p,
              g = t[c].replace(s, "%20"),
              v = g.indexOf(r);
          v >= 0 ? (l = g.substr(0, v), h = g.substr(v + 1)) : (l = g, h = ""), d = decodeURIComponent(l), p = decodeURIComponent(h), n(a, d) ? i(a[d]) ? a[d].push(p) : a[d] = [a[d], p] : a[d] = p;
        }

        return a;
      };

      var i = Array.isArray || function (t) {
        return "[object Array]" === Object.prototype.toString.call(t);
      };
    }, function (t, e, r) {

      var n = function (t) {
        switch (typeof t) {
          case "string":
            return t;

          case "boolean":
            return t ? "true" : "false";

          case "number":
            return isFinite(t) ? t : "";

          default:
            return "";
        }
      };

      t.exports = function (t, e, r, s) {
        return e = e || "&", r = r || "=", null === t && (t = void 0), "object" == typeof t ? o(a(t), function (a) {
          var s = encodeURIComponent(n(a)) + r;
          return i(t[a]) ? o(t[a], function (t) {
            return s + encodeURIComponent(n(t));
          }).join(e) : s + encodeURIComponent(n(t[a]));
        }).join(e) : s ? encodeURIComponent(n(s)) + r + encodeURIComponent(n(t)) : "";
      };

      var i = Array.isArray || function (t) {
        return "[object Array]" === Object.prototype.toString.call(t);
      };

      function o(t, e) {
        if (t.map) return t.map(e);

        for (var r = [], n = 0; n < t.length; n++) r.push(e(t[n], n));

        return r;
      }

      var a = Object.keys || function (t) {
        var e = [];

        for (var r in t) Object.prototype.hasOwnProperty.call(t, r) && e.push(r);

        return e;
      };
    }, function (t, e, r) {

      (function (e) {
        var n = r(1),
            i = n(r(30)),
            o = n(r(159)),
            a = n(r(37)),
            s = n(r(31)),
            u = n(r(7)),
            f = n(r(10)),
            c = r(427),
            l = r(428),
            h = function () {
          function t(e) {
            var r = this;
            (0, u.default)(this, t), this.position = 0, this.url = e, this.cache = new l({
              fetch: function (t, e) {
                return r._fetch(t, e);
              }
            });
          }

          var r, n, h;
          return (0, f.default)(t, [{
            key: "_fetch",
            value: (h = (0, s.default)(i.default.mark(function t(r, n) {
              var s, u, f, l, h, d;
              return i.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    return u = {}, n < 1 / 0 ? u.range = (0, a.default)(f = "bytes=".concat(r, "-")).call(f, r + n) : n === 1 / 0 && 0 !== r && (u.range = "bytes=".concat(r, "-")), t.next = 4, c(this.url, {
                      method: "GET",
                      headers: u,
                      redirect: "follow",
                      mode: "cors"
                    });

                  case 4:
                    if ((200 !== (l = t.sent).status || 0 !== r) && 206 !== l.status) {
                      t.next = 14;
                      break;
                    }

                    return t.t0 = e, t.next = 9, l.arrayBuffer();

                  case 9:
                    return t.t1 = t.sent, h = t.t0.from.call(t.t0, t.t1), (d = /\/(\d+)$/.exec(l.headers.get("content-range")))[1] && (this._stat = {
                      size: (0, o.default)(d[1], 10)
                    }), t.abrupt("return", h);

                  case 14:
                    throw new Error((0, a.default)(s = "HTTP ".concat(l.status, " fetching ")).call(s, this.url));

                  case 15:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function (t, e) {
              return h.apply(this, arguments);
            })
          }, {
            key: "read",
            value: function (t) {
              var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : 0,
                  r = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : 1 / 0,
                  n = arguments.length > 3 && void 0 !== arguments[3] ? arguments[3] : 0,
                  i = n;
              return null === i && (i = this.position, this.position += r), this.cache.get(t, e, r, n);
            }
          }, {
            key: "readFile",
            value: (n = (0, s.default)(i.default.mark(function t() {
              var r;
              return i.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    return t.next = 2, c(this.url, {
                      method: "GET",
                      redirect: "follow",
                      mode: "cors"
                    });

                  case 2:
                    return r = t.sent, t.t0 = e, t.next = 6, r.arrayBuffer();

                  case 6:
                    return t.t1 = t.sent, t.abrupt("return", t.t0.from.call(t.t0, t.t1));

                  case 8:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function () {
              return n.apply(this, arguments);
            })
          }, {
            key: "stat",
            value: (r = (0, s.default)(i.default.mark(function t() {
              var r;
              return i.default.wrap(function (t) {
                for (;;) switch (t.prev = t.next) {
                  case 0:
                    if (this._stat) {
                      t.next = 6;
                      break;
                    }

                    return r = e.allocUnsafe(10), t.next = 4, this.read(r, 0, 10, 0);

                  case 4:
                    if (this._stat) {
                      t.next = 6;
                      break;
                    }

                    throw new Error("unable to determine size of file at ".concat(this.url));

                  case 6:
                    return t.abrupt("return", this._stat);

                  case 7:
                  case "end":
                    return t.stop();
                }
              }, t, this);
            })), function () {
              return r.apply(this, arguments);
            })
          }]), t;
        }();

        t.exports = h;
      }).call(this, r(19).Buffer);
    }, function (t, e) {
      var r = function (t) {
        function e() {
          this.fetch = !1, this.DOMException = t.DOMException;
        }

        return e.prototype = t, new e();
      }("undefined" != typeof self ? self : this);

      !function (t) {
        !function (e) {
          var r = {
            searchParams: "URLSearchParams" in t,
            iterable: "Symbol" in t && "iterator" in Symbol,
            blob: "FileReader" in t && "Blob" in t && function () {
              try {
                return new Blob(), !0;
              } catch (t) {
                return !1;
              }
            }(),
            formData: "FormData" in t,
            arrayBuffer: "ArrayBuffer" in t
          };
          if (r.arrayBuffer) var n = ["[object Int8Array]", "[object Uint8Array]", "[object Uint8ClampedArray]", "[object Int16Array]", "[object Uint16Array]", "[object Int32Array]", "[object Uint32Array]", "[object Float32Array]", "[object Float64Array]"],
              i = ArrayBuffer.isView || function (t) {
            return t && n.indexOf(Object.prototype.toString.call(t)) > -1;
          };

          function o(t) {
            if ("string" != typeof t && (t = String(t)), /[^a-z0-9\-#$%&'*+.^_`|~]/i.test(t)) throw new TypeError("Invalid character in header field name");
            return t.toLowerCase();
          }

          function a(t) {
            return "string" != typeof t && (t = String(t)), t;
          }

          function s(t) {
            var e = {
              next: function () {
                var e = t.shift();
                return {
                  done: void 0 === e,
                  value: e
                };
              }
            };
            return r.iterable && (e[Symbol.iterator] = function () {
              return e;
            }), e;
          }

          function u(t) {
            this.map = {}, t instanceof u ? t.forEach(function (t, e) {
              this.append(e, t);
            }, this) : Array.isArray(t) ? t.forEach(function (t) {
              this.append(t[0], t[1]);
            }, this) : t && Object.getOwnPropertyNames(t).forEach(function (e) {
              this.append(e, t[e]);
            }, this);
          }

          function f(t) {
            if (t.bodyUsed) return Promise.reject(new TypeError("Already read"));
            t.bodyUsed = !0;
          }

          function c(t) {
            return new Promise(function (e, r) {
              t.onload = function () {
                e(t.result);
              }, t.onerror = function () {
                r(t.error);
              };
            });
          }

          function l(t) {
            var e = new FileReader(),
                r = c(e);
            return e.readAsArrayBuffer(t), r;
          }

          function h(t) {
            if (t.slice) return t.slice(0);
            var e = new Uint8Array(t.byteLength);
            return e.set(new Uint8Array(t)), e.buffer;
          }

          function d() {
            return this.bodyUsed = !1, this._initBody = function (t) {
              var e;
              this._bodyInit = t, t ? "string" == typeof t ? this._bodyText = t : r.blob && Blob.prototype.isPrototypeOf(t) ? this._bodyBlob = t : r.formData && FormData.prototype.isPrototypeOf(t) ? this._bodyFormData = t : r.searchParams && URLSearchParams.prototype.isPrototypeOf(t) ? this._bodyText = t.toString() : r.arrayBuffer && r.blob && (e = t) && DataView.prototype.isPrototypeOf(e) ? (this._bodyArrayBuffer = h(t.buffer), this._bodyInit = new Blob([this._bodyArrayBuffer])) : r.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(t) || i(t)) ? this._bodyArrayBuffer = h(t) : this._bodyText = t = Object.prototype.toString.call(t) : this._bodyText = "", this.headers.get("content-type") || ("string" == typeof t ? this.headers.set("content-type", "text/plain;charset=UTF-8") : this._bodyBlob && this._bodyBlob.type ? this.headers.set("content-type", this._bodyBlob.type) : r.searchParams && URLSearchParams.prototype.isPrototypeOf(t) && this.headers.set("content-type", "application/x-www-form-urlencoded;charset=UTF-8"));
            }, r.blob && (this.blob = function () {
              var t = f(this);
              if (t) return t;
              if (this._bodyBlob) return Promise.resolve(this._bodyBlob);
              if (this._bodyArrayBuffer) return Promise.resolve(new Blob([this._bodyArrayBuffer]));
              if (this._bodyFormData) throw new Error("could not read FormData body as blob");
              return Promise.resolve(new Blob([this._bodyText]));
            }, this.arrayBuffer = function () {
              return this._bodyArrayBuffer ? f(this) || Promise.resolve(this._bodyArrayBuffer) : this.blob().then(l);
            }), this.text = function () {
              var t,
                  e,
                  r,
                  n = f(this);
              if (n) return n;
              if (this._bodyBlob) return t = this._bodyBlob, e = new FileReader(), r = c(e), e.readAsText(t), r;
              if (this._bodyArrayBuffer) return Promise.resolve(function (t) {
                for (var e = new Uint8Array(t), r = new Array(e.length), n = 0; n < e.length; n++) r[n] = String.fromCharCode(e[n]);

                return r.join("");
              }(this._bodyArrayBuffer));
              if (this._bodyFormData) throw new Error("could not read FormData body as text");
              return Promise.resolve(this._bodyText);
            }, r.formData && (this.formData = function () {
              return this.text().then(v);
            }), this.json = function () {
              return this.text().then(JSON.parse);
            }, this;
          }

          u.prototype.append = function (t, e) {
            t = o(t), e = a(e);
            var r = this.map[t];
            this.map[t] = r ? r + ", " + e : e;
          }, u.prototype.delete = function (t) {
            delete this.map[o(t)];
          }, u.prototype.get = function (t) {
            return t = o(t), this.has(t) ? this.map[t] : null;
          }, u.prototype.has = function (t) {
            return this.map.hasOwnProperty(o(t));
          }, u.prototype.set = function (t, e) {
            this.map[o(t)] = a(e);
          }, u.prototype.forEach = function (t, e) {
            for (var r in this.map) this.map.hasOwnProperty(r) && t.call(e, this.map[r], r, this);
          }, u.prototype.keys = function () {
            var t = [];
            return this.forEach(function (e, r) {
              t.push(r);
            }), s(t);
          }, u.prototype.values = function () {
            var t = [];
            return this.forEach(function (e) {
              t.push(e);
            }), s(t);
          }, u.prototype.entries = function () {
            var t = [];
            return this.forEach(function (e, r) {
              t.push([r, e]);
            }), s(t);
          }, r.iterable && (u.prototype[Symbol.iterator] = u.prototype.entries);
          var p = ["DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT"];

          function g(t, e) {
            var r,
                n,
                i = (e = e || {}).body;

            if (t instanceof g) {
              if (t.bodyUsed) throw new TypeError("Already read");
              this.url = t.url, this.credentials = t.credentials, e.headers || (this.headers = new u(t.headers)), this.method = t.method, this.mode = t.mode, this.signal = t.signal, i || null == t._bodyInit || (i = t._bodyInit, t.bodyUsed = !0);
            } else this.url = String(t);

            if (this.credentials = e.credentials || this.credentials || "same-origin", !e.headers && this.headers || (this.headers = new u(e.headers)), this.method = (r = e.method || this.method || "GET", n = r.toUpperCase(), p.indexOf(n) > -1 ? n : r), this.mode = e.mode || this.mode || null, this.signal = e.signal || this.signal, this.referrer = null, ("GET" === this.method || "HEAD" === this.method) && i) throw new TypeError("Body not allowed for GET or HEAD requests");

            this._initBody(i);
          }

          function v(t) {
            var e = new FormData();
            return t.trim().split("&").forEach(function (t) {
              if (t) {
                var r = t.split("="),
                    n = r.shift().replace(/\+/g, " "),
                    i = r.join("=").replace(/\+/g, " ");
                e.append(decodeURIComponent(n), decodeURIComponent(i));
              }
            }), e;
          }

          function y(t, e) {
            e || (e = {}), this.type = "default", this.status = void 0 === e.status ? 200 : e.status, this.ok = this.status >= 200 && this.status < 300, this.statusText = "statusText" in e ? e.statusText : "OK", this.headers = new u(e.headers), this.url = e.url || "", this._initBody(t);
          }

          g.prototype.clone = function () {
            return new g(this, {
              body: this._bodyInit
            });
          }, d.call(g.prototype), d.call(y.prototype), y.prototype.clone = function () {
            return new y(this._bodyInit, {
              status: this.status,
              statusText: this.statusText,
              headers: new u(this.headers),
              url: this.url
            });
          }, y.error = function () {
            var t = new y(null, {
              status: 0,
              statusText: ""
            });
            return t.type = "error", t;
          };
          var m = [301, 302, 303, 307, 308];
          y.redirect = function (t, e) {
            if (-1 === m.indexOf(e)) throw new RangeError("Invalid status code");
            return new y(null, {
              status: e,
              headers: {
                location: t
              }
            });
          }, e.DOMException = t.DOMException;

          try {
            new e.DOMException();
          } catch (t) {
            e.DOMException = function (t, e) {
              this.message = t, this.name = e;
              var r = Error(t);
              this.stack = r.stack;
            }, e.DOMException.prototype = Object.create(Error.prototype), e.DOMException.prototype.constructor = e.DOMException;
          }

          function b(t, n) {
            return new Promise(function (i, o) {
              var a = new g(t, n);
              if (a.signal && a.signal.aborted) return o(new e.DOMException("Aborted", "AbortError"));
              var s = new XMLHttpRequest();

              function f() {
                s.abort();
              }

              s.onload = function () {
                var t,
                    e,
                    r = {
                  status: s.status,
                  statusText: s.statusText,
                  headers: (t = s.getAllResponseHeaders() || "", e = new u(), t.replace(/\r?\n[\t ]+/g, " ").split(/\r?\n/).forEach(function (t) {
                    var r = t.split(":"),
                        n = r.shift().trim();

                    if (n) {
                      var i = r.join(":").trim();
                      e.append(n, i);
                    }
                  }), e)
                };
                r.url = "responseURL" in s ? s.responseURL : r.headers.get("X-Request-URL");
                var n = "response" in s ? s.response : s.responseText;
                i(new y(n, r));
              }, s.onerror = function () {
                o(new TypeError("Network request failed"));
              }, s.ontimeout = function () {
                o(new TypeError("Network request failed"));
              }, s.onabort = function () {
                o(new e.DOMException("Aborted", "AbortError"));
              }, s.open(a.method, a.url, !0), "include" === a.credentials ? s.withCredentials = !0 : "omit" === a.credentials && (s.withCredentials = !1), "responseType" in s && r.blob && (s.responseType = "blob"), a.headers.forEach(function (t, e) {
                s.setRequestHeader(e, t);
              }), a.signal && (a.signal.addEventListener("abort", f), s.onreadystatechange = function () {
                4 === s.readyState && a.signal.removeEventListener("abort", f);
              }), s.send(void 0 === a._bodyInit ? null : a._bodyInit);
            });
          }

          b.polyfill = !0, t.fetch || (t.fetch = b, t.Headers = u, t.Request = g, t.Response = y), e.Headers = u, e.Request = g, e.Response = y, e.fetch = b;
        }({});
      }(r), delete r.fetch.polyfill, (e = r.fetch).default = r.fetch, e.fetch = r.fetch, e.Headers = r.Headers, e.Request = r.Request, e.Response = r.Response, t.exports = e;
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(30)),
          o = n(r(16)),
          a = n(r(76)),
          s = n(r(31)),
          u = n(r(7)),
          f = n(r(10)),
          c = r(94),
          l = function () {
        function t(e) {
          var r = e.fetch,
              n = e.size,
              i = void 0 === n ? 1e7 : n,
              o = e.chunkSize,
              a = void 0 === o ? 32768 : o;
          if ((0, u.default)(this, t), !r) throw new Error("fetch function required");
          this.fetch = r, this.chunkSize = a, this.lruCache = new c({
            maxSize: Math.floor(i / a)
          });
        }

        var e;
        return (0, f.default)(t, [{
          key: "get",
          value: (e = (0, s.default)(i.default.mark(function t(e, r, n, s) {
            var u,
                f,
                c,
                l,
                h,
                d,
                p,
                g = this;
            return i.default.wrap(function (t) {
              for (;;) switch (t.prev = t.next) {
                case 0:
                  if (!(e.length < r + n)) {
                    t.next = 2;
                    break;
                  }

                  throw new Error("output buffer not big enough for request");

                case 2:
                  for (u = Math.floor(s / this.chunkSize), f = Math.floor((s + n) / this.chunkSize), c = new Array(f - u + 1), l = function (t) {
                    c[t - u] = g._getChunk(t).then(function (e) {
                      return {
                        data: e,
                        chunkNumber: t
                      };
                    });
                  }, h = u; h <= f; h += 1) l(h);

                  return t.next = 9, a.default.all(c);

                case 9:
                  d = t.sent, p = s - d[0].chunkNumber * this.chunkSize, (0, o.default)(d).call(d, function (t) {
                    var i = t.data,
                        o = t.chunkNumber,
                        a = o * g.chunkSize,
                        c = 0,
                        l = g.chunkSize,
                        h = r + (o - u) * g.chunkSize - p;
                    o === u && (h = r, c = p), o === f && (l = s + n - a), i.copy(e, h, c, l);
                  });

                case 12:
                case "end":
                  return t.stop();
              }
            }, t, this);
          })), function (t, r, n, i) {
            return e.apply(this, arguments);
          })
        }, {
          key: "_getChunk",
          value: function (t) {
            var e = this.lruCache.get(t);
            if (e) return e;
            var r = this.fetch(t * this.chunkSize, this.chunkSize);
            return this.lruCache.set(t, r), r;
          }
        }]), t;
      }();

      t.exports = l;
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(30)),
          o = n(r(31)),
          a = n(r(7)),
          s = n(r(10)),
          u = (r(161).promisify, null),
          f = u,
          c = function () {
        function t(e) {
          (0, a.default)(this, t), this.position = 0, this.filename = e, this.fd = f(this.filename, "r");
        }

        var e, r, n;
        return (0, s.default)(t, [{
          key: "read",
          value: (n = (0, o.default)(i.default.mark(function t(e) {
            var r,
                n,
                o,
                a = arguments;
            return i.default.wrap(function (t) {
              for (;;) switch (t.prev = t.next) {
                case 0:
                  return r = a.length > 1 && void 0 !== a[1] ? a[1] : 0, n = a.length > 2 ? a[2] : void 0, null === (o = a.length > 3 ? a[3] : void 0) && (this.position, this.position += n), t.t0 = null, t.next = 8, this.fd;

                case 8:
                  return t.t1 = t.sent, t.t2 = e, t.t3 = r, t.t4 = n, t.t5 = o, t.abrupt("return", (0, t.t0)(t.t1, t.t2, t.t3, t.t4, t.t5));

                case 14:
                case "end":
                  return t.stop();
              }
            }, t, this);
          })), function (t) {
            return n.apply(this, arguments);
          })
        }, {
          key: "readFile",
          value: (r = (0, o.default)(i.default.mark(function t() {
            return i.default.wrap(function (t) {
              for (;;) switch (t.prev = t.next) {
                case 0:
                  return t.t0 = null, t.next = 3, this.fd;

                case 3:
                  return t.t1 = t.sent, t.abrupt("return", (0, t.t0)(t.t1));

                case 5:
                case "end":
                  return t.stop();
              }
            }, t, this);
          })), function () {
            return r.apply(this, arguments);
          })
        }, {
          key: "stat",
          value: (e = (0, o.default)(i.default.mark(function t() {
            return i.default.wrap(function (t) {
              for (;;) switch (t.prev = t.next) {
                case 0:
                  if (this._stat) {
                    t.next = 8;
                    break;
                  }

                  return t.t0 = null, t.next = 4, this.fd;

                case 4:
                  return t.t1 = t.sent, t.next = 7, (0, t.t0)(t.t1);

                case 7:
                  this._stat = t.sent;

                case 8:
                  return t.abrupt("return", this._stat);

                case 9:
                case "end":
                  return t.stop();
              }
            }, t, this);
          })), function () {
            return e.apply(this, arguments);
          })
        }]), t;
      }();

      t.exports = c;
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(48)),
          o = n(r(77)),
          a = n(r(101)),
          s = n(r(431)),
          u = n(r(16));
      t.exports = {
        parseHeaderText: function (t) {
          var e = t.split(/\r?\n/),
              r = [];
          return (0, u.default)(e).call(e, function (t) {
            var e = t.split(/\t/),
                n = (0, s.default)(e),
                u = n[0],
                f = (0, a.default)(n).call(n, 1),
                c = (0, o.default)(f).call(f, function (t) {
              var e = t.split(":", 2),
                  r = (0, i.default)(e, 2);
              return {
                tag: r[0],
                value: r[1]
              };
            });
            u && r.push({
              tag: u.substr(1),
              data: c
            });
          }), r;
        }
      };
    }, function (t, e, r) {
      var n = r(157),
          i = r(152),
          o = r(158);

      t.exports = function (t) {
        return n(t) || i(t) || o();
      };
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(30)),
          o = n(r(103)),
          a = n(r(150)),
          s = n(r(48)),
          u = n(r(102)),
          f = n(r(16)),
          c = n(r(59)),
          l = n(r(76)),
          h = n(r(37)),
          d = n(r(77)),
          p = n(r(433)),
          g = n(r(31)),
          v = n(r(7)),
          y = n(r(10)),
          m = r(9),
          b = m.CramUnimplementedError,
          _ = m.CramSizeLimitError,
          w = r(104),
          x = function () {
        function t(e) {
          if ((0, v.default)(this, t), e.cram ? this.cram = e.cram : this.cram = new w({
            url: e.cramUrl,
            path: e.cramPath,
            filehandle: e.cramFilehandle,
            seqFetch: e.seqFetch,
            checkSequenceMD5: e.checkSequenceMD5,
            cacheSize: e.cacheSize
          }), !(this.cram instanceof w)) throw new Error("invalid arguments: no cramfile");
          if (this.index = e.index, !this.index.getEntriesForRange) throw new Error("invalid arguments: not an index");
          this.fetchSizeLimit = e.fetchSizeLimit || 3e6;
        }

        var e;
        return (0, y.default)(t, [{
          key: "getRecordsForRange",
          value: (e = (0, g.default)(i.default.mark(function t(e, r, n) {
            var g,
                v,
                y,
                m,
                w,
                x,
                E,
                S,
                k,
                A,
                C,
                T,
                R,
                O,
                I,
                L,
                B,
                P,
                F,
                N,
                M,
                j,
                z,
                U,
                D,
                q,
                Z,
                H,
                V,
                G,
                Y,
                W,
                K,
                Q,
                X,
                $ = this,
                J = arguments;
            return i.default.wrap(function (t) {
              for (;;) switch (t.prev = t.next) {
                case 0:
                  if ((x = J.length > 3 && void 0 !== J[3] ? J[3] : {}).viewAsPairs = x.viewAsPairs || !1, x.pairAcrossChr = x.pairAcrossChr || !1, x.maxInsertSize = x.maxInsertSize || 2e5, "string" != typeof e) {
                    t.next = 6;
                    break;
                  }

                  throw new b("string sequence names not yet supported");

                case 6:
                  return E = e, t.next = 9, this.index.getEntriesForRange(E, r, n);

                case 9:
                  if (S = t.sent, !((k = (0, p.default)(g = (0, d.default)(S).call(S, function (t) {
                    return t.sliceBytes;
                  })).call(g, function (t, e) {
                    return t + e;
                  }, 0)) > this.fetchSizeLimit)) {
                    t.next = 13;
                    break;
                  }

                  throw new _((0, h.default)(v = "data size of ".concat(k.toLocaleString(), " bytes exceeded fetch size limit of ")).call(v, this.fetchSizeLimit.toLocaleString(), " bytes"));

                case 13:
                  return A = function (t) {
                    return t.sequenceId === e && t.alignmentStart <= n && t.alignmentStart + t.lengthOnRef - 1 >= r;
                  }, t.next = 16, l.default.all((0, d.default)(S).call(S, function (t) {
                    return $.getRecordsInSlice(t, A);
                  }));

                case 16:
                  if (C = t.sent, T = (m = (0, h.default)(y = Array.prototype)).call.apply(m, (0, h.default)(w = [y]).call(w, (0, c.default)(C))), !x.viewAsPairs) {
                    t.next = 42;
                    break;
                  }

                  for (L = {}, B = {}, P = 0; P < T.length; P += 1) F = T[P].readName, N = T[P].uniqueId, L[F] || (L[F] = 0), L[F] += 1, B[N] = 1;

                  for (M = {}, (0, f.default)(R = (0, u.default)(L)).call(R, function (t) {
                    var e = (0, s.default)(t, 2),
                        r = e[0];
                    1 === e[1] && (M[r] = !0);
                  }), j = [], z = 0; z < T.length; z += 1) U = T[z].readName, M[U] && T[z].mate && (T[z].mate.sequenceId === E || x.pairAcrossChr) && Math.abs(T[z].alignmentStart - T[z].mate.alignmentStart) < x.maxInsertSize && (D = this.index.getEntriesForRange(T[z].mate.sequenceId, T[z].mate.alignmentStart, T[z].mate.alignmentStart + 1), j.push(D));

                  return t.next = 28, l.default.all(j);

                case 28:
                  for (q = t.sent, Z = [], H = 0; H < q.length; H += 1) (V = Z).push.apply(V, (0, c.default)(q[H]));

                  if (Z = (0, a.default)(O = (0, o.default)(Z).call(Z, function (t, e) {
                    return t.toString().localeCompare(e.toString());
                  })).call(O, function (t, e, r) {
                    return !e || t.toString() !== r[e - 1].toString();
                  }), G = [], Y = [], !((W = (0, p.default)(I = (0, d.default)(Z).call(Z, function (t) {
                    return t.sliceBytes;
                  })).call(I, function (t, e) {
                    return t + e;
                  }, 0)) > this.fetchSizeLimit)) {
                    t.next = 37;
                    break;
                  }

                  throw new Error((0, h.default)(K = "mate data size of ".concat(W.toLocaleString(), " bytes exceeded fetch size limit of ")).call(K, this.fetchSizeLimit.toLocaleString(), " bytes"));

                case 37:
                  return (0, f.default)(Z).call(Z, function (t) {
                    var e = $.cram.featureCache.get(t.toString());
                    e || (e = $.getRecordsInSlice(t, function () {
                      return !0;
                    }), $.cram.featureCache.set(t.toString(), e)), G.push(e);
                    var r = e.then(function (t) {
                      for (var e = [], r = 0; r < t.length; r += 1) {
                        var n = t[r];
                        M[n.readName] && !B[n.uniqueId] && e.push(n);
                      }

                      return e;
                    });
                    Y.push(r);
                  }), t.next = 40, l.default.all(Y);

                case 40:
                  (Q = t.sent).length && (X = (0, p.default)(Q).call(Q, function (t, e) {
                    return (0, h.default)(t).call(t, e);
                  }), T = (0, h.default)(T).call(T, X));

                case 42:
                  return t.abrupt("return", T);

                case 43:
                case "end":
                  return t.stop();
              }
            }, t, this);
          })), function (t, r, n) {
            return e.apply(this, arguments);
          })
        }, {
          key: "getRecordsInSlice",
          value: function (t, e) {
            var r = t.containerStart,
                n = t.sliceStart,
                i = t.sliceBytes;
            return this.cram.getContainerAtPosition(r).getSlice(n, i).getRecords(e);
          }
        }, {
          key: "hasDataForReferenceSequence",
          value: function (t) {
            return this.index.hasDataForReferenceSequence(t);
          }
        }]), t;
      }();

      t.exports = x;
    }, function (t, e, r) {
      t.exports = r(434);
    }, function (t, e, r) {
      t.exports = r(435);
    }, function (t, e, r) {
      var n = r(436),
          i = Array.prototype;

      t.exports = function (t) {
        var e = t.reduce;
        return t === i || t instanceof Array && e === i.reduce ? n : e;
      };
    }, function (t, e, r) {
      r(437);
      var n = r(18);
      t.exports = n("Array").reduce;
    }, function (t, e, r) {

      var n = r(0),
          i = r(438).left;
      n({
        target: "Array",
        proto: !0,
        forced: r(54)("reduce")
      }, {
        reduce: function (t) {
          return i(this, t, arguments.length, arguments.length > 1 ? arguments[1] : void 0);
        }
      });
    }, function (t, e, r) {
      var n = r(5),
          i = r(22),
          o = r(60),
          a = r(29),
          s = function (t) {
        return function (e, r, s, u) {
          n(r);
          var f = i(e),
              c = o(f),
              l = a(f.length),
              h = t ? l - 1 : 0,
              d = t ? -1 : 1;
          if (s < 2) for (;;) {
            if (h in c) {
              u = c[h], h += d;
              break;
            }

            if (h += d, t ? h < 0 : l <= h) throw TypeError("Reduce of empty array with no initial value");
          }

          for (; t ? h >= 0 : l > h; h += d) h in c && (u = r(u, c[h], h, f));

          return u;
        };
      };

      t.exports = {
        left: s(!1),
        right: s(!0)
      };
    }, function (t, e, r) {

      var n = r(1),
          i = n(r(30)),
          o = n(r(31)),
          a = n(r(103)),
          s = n(r(102)),
          u = n(r(16)),
          f = n(r(154)),
          c = n(r(440)),
          l = n(r(48)),
          h = n(r(442)),
          d = n(r(37)),
          p = n(r(75)),
          g = n(r(7)),
          v = n(r(10)),
          y = r(447).default,
          m = r(94),
          b = (0, r(161).promisify)(r(126).gunzip),
          _ = r(160).open,
          w = r(9).CramMalformedError,
          x = function () {
        function t(e) {
          (0, g.default)(this, t), (0, p.default)(this, e);
        }

        return (0, v.default)(t, [{
          key: "toString",
          value: function () {
            var t, e, r, n;
            return (0, d.default)(t = (0, d.default)(e = (0, d.default)(r = (0, d.default)(n = "".concat(this.start, ":")).call(n, this.span, ":")).call(r, this.containerStart, ":")).call(e, this.sliceStart, ":")).call(t, this.sliceBytes);
          }
        }]), t;
      }();

      function E(t, e) {
        if ((0, h.default)(e).call(e, function (t) {
          return void 0 === t;
        })) throw new w("invalid .crai index file");
        var r = (0, l.default)(e, 6),
            n = r[0],
            i = r[1],
            o = r[2],
            a = r[3],
            s = r[4],
            u = r[5];
        t[n] || (t[n] = []), t[n].push(new x({
          start: i,
          span: o,
          containerStart: a,
          sliceStart: s,
          sliceBytes: u
        }));
      }

      var S = function () {
        function t(e) {
          var r,
              n = this;
          (0, g.default)(this, t);

          var i = _(e.url, e.path, e.filehandle);

          this._parseCache = new y({
            cache: new m({
              maxSize: 1
            }),
            fill: function (t, e) {
              return n.parseIndex({
                signal: e
              });
            }
          }), this.readFile = (0, c.default)(r = i.readFile).call(r, i);
        }

        var e, r;
        return (0, v.default)(t, [{
          key: "parseIndex",
          value: function () {
            var t = {};
            return this.readFile().then(function (t) {
              return 31 === t[0] && 139 === t[1] ? b(t) : t;
            }).then(function (e) {
              var r;
              if (e.length > 4 && 21578050 === e.readUInt32LE(0)) throw new w("invalid .crai index file. note: file appears to be a .bai index. this is technically legal but please open a github issue if you need support");

              for (var n = [], i = "", o = 0; o < e.length; o += 1) {
                var c = e[o];
                if (c >= 48 && c <= 57 || !i && 45 === c) i += String.fromCharCode(c);else if (9 === c) n.push((0, f.default)(i, 10)), i = "";else if (10 === c) n.push((0, f.default)(i, 10)), i = "", E(t, n), n = [];else if (13 !== c && 32 !== c) throw new w("invalid .crai index file");
              }

              return i && n.push((0, f.default)(i, 10)), 6 === n.length && E(t, n), (0, u.default)(r = (0, s.default)(t)).call(r, function (e) {
                var r = (0, l.default)(e, 2),
                    n = r[0],
                    i = r[1];
                t[n] = (0, a.default)(i).call(i, function (t, e) {
                  return t.start - e.start || t.span - e.span;
                });
              }), t;
            });
          }
        }, {
          key: "getIndex",
          value: function () {
            var t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {};
            return this._parseCache.get("index", null, t.signal);
          }
        }, {
          key: "hasDataForReferenceSequence",
          value: (r = (0, o.default)(i.default.mark(function t(e) {
            return i.default.wrap(function (t) {
              for (;;) switch (t.prev = t.next) {
                case 0:
                  return t.next = 2, this.getIndex();

                case 2:
                  return t.t0 = e, t.abrupt("return", !!t.sent[t.t0]);

                case 4:
                case "end":
                  return t.stop();
              }
            }, t, this);
          })), function (t) {
            return r.apply(this, arguments);
          })
        }, {
          key: "getEntriesForRange",
          value: (e = (0, o.default)(i.default.mark(function t(e, r, n) {
            var o, a, s, u;
            return i.default.wrap(function (t) {
              for (;;) switch (t.prev = t.next) {
                case 0:
                  return t.next = 2, this.getIndex();

                case 2:
                  if (t.t0 = e, o = t.sent[t.t0]) {
                    t.next = 6;
                    break;
                  }

                  return t.abrupt("return", []);

                case 6:
                  for (a = function (t) {
                    var e = t.start,
                        i = t.start + t.span;
                    return e >= n ? -1 : i <= r ? 1 : 0;
                  }, s = [], u = 0; u < o.length; u += 1) 0 === a(o[u]) && s.push(o[u]);

                  return t.abrupt("return", s);

                case 10:
                case "end":
                  return t.stop();
              }
            }, t, this);
          })), function (t, r, n) {
            return e.apply(this, arguments);
          })
        }]), t;
      }();

      t.exports = S;
    }, function (t, e, r) {
      t.exports = r(441);
    }, function (t, e, r) {
      t.exports = r(144);
    }, function (t, e, r) {
      t.exports = r(443);
    }, function (t, e, r) {
      t.exports = r(444);
    }, function (t, e, r) {
      var n = r(445),
          i = Array.prototype;

      t.exports = function (t) {
        var e = t.some;
        return t === i || t instanceof Array && e === i.some ? n : e;
      };
    }, function (t, e, r) {
      r(446);
      var n = r(18);
      t.exports = n("Array").some;
    }, function (t, e, r) {

      var n = r(0),
          i = r(45).some;
      n({
        target: "Array",
        proto: !0,
        forced: r(54)("some")
      }, {
        some: function (t) {
          return i(this, t, arguments.length > 1 ? arguments[1] : void 0);
        }
      });
    }, function (t, e, r) {

      var n = this && this.__importDefault || function (t) {
        return t && t.__esModule ? t : {
          default: t
        };
      };

      Object.defineProperty(e, "__esModule", {
        value: !0
      });
      var i = n(r(448));
      e.default = i.default;
    }, function (t, e, r) {

      var n = this && this.__importDefault || function (t) {
        return t && t.__esModule ? t : {
          default: t
        };
      };

      Object.defineProperty(e, "__esModule", {
        value: !0
      });

      var i = r(162),
          o = n(r(450)),
          a = function () {
        function t(t) {
          var e = t.fill,
              r = t.cache;
          if ("function" != typeof e) throw new TypeError("must pass a fill function");
          if ("object" != typeof r) throw new TypeError("must pass a cache object");
          if ("function" != typeof r.get || "function" != typeof r.set || "function" != typeof r.delete) throw new TypeError("cache must implement get(key), set(key, val), and and delete(key)");
          this.cache = r, this.fillCallback = e;
        }

        return t.isAbortException = function (t) {
          return "AbortError" === t.name || "ERR_ABORTED" === t.code || "AbortError: aborted" === t.message || "Error: aborted" === t.message;
        }, t.prototype.evict = function (t, e) {
          this.cache.get(t) === e && this.cache.delete(t);
        }, t.prototype.fill = function (t, e, r) {
          var n = this,
              i = new o.default(),
              a = {
            aborter: i,
            promise: this.fillCallback(e, i.signal),
            settled: !1,

            get aborted() {
              return this.aborter.signal.aborted;
            }

          };
          a.aborter.addSignal(r), a.aborter.signal.addEventListener("abort", function () {
            n.evict(t, a);
          }), a.promise.then(function () {
            a.settled = !0;
          }, function (e) {
            a.settled = !0, n.evict(t, a);
          }).catch(function (t) {
            throw console.error(t), t;
          }), this.cache.set(t, a);
        }, t.checkSinglePromise = function (t, e) {
          function r() {
            if (e && e.aborted) throw Object.assign(new Error("aborted"), {
              code: "ERR_ABORTED"
            });
          }

          return t.then(function (t) {
            return r(), t;
          }, function (t) {
            throw r(), t;
          });
        }, t.prototype.has = function (t) {
          return this.cache.has(t);
        }, t.prototype.get = function (e, r, n) {
          if (!n && r instanceof i.AbortSignal) throw new TypeError("second get argument appears to be an AbortSignal, perhaps you meant to pass `null` for the fill data?");
          var o = this.cache.get(e);
          return o ? o.aborted ? (this.evict(e, o), this.get(e, r, n)) : o.settled ? o.promise : (o.aborter.addSignal(n), t.checkSinglePromise(o.promise, n)) : (this.fill(e, r, n), t.checkSinglePromise(this.cache.get(e).promise, n));
        }, t.prototype.delete = function (t) {
          var e = this.cache.get(t);
          e && (e.settled || e.aborter.abort(), this.cache.delete(t));
        }, t.prototype.clear = function () {
          for (var t = this.cache.keys(), e = 0, r = t.next(); !r.done; r = t.next()) this.delete(r.value), e += 1;

          return e;
        }, t;
      }();

      e.default = a;
    }, function (t, e, r) {

      function n(t, e) {
        if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function");
      }

      function i(t, e) {
        for (var r = 0; r < e.length; r++) {
          var n = e[r];
          n.enumerable = n.enumerable || !1, n.configurable = !0, "value" in n && (n.writable = !0), Object.defineProperty(t, n.key, n);
        }
      }

      function o(t, e, r) {
        return e && i(t.prototype, e), r && i(t, r), t;
      }

      function a(t) {
        return (a = Object.setPrototypeOf ? Object.getPrototypeOf : function (t) {
          return t.__proto__ || Object.getPrototypeOf(t);
        })(t);
      }

      function s(t, e) {
        return (s = Object.setPrototypeOf || function (t, e) {
          return t.__proto__ = e, t;
        })(t, e);
      }

      function u(t) {
        if (void 0 === t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
        return t;
      }

      function f(t, e, r) {
        return (f = "undefined" != typeof Reflect && Reflect.get ? Reflect.get : function (t, e, r) {
          var n = function (t, e) {
            for (; !Object.prototype.hasOwnProperty.call(t, e) && null !== (t = a(t)););

            return t;
          }(t, e);

          if (n) {
            var i = Object.getOwnPropertyDescriptor(n, e);
            return i.get ? i.get.call(r) : i.value;
          }
        })(t, e, r || t);
      }

      Object.defineProperty(e, "__esModule", {
        value: !0
      });

      var c = function () {
        function t() {
          n(this, t), Object.defineProperty(this, "listeners", {
            value: {},
            writable: !0,
            configurable: !0
          });
        }

        return o(t, [{
          key: "addEventListener",
          value: function (t, e) {
            t in this.listeners || (this.listeners[t] = []), this.listeners[t].push(e);
          }
        }, {
          key: "removeEventListener",
          value: function (t, e) {
            if (t in this.listeners) for (var r = this.listeners[t], n = 0, i = r.length; n < i; n++) if (r[n] === e) return void r.splice(n, 1);
          }
        }, {
          key: "dispatchEvent",
          value: function (t) {
            var e = this;

            if (t.type in this.listeners) {
              for (var r = function (r) {
                setTimeout(function () {
                  return r.call(e, t);
                });
              }, n = this.listeners[t.type], i = 0, o = n.length; i < o; i++) r(n[i]);

              return !t.defaultPrevented;
            }
          }
        }]), t;
      }(),
          l = function (t) {
        function e() {
          var t;
          return n(this, e), (t = function (t, e) {
            return !e || "object" != typeof e && "function" != typeof e ? u(t) : e;
          }(this, a(e).call(this))).listeners || c.call(u(t)), Object.defineProperty(u(t), "aborted", {
            value: !1,
            writable: !0,
            configurable: !0
          }), Object.defineProperty(u(t), "onabort", {
            value: null,
            writable: !0,
            configurable: !0
          }), t;
        }

        return function (t, e) {
          if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function");
          t.prototype = Object.create(e && e.prototype, {
            constructor: {
              value: t,
              writable: !0,
              configurable: !0
            }
          }), e && s(t, e);
        }(e, t), o(e, [{
          key: "toString",
          value: function () {
            return "[object AbortSignal]";
          }
        }, {
          key: "dispatchEvent",
          value: function (t) {
            "abort" === t.type && (this.aborted = !0, "function" == typeof this.onabort && this.onabort.call(this, t)), f(a(e.prototype), "dispatchEvent", this).call(this, t);
          }
        }]), e;
      }(c),
          h = function () {
        function t() {
          n(this, t), Object.defineProperty(this, "signal", {
            value: new l(),
            writable: !0,
            configurable: !0
          });
        }

        return o(t, [{
          key: "abort",
          value: function () {
            var t;

            try {
              t = new Event("abort");
            } catch (e) {
              "undefined" != typeof document ? document.createEvent ? (t = document.createEvent("Event")).initEvent("abort", !1, !1) : (t = document.createEventObject()).type = "abort" : t = {
                type: "abort",
                bubbles: !1,
                cancelable: !1
              };
            }

            this.signal.dispatchEvent(t);
          }
        }, {
          key: "toString",
          value: function () {
            return "[object AbortController]";
          }
        }]), t;
      }();

      "undefined" != typeof Symbol && Symbol.toStringTag && (h.prototype[Symbol.toStringTag] = "AbortController", l.prototype[Symbol.toStringTag] = "AbortSignal"), e.AbortController = h, e.AbortSignal = l, e.abortableFetch = function (t) {
        "function" == typeof t && (t = {
          fetch: t
        });
        var e = t,
            r = e.fetch,
            n = e.Request,
            i = void 0 === n ? r.Request : n,
            o = e.AbortController,
            a = e.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL,
            s = void 0 !== a && a;
        if (!function (t) {
          return t.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL ? (console.log("__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL=true is set, will force install polyfill"), !0) : "function" == typeof t.Request && !t.Request.prototype.hasOwnProperty("signal") || !t.AbortController;
        }({
          fetch: r,
          Request: i,
          AbortController: o,
          __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL: s
        })) return {
          fetch: r,
          Request: u
        };
        var u = i;
        (u && !u.prototype.hasOwnProperty("signal") || s) && ((u = function (t, e) {
          var r;
          e && e.signal && (r = e.signal, delete e.signal);
          var n = new i(t, e);
          return r && Object.defineProperty(n, "signal", {
            writable: !1,
            enumerable: !1,
            configurable: !0,
            value: r
          }), n;
        }).prototype = i.prototype);
        var f = r;
        return {
          fetch: function (t, e) {
            var r = u && u.prototype.isPrototypeOf(t) ? t.signal : e ? e.signal : void 0;

            if (r) {
              var n;

              try {
                n = new DOMException("Aborted", "AbortError");
              } catch (t) {
                (n = new Error("Aborted")).name = "AbortError";
              }

              if (r.aborted) return Promise.reject(n);
              var i = new Promise(function (t, e) {
                r.addEventListener("abort", function () {
                  return e(n);
                }, {
                  once: !0
                });
              });
              return e && e.signal && delete e.signal, Promise.race([i, f(t, e)]);
            }

            return f(t, e);
          },
          Request: u
        };
      };
    }, function (t, e, r) {

      Object.defineProperty(e, "__esModule", {
        value: !0
      });

      var n = r(162),
          i = function () {},
          o = function () {
        function t() {
          this.signals = new Set(), this.abortController = new n.AbortController();
        }

        return t.prototype.addSignal = function (t) {
          var e = this;
          if (void 0 === t && (t = new i()), this.signal.aborted) throw new Error("cannot add a signal, already aborted!");
          this.signals.add(t), t.aborted ? this.handleAborted(t) : "function" == typeof t.addEventListener && t.addEventListener("abort", function () {
            e.handleAborted(t);
          });
        }, t.prototype.handleAborted = function (t) {
          this.signals.delete(t), 0 === this.signals.size && this.abortController.abort();
        }, Object.defineProperty(t.prototype, "signal", {
          get: function () {
            return this.abortController.signal;
          },
          enumerable: !0,
          configurable: !0
        }), t.prototype.abort = function () {
          this.abortController.abort();
        }, t;
      }();

      e.default = o;
    }]);

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2018 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const READ_STRAND_FLAG = 0x10;
    const MATE_STRAND_FLAG = 0x20;
    const CRAM_MATE_STRAND_FLAG = 0x1;
    /**
     * Class for reading a cram file.  Wraps the gMOD Cram package.
     *
     * @param config
     * @constructor
     */

    class CramReader {
      constructor(config, genome, browser) {
        this.config = config;
        this.browser = browser;
        this.genome = genome;
        this.cramFile = new gmodCRAM.CramFile({
          filehandle: new FileHandler(config.url, config),
          seqFetch: config.seqFetch || seqFetch.bind(this),
          checkSequenceMD5: config.checkSequenceMD5 !== undefined ? config.checkSequenceMD5 : true
        });
        const indexFileHandle = new FileHandler(config.indexURL, config);
        this.indexedCramFile = new gmodCRAM.IndexedCramFile({
          cram: this.cramFile,
          index: new gmodCRAM.CraiIndex({
            filehandle: indexFileHandle
          }),
          fetchSizeLimit: 30000000
        });
        BamUtils.setReaderDefaults(this, config);

        function seqFetch(seqID, start, end) {
          const sequence = this.genome.sequence;
          const genome = this.genome;
          return this.getHeader().then(function (header) {
            const chr = genome.getChromosomeName(header.chrNames[seqID]);
            return sequence.getSequence(chr, start - 1, end);
          });
        }
      }
      /**
       * Parse the sequence dictionary from the SAM header and build chr name tables.  This function
       * is public so it can be unit tested.
       *
       * @returns {PromiseLike<chrName, chrToIndex, chrAliasTable}>}
       */


      async getHeader() {
        if (!this.header) {
          const genome = this.genome;
          const samHeader = await this.cramFile.getSamHeader();
          const chrToIndex = {};
          const chrNames = [];
          const chrAliasTable = {};
          const readGroups = [];

          for (let line of samHeader) {
            if ('SQ' === line.tag) {
              for (let d of line.data) {
                if (d.tag === "SN") {
                  const seq = d.value;
                  chrToIndex[seq] = chrNames.length;
                  chrNames.push(seq);

                  if (genome) {
                    const alias = genome.getChromosomeName(seq);
                    chrAliasTable[alias] = seq;
                  }

                  break;
                }
              }
            } else if ('RG' === line.tag) {
              readGroups.push(line.data);
            }
          }

          this.header = {
            chrNames: chrNames,
            chrToIndex: chrToIndex,
            chrAliasTable: chrAliasTable,
            readGroups: readGroups
          };
        }

        return this.header;
      }

      async readAlignments(chr, bpStart, bpEnd) {
        this.browser;
        const header = await this.getHeader();
        const queryChr = header.chrAliasTable.hasOwnProperty(chr) ? header.chrAliasTable[chr] : chr;
        const chrIdx = header.chrToIndex[queryChr];
        const alignmentContainer = new AlignmentContainer(chr, bpStart, bpEnd, this.config);

        if (chrIdx === undefined) {
          return alignmentContainer;
        } else {
          try {
            const records = await this.indexedCramFile.getRecordsForRange(chrIdx, bpStart, bpEnd);

            for (let record of records) {
              const refID = record.sequenceId;
              const pos = record.alignmentStart;
              const alignmentEnd = pos + record.lengthOnRef;

              if (refID < 0) {
                continue; // unmapped read
              } else if (refID > chrIdx || pos > bpEnd) {
                return; // off right edge, we're done
              } else if (refID < chrIdx) {
                continue; // Sequence to left of start, not sure this is possible
              }

              if (alignmentEnd < bpStart) {
                continue;
              } // Record out-of-range "to the left", skip to next one


              const alignment = decodeCramRecord(record, header.chrNames);

              if (this.filter.pass(alignment)) {
                alignmentContainer.push(alignment);
              }
            }

            alignmentContainer.finish();
            return alignmentContainer;
          } catch (error) {
            let message = error.message;

            if (message && message.indexOf("MD5") >= 0) {
              message = "Sequence mismatch. Is this the correct genome for the loaded CRAM?";
            }

            Alert.presentAlert(new Error(message));
            throw error;
          }
        }

        function decodeCramRecord(record, chrNames) {
          const alignment = new BamAlignment();
          alignment.chr = chrNames[record.sequenceId];
          alignment.start = record.alignmentStart - 1;
          alignment.lengthOnRef = record.lengthOnRef;
          alignment.flags = record.flags;
          alignment.strand = !(record.flags & READ_STRAND_FLAG);
          alignment.fragmentLength = record.templateLength || record.templateSize;
          alignment.mq = record.mappingQuality;
          alignment.end = record.alignmentStart + record.lengthOnRef;
          alignment.readGroupId = record.readGroupId;

          if (record.mate && record.mate.sequenceId !== undefined) {
            const strand = record.mate.flags !== undefined ? !(record.mate.flags & CRAM_MATE_STRAND_FLAG) : !(record.flags & MATE_STRAND_FLAG);
            alignment.mate = {
              chr: chrNames[record.mate.sequenceId],
              position: record.mate.alignmentStart,
              strand: strand
            };
          }

          alignment.seq = record.getReadBases();
          alignment.qual = record.qualityScores;
          alignment.tagDict = record.tags;
          alignment.readName = record.readName; // TODO -- cigar encoded in tag?
          // BamUtils.bam_tag2cigar(ba, blockEnd, p, lseq, alignment, cigarArray);

          makeBlocks(record, alignment);

          if (alignment.mate && alignment.start > alignment.mate.position && alignment.fragmentLength > 0) {
            alignment.fragmentLength = -alignment.fragmentLength;
          }

          BamUtils.setPairOrientation(alignment);
          return alignment;
        }

        function makeBlocks(cramRecord, alignment) {
          const blocks = [];
          let insertions;
          let gaps;
          let basesUsed = 0;
          let cigarString = '';
          alignment.scStart = alignment.start;
          alignment.scLengthOnRef = alignment.lengthOnRef;

          if (cramRecord.readFeatures) {
            for (let feature of cramRecord.readFeatures) {
              const code = feature.code;
              const data = feature.data;
              const readPos = feature.pos - 1;
              const refPos = feature.refPos - 1;

              switch (code) {
                case 'S':
                case 'I':
                case 'i':
                case 'N':
                case 'D':
                  if (readPos > basesUsed) {
                    const len = readPos - basesUsed;
                    blocks.push(new AlignmentBlock({
                      start: refPos - len,
                      seqOffset: basesUsed,
                      len: len,
                      type: 'M'
                    }));
                    basesUsed += len;
                    cigarString += len + 'M';
                  }

                  if ('S' === code) {
                    let scPos = refPos;
                    alignment.scLengthOnRef += data.length;

                    if (readPos === 0) {
                      alignment.scStart -= data.length;
                      scPos -= data.length;
                    }

                    const len = data.length;
                    blocks.push(new AlignmentBlock({
                      start: scPos,
                      seqOffset: basesUsed,
                      len: len,
                      type: 'S'
                    }));
                    basesUsed += len;
                    cigarString += len + code;
                  } else if ('I' === code || 'i' === code) {
                    if (insertions === undefined) {
                      insertions = [];
                    }

                    const len = 'i' === code ? 1 : data.length;
                    insertions.push(new AlignmentBlock({
                      start: refPos,
                      len: len,
                      seqOffset: basesUsed,
                      type: 'I'
                    }));
                    basesUsed += len;
                    cigarString += len + code;
                  } else if ('D' === code || 'N' === code) {
                    if (!gaps) {
                      gaps = [];
                    }

                    gaps.push({
                      start: refPos,
                      len: data,
                      type: code
                    });
                    cigarString += data + code;
                  }

                  break;

                case 'H':
                case 'P':
                  cigarString += data + code;
                  break;

              }
            }
          } // Last block


          const len = cramRecord.readLength - basesUsed;

          if (len > 0) {
            blocks.push(new AlignmentBlock({
              start: cramRecord.alignmentStart + cramRecord.lengthOnRef - len - 1,
              seqOffset: basesUsed,
              len: len,
              type: 'M'
            }));
            cigarString += len + 'M';
          }

          alignment.blocks = blocks;
          alignment.insertions = insertions;
          alignment.gaps = gaps;
          alignment.cigar = cigarString;
        }
      }

    }

    class FileHandler {
      constructor(source, config) {
        this.position = 0;
        this.url = source;
        this.config = config;
        this.cache = new BufferCache({
          fetch: (start, length) => this._fetch(start, length)
        });
      }

      async _fetch(position, length) {
        const loadRange = {
          start: position,
          size: length
        };
        this._stat = {
          size: undefined
        };
        const arrayBuffer = await igvxhr.loadArrayBuffer(this.url, buildOptions(this.config, {
          range: loadRange
        }));
        return Buffer.from(arrayBuffer);
      }

      async read(buffer) {
        let offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
        let length = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : Infinity;
        let position = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
        let readPosition = position;

        if (readPosition === null) {
          readPosition = this.position;
          this.position += length;
        }

        return this.cache.get(buffer, offset, length, position);
      }

      async readFile() {
        const arrayBuffer = await igvxhr.loadArrayBuffer(this.url, buildOptions(this.config));
        return Buffer.from(arrayBuffer);
      }

      async stat() {
        if (!this._stat) {
          const buf = Buffer.allocUnsafe(10);
          await this.read(buf, 0, 10, 0);
          if (!this._stat) throw new Error(`unable to determine size of file at ${this.url}`);
        }

        return this._stat;
      }

    }

    class BufferCache {
      constructor(_ref) {
        let {
          fetch,
          size = 10000000,
          chunkSize = 32768
        } = _ref;
        this.fetch = fetch;
        this.chunkSize = chunkSize;
        this.lruCache = new QuickLRU({
          maxSize: Math.floor(size / chunkSize)
        });
      }

      async get(outputBuffer, offset, length, position) {
        if (outputBuffer.length < offset + length) throw new Error('output buffer not big enough for request'); // calculate the list of chunks involved in this fetch

        const firstChunk = Math.floor(position / this.chunkSize);
        const lastChunk = Math.floor((position + length) / this.chunkSize); // fetch them all as necessary

        const fetches = new Array(lastChunk - firstChunk + 1);

        for (let chunk = firstChunk; chunk <= lastChunk; chunk += 1) {
          fetches[chunk - firstChunk] = this._getChunk(chunk).then(data => ({
            data,
            chunkNumber: chunk
          }));
        } // stitch together the response buffer using them


        const chunks = await Promise.all(fetches);
        const chunksOffset = position - chunks[0].chunkNumber * this.chunkSize;
        chunks.forEach(_ref2 => {
          let {
            data,
            chunkNumber
          } = _ref2;
          const chunkPositionStart = chunkNumber * this.chunkSize;
          let copyStart = 0;
          let copyEnd = this.chunkSize;
          let copyOffset = offset + (chunkNumber - firstChunk) * this.chunkSize - chunksOffset;

          if (chunkNumber === firstChunk) {
            copyOffset = offset;
            copyStart = chunksOffset;
          }

          if (chunkNumber === lastChunk) {
            copyEnd = position + length - chunkPositionStart;
          }

          data.copy(outputBuffer, copyOffset, copyStart, copyEnd);
        });
      }

      _getChunk(chunkNumber) {
        const cachedPromise = this.lruCache.get(chunkNumber);
        if (cachedPromise) return cachedPromise;
        const freshPromise = this.fetch(chunkNumber * this.chunkSize, this.chunkSize);
        this.lruCache.set(chunkNumber, freshPromise);
        return freshPromise;
      }

    } // From https://github.com/sindresorhus/quick-lru

    class QuickLRU {
      constructor() {
        let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

        if (!(options.maxSize && options.maxSize > 0)) {
          throw new TypeError('`maxSize` must be a number greater than 0');
        }

        this.maxSize = options.maxSize;
        this.cache = new Map();
        this.oldCache = new Map();
        this._size = 0;
      }

      _set(key, value) {
        this.cache.set(key, value);
        this._size++;

        if (this._size >= this.maxSize) {
          this._size = 0;
          this.oldCache = this.cache;
          this.cache = new Map();
        }
      }

      get(key) {
        if (this.cache.has(key)) {
          return this.cache.get(key);
        }

        if (this.oldCache.has(key)) {
          const value = this.oldCache.get(key);

          this._set(key, value);

          return value;
        }
      }

      set(key, value) {
        if (this.cache.has(key)) {
          this.cache.set(key, value);
        } else {
          this._set(key, value);
        }

        return this;
      }

      has(key) {
        return this.cache.has(key) || this.oldCache.has(key);
      }

      peek(key) {
        if (this.cache.has(key)) {
          return this.cache.get(key);
        }

        if (this.oldCache.has(key)) {
          return this.oldCache.get(key);
        }
      }

      delete(key) {
        const deleted = this.cache.delete(key);

        if (deleted) {
          this._size--;
        }

        return this.oldCache.delete(key) || deleted;
      }

      clear() {
        this.cache.clear();
        this.oldCache.clear();
        this._size = 0;
      }

      *keys() {
        for (const [key] of this) {
          yield key;
        }
      }

      *values() {
        for (const [, value] of this) {
          yield value;
        }
      }

      *[Symbol.iterator]() {
        for (const item of this.cache) {
          yield item;
        }

        for (const item of this.oldCache) {
          const [key] = item;

          if (!this.cache.has(key)) {
            yield item;
          }
        }
      }

      get size() {
        let oldCacheSize = 0;

        for (const key of this.oldCache.keys()) {
          if (!this.cache.has(key)) {
            oldCacheSize++;
          }
        }

        return this._size + oldCacheSize;
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const apiKey = igvxhr.apiKey;

    function ga4ghGet(options) {
      var url = options.url + "/" + options.entity + "/" + options.entityId;
      options.headers = ga4ghHeaders();
      return igvxhr.loadJson(url, options); // Returns a promise
    }

    function ga4ghSearch(options) {
      return new Promise(function (fulfill, reject) {
        var results = options.results ? options.results : [],
            url = options.url,
            body = options.body,
            decode = options.decode,
            paramSeparator = "?",
            fields = options.fields; // Partial response

        if (apiKey) {
          url = url + paramSeparator + "key=" + apiKey;
          paramSeparator = "&";
        }

        if (fields) {
          url = url + paramSeparator + "fields=" + fields;
        } // Start the recursive load cycle.  Data is fetched in chunks, if more data is available a "nextPageToken" is returned.


        return loadChunk();

        function loadChunk(pageToken) {
          if (pageToken) {
            body.pageToken = pageToken;
          } else {
            if (body.pageToken !== undefined) delete body.pageToken; // Remove previous page token, if any
          }

          var sendData = JSON.stringify(body);
          igvxhr.loadJson(url, {
            sendData: sendData,
            contentType: "application/json",
            headers: ga4ghHeaders() //    oauthToken: ga4ghToken()

          }).then(function (json) {
            var nextPageToken, tmp;

            if (json) {
              tmp = decode ? decode(json) : json;

              if (tmp) {
                tmp.forEach(function (a) {

                  {
                    results.push(a);
                  }
                });
              }

              nextPageToken = json["nextPageToken"];

              if (nextPageToken) {
                loadChunk(nextPageToken);
              } else {
                fulfill(results);
              }
            } else {
              fulfill(results);
            }
          }).catch(function (error) {
            reject(error);
          });
        }
      });
    }

    function ga4ghHeaders() {
      return {
        "Cache-Control": "no-cache"
      };
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    var CigarOperationTable = {
      "ALIGNMENT_MATCH": "M",
      "INSERT": "I",
      "DELETE": "D",
      "SKIP": "N",
      "CLIP_SOFT": "S",
      "CLIP_HARD": "H",
      "PAD": "P",
      "SEQUENCE_MATCH": "=",
      "SEQUENCE_MISMATCH": "X"
    };

    const Ga4ghAlignmentReader = function (config, genome) {
      this.config = config;
      this.genome = genome;
      this.url = config.url;
      this.filter = new BamFilter(config.filter);
      this.readGroupSetIds = config.readGroupSetIds;
      this.authKey = config.authKey; // Might be undefined or nill

      this.samplingWindowSize = config.samplingWindowSize === undefined ? 100 : config.samplingWindowSize;
      this.samplingDepth = config.samplingDepth === undefined ? 1000 : config.samplingDepth;

      if (config.viewAsPairs) {
        this.pairsSupported = true;
      } else {
        this.pairsSupported = config.pairsSupported === undefined ? true : config.pairsSupported;
      }
    };

    Ga4ghAlignmentReader.prototype.readAlignments = function (chr, bpStart, bpEnd) {
      const genome = this.genome;
      const self = this;
      return getChrAliasTable().then(function (chrAliasTable) {
        var queryChr = chrAliasTable.hasOwnProperty(chr) ? chrAliasTable[chr] : chr,
            readURL = self.url + "/reads/search";
        return ga4ghSearch({
          url: readURL,
          body: {
            "readGroupSetIds": [self.readGroupSetIds],
            "referenceName": queryChr,
            "start": bpStart,
            "end": bpEnd,
            "pageSize": "10000"
          },
          decode: decodeGa4ghReads,
          results: new AlignmentContainer(chr, bpStart, bpEnd, self.config)
        });
      });

      function getChrAliasTable() {
        if (self.chrAliasTable) {
          return Promise.resolve(self.chrAliasTable);
        } else {
          return self.readMetadata().then(function (json) {
            self.chrAliasTable = {};

            if (genome && json.readGroups && json.readGroups.length > 0) {
              var referenceSetId = json.readGroups[0].referenceSetId;

              if (referenceSetId) {
                // Query for reference names to build an alias table (map of genome ref names -> dataset ref names)
                var readURL = self.url + "/references/search";
                return ga4ghSearch({
                  url: readURL,
                  body: {
                    "referenceSetId": referenceSetId
                  },
                  decode: function (j) {
                    return j.references;
                  }
                }).then(function (references) {
                  references.forEach(function (ref) {
                    var refName = ref.name,
                        alias = genome.getChromosomeName(refName);
                    self.chrAliasTable[alias] = refName;
                  });
                  return self.chrAliasTable;
                });
              } else {
                // Try hardcoded constants -- workaround for non-compliant data at Google
                populateChrAliasTable(self.chrAliasTable, self.config.datasetId);
                return self.chrAliasTable;
              }
            } else {
              // No browser object, can't build map.  This can occur when run from unit tests
              return self.chrAliasTable;
            }
          });
        }
      }
      /**
       * Decode an array of ga4gh read records
       *
        */


      function decodeGa4ghReads(j) {
        var i,
            jsonRecords = j.alignments,
            len = jsonRecords.length,
            alignment,
            jsonAlignment,
            cigarDecoded,
            alignments = [],
            mate,
            blocks;

        for (i = 0; i < len; i++) {
          let record = jsonRecords[i];
          alignment = new BamAlignment();
          alignment.readName = record.fragmentName;
          alignment.properPlacement = record.properPlacement;
          alignment.duplicateFragment = record.duplicateFragment;
          alignment.numberReads = record.numberReads;
          alignment.fragmentLength = record.fragmentLength;
          alignment.readNumber = record.readNumber;
          alignment.failedVendorQualityChecks = record.failedVendorQualityChecks;
          alignment.secondaryAlignment = record.secondaryAlignment;
          alignment.supplementaryAlignment = record.supplementaryAlignment;
          alignment.seq = record.alignedSequence;
          alignment.qual = record.alignedQuality;
          alignment.matePos = record.nextMatePosition;
          alignment.tagDict = record.info;
          alignment.flags = encodeFlags();
          jsonAlignment = record.alignment;

          if (jsonAlignment) {
            alignment.mapped = true;
            alignment.chr = record.alignment.position.referenceName;
            if (genome) alignment.chr = genome.getChromosomeName(alignment.chr);
            alignment.start = parseInt(record.alignment.position.position);
            alignment.strand = !record.alignment.position.reverseStrand;
            alignment.mq = record.alignment.mappingQuality;
            alignment.cigar = encodeCigar(record.alignment.cigar);
            cigarDecoded = translateCigar(record.alignment.cigar);
            alignment.lengthOnRef = cigarDecoded.lengthOnRef;
            blocks = makeBlocks(alignment, cigarDecoded.array);
            alignment.blocks = blocks.blocks;
            alignment.insertions = blocks.insertions;
          } else {
            alignment.mapped = false;
          }

          mate = record.nextMatePosition;

          if (mate) {
            alignment.mate = {
              chr: mate.referenceFrame,
              position: parseInt(mate.position),
              strand: !mate.reverseStrand
            };
          }

          if (self.filter.pass(alignment)) {
            alignments.push(alignment);
          }
        }

        return alignments; // Encode a cigar string -- used for popup text

        function encodeCigar(cigarArray) {
          var cigarString = "";
          cigarArray.forEach(function (cigarUnit) {
            var op = CigarOperationTable[cigarUnit.operation],
                len = cigarUnit.operationLength;
            cigarString = cigarString + (len + op);
          });
          return cigarString;
        } // TODO -- implement me


        function encodeFlags(json) {
          return 0;
        }

        function translateCigar(cigar) {
          var cigarUnit,
              opLen,
              opLtr,
              lengthOnRef = 0,
              cigarArray = [],
              i;

          for (i = 0; i < cigar.length; i++) {
            cigarUnit = cigar[i];
            opLtr = CigarOperationTable[cigarUnit.operation];
            opLen = parseInt(cigarUnit.operationLength); // Google represents long as a String

            if (opLtr === 'M' || opLtr === 'EQ' || opLtr === 'X' || opLtr === 'D' || opLtr === 'N' || opLtr === '=') lengthOnRef += opLen;
            cigarArray.push({
              len: opLen,
              ltr: opLtr
            });
          }

          return {
            lengthOnRef: lengthOnRef,
            array: cigarArray
          };
        }
        /**
         * Split the alignment record into blocks as specified in the cigarArray.  Each aligned block contains
         * its portion of the read sequence and base quality strings.  A read sequence or base quality string
         * of "*" indicates the value is not recorded.  In all other cases the length of the block sequence (block.seq)
         * and quality string (block.qual) must == the block length.
         *
         * NOTE: Insertions are not yet treated // TODO
         *
         * @param record
         * @param cigarArray
         * @returns array of blocks
         */


        function makeBlocks(record, cigarArray) {
          var blocks = [],
              gaps,
              insertions,
              seqOffset = 0,
              pos = record.start,
              len = cigarArray.length,
              gapType;

          for (var i = 0; i < len; i++) {
            var c = cigarArray[i];

            switch (c.ltr) {
              case 'H':
                break;
              // ignore hard clips

              case 'P':
                break;
              // ignore pads

              case 'S':
                seqOffset += c.len;
                gapType = 'S';
                break;
              // soft clip read bases

              case 'N':
              case 'D':
                if (gaps === undefined) {
                  gaps = [];
                }

                gaps.push({
                  start: pos,
                  len: c.len,
                  type: c.ltr
                });
                pos += c.len;
                break;

              case 'I':
                if (insertions === undefined) insertions = [];
                insertions.push(new AlignmentBlock({
                  start: pos,
                  len: c.len,
                  seqOffset: seqOffset
                }));
                seqOffset += c.len;
                break;

              case 'M':
              case 'EQ':
              case '=':
              case 'X':
                blocks.push(new AlignmentBlock({
                  start: pos,
                  len: c.len,
                  seqOffset: seqOffset,
                  gapType: gapType
                }));
                seqOffset += c.len;
                pos += c.len;
                break;

              default:
                console.log("Error processing cigar element: " + c.len + c.ltr);
            }
          }

          return {
            blocks: blocks,
            insertions: insertions,
            gaps: gaps
          };
        }
      }
    };

    Ga4ghAlignmentReader.prototype.readMetadata = function () {
      return ga4ghGet({
        url: this.url,
        entity: "readgroupsets",
        entityId: this.readGroupSetIds
      });
    };
    /**
     * Hardcoded hack to work around some non-compliant google datasets
     *
     * @param chrAliasTable
     * @param datasetId
     */


    function populateChrAliasTable(chrAliasTable, datasetId) {
      var i;

      if ("461916304629" === datasetId || "337315832689" === datasetId) {
        for (i = 1; i < 23; i++) {
          chrAliasTable["chr" + i] = i;
        }

        chrAliasTable["chrX"] = "X";
        chrAliasTable["chrY"] = "Y";
        chrAliasTable["chrM"] = "MT";
      }
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class BamSource {
      constructor(config, browser) {
        const genome = browser.genome;
        this.config = config;
        this.genome = genome;

        if (isDataURL(config.url)) {
          if ("cram" === config.format) {
            throw "CRAM data uris are not supported";
          }

          this.config.indexed = false;
        }

        if ("ga4gh" === config.sourceType) {
          this.bamReader = new Ga4ghAlignmentReader(config, genome);
        } else if ("pysam" === config.sourceType) {
          this.bamReader = new BamWebserviceReader(config, genome);
        } else if ("htsget" === config.sourceType) {
          this.bamReader = new HtsgetBamReader(config, genome);
        } else if ("shardedBam" === config.sourceType) {
          this.bamReader = new ShardedBamReader(config, genome);
        } else if ("cram" === config.format) {
          this.bamReader = new CramReader(config, genome, browser);
        } else {
          if (!this.config.indexURL && config.indexed !== false) {
            if (isString$3(this.config.url)) {
              const inferIndexPath$1 = inferIndexPath(this.config.url, "bai");

              if (inferIndexPath$1) {
                console.error(`Warning: no indexURL specified for ${this.config.url}.  Guessing ${this.baiPath}`);
                this.config.indexURL = inferIndexPath$1;
              } else {
                console.error(`Warning: no indexURL specified for ${this.config.url}.`);
                this.config.indexed = false;
              }
            } else {
              console.error(`Warning: no indexURL specified for ${this.config.name}.`);
              this.config.indexed = false;
            }
          }

          if (this.config.indexed !== false) {
            // && this.config.indexURL) {
            this.bamReader = new BamReader(config, genome);
          } else {
            this.bamReader = new BamReaderNonIndexed(config, genome);
          }
        }

        this.viewAsPairs = config.viewAsPairs;
        this.showSoftClips = config.showSoftClips;
      }

      setViewAsPairs(bool) {
        this.viewAsPairs = bool;
      }

      setShowSoftClips(bool) {
        this.showSoftClips = bool;
      }

      async getAlignments(chr, bpStart, bpEnd) {
        const genome = this.genome;
        const showSoftClips = this.showSoftClips;
        const alignmentContainer = await this.bamReader.readAlignments(chr, bpStart, bpEnd);
        let alignments = alignmentContainer.alignments;

        if (!this.viewAsPairs) {
          alignments = unpairAlignments([{
            alignments: alignments
          }]);
        }

        const hasAlignments = alignments.length > 0;
        alignmentContainer.packedAlignmentRows = packAlignmentRows(alignments, alignmentContainer.start, alignmentContainer.end, showSoftClips);
        this.alignmentContainer = alignmentContainer;

        if (hasAlignments) {
          const sequence = await genome.sequence.getSequence(chr, alignmentContainer.start, alignmentContainer.end);

          if (sequence) {
            alignmentContainer.coverageMap.refSeq = sequence; // TODO -- fix this

            alignmentContainer.sequence = sequence; // TODO -- fix this

            return alignmentContainer;
          } else {
            console.error("No sequence for: " + chr + ":" + alignmentContainer.start + "-" + alignmentContainer.end);
          }
        }

        return alignmentContainer;
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2018 Regents of the University of California
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    const fixColor = colorString => {
      if (isString$3(colorString)) {
        return colorString.indexOf(",") > 0 && !colorString.startsWith("rgb(") ? `rgb(${colorString})` : colorString;
      } else {
        return colorString;
      }
    };
    /**
     * A collection of properties and methods shared by all (or most) track types.
     *
     * @param config
     * @param browser
     * @constructor
     */


    class TrackBase {
      constructor(config, browser) {
        this.browser = browser;
        this.init(config);
      }
      /**
       * Initialize track properties from the config object.  This method is typically overriden in subclasses, which
       * will call this implementation as super.init(config).
       *
       * @param config
       */


      init(config) {
        if (config.displayMode) {
          config.displayMode = config.displayMode.toUpperCase();
        }

        this.config = config;
        this.url = config.url;
        this.type = config.type;
        this.supportHiDPI = config.supportHiDPI === undefined ? true : config.supportHiDPI;

        if (config.name || config.label) {
          this.name = config.name || config.label;
        } else if (isFile(config.url)) {
          this.name = config.url.name;
        } else if (isString$3(config.url) && !config.url.startsWith("data:")) {
          this.name = getFilename$1(config.url);
        }

        this.id = this.config.id === undefined ? this.name : this.config.id;
        this.order = config.order;
        if (config.color) this.color = fixColor(config.color);
        if (config.altColor) this.altColor = fixColor(config.altColor);

        if ("civic-ws" === config.sourceType) {
          // Ugly proxy for specialized track type
          this.defaultColor = "rgb(155,20,20)";
        } else {
          this.defaultColor = "rgb(0,0,150)";
        }

        this.autoscaleGroup = config.autoscaleGroup;
        this.removable = config.removable === undefined ? true : config.removable; // Defaults to true

        this.height = config.height || 100;
        this.autoHeight = config.autoHeight;
        this.minHeight = config.minHeight || Math.min(25, this.height);
        this.maxHeight = config.maxHeight || Math.max(1000, this.height);
        this.visibilityWindow = config.visibilityWindow;

        if (config.onclick) {
          this.onclick = config.onclick;
          config.onclick = undefined; // functions cannot be saved in sessions, clear it here.
        }

        if (config.description) {
          // Override description -- displayed when clicking on track label.  Convert to function if neccessary
          if (typeof config.description === 'function') {
            this.description = config.description;
          } else {
            this.description = () => config.description;
          }
        }
      }

      get name() {
        return this._name;
      }

      set name(name) {
        this._name = name;

        if (this.trackView) {
          this.trackView.setTrackLabelName(name);
        }
      }
      /**
       * Update track properties from the config object.
       *
       * @param config
       */


      updateConfig(config) {
        this.init(config);
      }
      /**
       * Default implementation -- update config with current values.
       * to create session object for bookmarking, sharing.  Updates the track "config" object to reflect the
       * current state.  Only simple properties (string, number, boolean) are updated.
       */


      getState() {
        // Create copy of config, minus transient properties (convention is name starts with '_').  Also, all
        // function properties are transient as they cannot be saved in json
        const state = {};

        for (let key of Object.keys(this.config)) {
          if (!key.startsWith("_") && typeof this.config[key] !== "function" && this.config[key] !== undefined) {
            state[key] = this.config[key];
          }
        } // Update original config values with any changes


        for (let key of Object.keys(state)) {
          if (key.startsWith("_")) continue; // transient property

          const value = this[key];

          if (value && (isSimpleType(value) || typeof value === "boolean" || key === "metadata")) {
            state[key] = value;
          }
        }

        if (this.color) state.color = this.color;
        if (this.altColor) state.altColor = this.altColor; // Flatten dataRange if present

        if (!this.autoscale && this.dataRange) {
          state.min = this.dataRange.min;
          state.max = this.dataRange.max;
        } // Check for non-json-if-yable properties.  Perhaps we should test what can be saved.


        for (let key of Object.keys(state)) {
          const value = state[key];

          if (typeof value === 'function') {
            throw Error(`Property '${key}' of track '${this.name} is a function. Functions cannot be saved in sessions.`);
          }

          if (value instanceof File) {
            // Test specifically for File.  Other types of File-like objects might be savable
            const str = `Track ${this.name} is a local file. Sessions cannot be saved with local file references.`;
            throw Error(str);
          }

          if (value instanceof Promise) {
            throw Error(`Property '${key}' of track '${this.name} is a Promise. Promises cannot be saved in sessions.`);
          }
        }

        return state;
      }

      get supportsWholeGenome() {
        return this.config.supportsWholeGenome === true;
      }
      /**
       * Does the track support sample names.  Current sample aware tracks include VCF (with genotypes), MUT, MAF, and SEG
       * @returns {boolean}
       */


      hasSamples() {
        return false;
      }

      getGenomeId() {
        return this.browser.genome ? this.browser.genome.id : undefined;
      }
      /**
       * Set certain track properties, usually from a "track" line.  Not all UCSC properties are supported.
       *
       * Track configuration settings have precendence over track line properties, so if both are present ignore the
       * track line.
       *
       * @param properties
       */


      setTrackProperties(properties) {
        const tracklineConfg = {};
        let tokens;

        for (let key of Object.keys(properties)) {
          switch (key.toLowerCase()) {
            case "usescore":
              tracklineConfg.useScore = properties[key] === 1 || properties[key] === "1" || properties[key] === "on" || properties[key] === true;
              break;

            case "visibility":
              //0 - hide, 1 - dense, 2 - full, 3 - pack, and 4 - squish
              switch (properties[key]) {
                case "2":
                case "3":
                case "pack":
                case "full":
                  tracklineConfg.displayMode = "EXPANDED";
                  break;

                case "4":
                case "squish":
                  tracklineConfg.displayMode = "SQUISHED";
                  break;

                case "1":
                case "dense":
                  tracklineConfg.displayMode = "COLLAPSED";
              }

              break;

            case "color":
            case "altcolor":
              tracklineConfg[key] = properties[key].startsWith("rgb(") ? properties[key] : "rgb(" + properties[key] + ")";
              break;

            case "featurevisiblitywindow":
            case "visibilitywindow":
              tracklineConfg.visibilityWindow = Number.parseInt(properties[key]);
              break;

            case "maxheightpixels":
              tokens = properties[key].split(":");

              if (tokens.length === 3) {
                tracklineConfg.minHeight = Number.parseInt(tokens[2]);
                tracklineConfg.height = Number.parseInt(tokens[1]);
                tracklineConfg.maxHeight = Number.parseInt(tokens[0]);
              }

              break;

            case "viewlimits":
              if (!this.config.autoscale) {
                // autoscale in the config has precedence
                tokens = properties[key].split(":");
                let min = 0;
                let max;

                if (tokens.length == 1) {
                  max = Number(tokens[0]);
                } else if (tokens.length == 2) {
                  min = Number(tokens[0]);
                  max = Number(tokens[1]);
                }

                tracklineConfg.autoscale = false;
                tracklineConfg.dataRange = {
                  min,
                  max
                };
              }

            case "name":
              tracklineConfg[key] = properties[key];
              break;

            case "url":
              tracklineConfg["infoURL"] = properties[key];
              break;

            case "type":
              const v = properties[key];

              if (UCSCTypeMappings.has(v)) {
                tracklineConfg[key] = UCSCTypeMappings.get(v);
              } else {
                tracklineConfg[key] = v;
              }

              break;

            case "graphtype":
              tracklineConfg["graphType"] = properties[key];
              break;

            default:
              tracklineConfg[key] = properties[key];
          }
        } // Track configuration objects have precedence over track line properties in general.  The "name" property
        // is an exception if it was derived and not explicitly entered (that is derived from the web app from filename).


        for (let key of Object.keys(tracklineConfg)) {
          if (!this.config.hasOwnProperty(key) || key === "name" && this.config._derivedName) {
            let value = tracklineConfg[key];
            if ("true" === value) value = true;
            if ("false" === value) value = false;
            this[key] = value;

            if (key === "height" && this.trackView) {
              try {
                const h = Number.parseInt(value);
                this.trackView.setTrackHeight(h);
              } catch (e) {
                console.error(e);
              }
            }
          }
        }
      }
      /**
       * Return the features clicked over.  Default implementation assumes a single row of features and only considers
       * the genomic location.   Overriden by most subclasses.
       *
       * @param clickState
       * @param features
       * @returns {[]|*[]}
       */


      clickedFeatures(clickState, features) {
        // We use the cached features rather than method to avoid async load.  If the
        // feature is not already loaded this won't work,  but the user wouldn't be mousing over it either.
        if (!features) features = clickState.viewport.cachedFeatures;

        if (!features || features.length === 0) {
          return [];
        }

        const genomicLocation = clickState.genomicLocation; // When zoomed out we need some tolerance around genomicLocation

        const tolerance = clickState.referenceFrame.bpPerPixel > 0.2 ? 3 * clickState.referenceFrame.bpPerPixel : 0.2;
        const ss = genomicLocation - tolerance;
        const ee = genomicLocation + tolerance;
        return FeatureUtils.findOverlapping(features, ss, ee);
      }
      /**
       * Default popup text function -- just extracts string and number properties in random order.
       * @param feature
       * @returns {Array}
       */


      extractPopupData(feature, genomeId) {
        const filteredProperties = new Set(['row', 'color', 'chr', 'start', 'end', 'cdStart', 'cdEnd', 'strand', 'alpha']);
        const data = [];
        let alleles, alleleFreqs;

        for (let property in feature) {
          if (feature.hasOwnProperty(property) && !filteredProperties.has(property) && isSimpleType(feature[property])) {
            let value = feature[property];
            data.push({
              name: capitalize(property),
              value: value
            });

            if (property === "alleles") {
              alleles = feature[property];
            } else if (property === "alleleFreqs") {
              alleleFreqs = feature[property];
            }
          }
        }

        if (alleles && alleleFreqs) {
          if (alleles.endsWith(",")) {
            alleles = alleles.substr(0, alleles.length - 1);
          }

          if (alleleFreqs.endsWith(",")) {
            alleleFreqs = alleleFreqs.substr(0, alleleFreqs.length - 1);
          }

          let a = alleles.split(",");
          let af = alleleFreqs.split(",");

          if (af.length > 1) {
            let b = [];

            for (let i = 0; i < af.length; i++) {
              b.push({
                a: a[i],
                af: Number(af[i])
              });
            }

            b.sort(function (x, y) {
              return x.af - y.af;
            });
            let ref = b[b.length - 1].a;

            if (ref.length === 1) {
              for (let i = b.length - 2; i >= 0; i--) {
                let alt = b[i].a;

                if (alt.length === 1) {
                  if (!genomeId) genomeId = this.getGenomeId();
                  const cravatLink = TrackBase.getCravatLink(feature.chr, feature.start + 1, ref, alt, genomeId);
                  console.log(cravatLink);

                  if (cravatLink) {
                    data.push('<hr/>');
                    data.push({
                      html: cravatLink
                    });
                    data.push('<hr/>');
                  }
                }
              }
            }
          }
        }

        if (feature.attributes) {
          for (let key of Object.keys(feature.attributes)) {
            data.push({
              name: key,
              value: feature.attributes[key]
            });
          }
        } // final chr position


        let posString = `${feature.chr}:${numberFormatter$1(feature.start + 1)}-${numberFormatter$1(feature.end)}`;

        if (feature.strand) {
          posString += ` (${feature.strand})`;
        }

        data.push({
          name: 'Location',
          value: posString
        });
        return data;
      }
      /**
       * Default track description -- displayed on click of track label.  This can be overriden in the track
       * configuration, or in subclasses.
       */


      description() {
        const wrapKeyValue = (k, v) => `<div class="igv-track-label-popup-shim"><b>${k}: </b>${v}</div>`;

        let str = '<div class="igv-track-label-popup">';

        if (this.url) {
          if (isFile(this.url)) {
            str += wrapKeyValue('Filename', this.url.name);
          } else {
            str += wrapKeyValue('URL', this.url);
          }
        } else {
          str = this.name;
        }

        if (this.config) {
          if (this.config.metadata) {
            for (let key of Object.keys(this.config.metadata)) {
              const value = this.config.metadata[key];
              str += wrapKeyValue(key, value);
            }
          } // Add any config properties that are capitalized


          for (let key of Object.keys(this.config)) {
            if (key.startsWith("_")) continue; // transient property

            let first = key.substr(0, 1);

            if (first !== first.toLowerCase()) {
              const value = this.config[key];

              if (value && isSimpleType(value)) {
                str += wrapKeyValue(key, value);
              }
            }
          }
        }

        str += '</div>';
        return str;
      }

      static getCravatLink(chr, position, ref, alt, genomeID) {
        if ("hg38" === genomeID || "GRCh38" === genomeID) {
          const cravatChr = chr.startsWith("chr") ? chr : "chr" + chr;
          return `<a target="_blank" href="https://run.opencravat.org/result/nocache/variant.html` + `?chrom=${cravatChr}&pos=${position}&ref_base=${ref}&alt_base=${alt}"><b>Cravat ${ref}->${alt}</b></a>`;
        } else {
          return undefined;
        }
      }

    }
    /**
     * Map UCSC track line "type" setting to file format.  In igv.js "type" refers to the track type, not the input file format
     * @type {Map<string, string>}
     */


    const UCSCTypeMappings = new Map([["wiggle_0", "wig"], ["bed", "bed"], ["bigBed", "bigBed"], ["bigWig", "bigWig"]]);

    function paintAxis(ctx, pixelWidth, pixelHeight) {
      var x1,
          x2,
          y1,
          y2,
          a,
          b,
          reference,
          shim,
          font = {
        'font': 'normal 10px Arial',
        'textAlign': 'right',
        'strokeStyle': "black"
      };

      if (undefined === this.dataRange || undefined === this.dataRange.max || undefined === this.dataRange.min) {
        return;
      }

      let flipAxis = undefined === this.flipAxis ? false : this.flipAxis;
      IGVGraphics.fillRect(ctx, 0, 0, pixelWidth, pixelHeight, {
        'fillStyle': "rgb(255, 255, 255)"
      });
      reference = 0.95 * pixelWidth;
      x1 = reference - 8;
      x2 = reference; //shim = 0.5 * 0.125;

      shim = .01;
      y1 = y2 = shim * pixelHeight;
      a = {
        x: x2,
        y: y1
      }; // tick

      IGVGraphics.strokeLine(ctx, x1, y1, x2, y2, font);
      IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.min : this.dataRange.max), x1 + 4, y1 + 12, font); //shim = 0.25 * 0.125;

      y1 = y2 = (1.0 - shim) * pixelHeight;
      b = {
        x: x2,
        y: y1
      }; // tick

      IGVGraphics.strokeLine(ctx, x1, y1, x2, y2, font);
      IGVGraphics.fillText(ctx, prettyPrint(flipAxis ? this.dataRange.max : this.dataRange.min), x1 + 4, y1 - 4, font);
      IGVGraphics.strokeLine(ctx, a.x, a.y, b.x, b.y, font);

      function prettyPrint(number) {
        // if number >= 100, show whole number
        // if >= 1 show 1 significant digits
        // if <  1 show 2 significant digits
        if (number === 0) {
          return "0";
        } else if (Math.abs(number) >= 10) {
          return number.toFixed();
        } else if (Math.abs(number) >= 1) {
          return number.toFixed(1);
        } else {
          return number.toFixed(2);
        }
      }
    }

    const appleCrayonRGBPalette = {
      cantaloupe: {
        r: 255,
        g: 206,
        b: 110
      },
      honeydew: {
        r: 206,
        g: 250,
        b: 110
      },
      spindrift: {
        r: 104,
        g: 251,
        b: 208
      },
      sky: {
        r: 106,
        g: 207,
        b: 255
      },
      lavender: {
        r: 210,
        g: 120,
        b: 255
      },
      carnation: {
        r: 255,
        g: 127,
        b: 211
      },
      licorice: {
        r: 0,
        g: 0,
        b: 0
      },
      snow: {
        r: 255,
        g: 255,
        b: 255
      },
      salmon: {
        r: 255,
        g: 114,
        b: 110
      },
      banana: {
        r: 255,
        g: 251,
        b: 109
      },
      flora: {
        r: 104,
        g: 249,
        b: 110
      },
      ice: {
        r: 104,
        g: 253,
        b: 255
      },
      orchid: {
        r: 110,
        g: 118,
        b: 255
      },
      bubblegum: {
        r: 255,
        g: 122,
        b: 255
      },
      lead: {
        r: 30,
        g: 30,
        b: 30
      },
      mercury: {
        r: 232,
        g: 232,
        b: 232
      },
      tangerine: {
        r: 255,
        g: 136,
        b: 2
      },
      lime: {
        r: 131,
        g: 249,
        b: 2
      },
      sea_foam: {
        r: 3,
        g: 249,
        b: 135
      },
      aqua: {
        r: 0,
        g: 140,
        b: 255
      },
      grape: {
        r: 137,
        g: 49,
        b: 255
      },
      strawberry: {
        r: 255,
        g: 41,
        b: 135
      },
      tungsten: {
        r: 58,
        g: 58,
        b: 58
      },
      silver: {
        r: 208,
        g: 208,
        b: 208
      },
      maraschino: {
        r: 255,
        g: 33,
        b: 1
      },
      lemon: {
        r: 255,
        g: 250,
        b: 3
      },
      spring: {
        r: 5,
        g: 248,
        b: 2
      },
      turquoise: {
        r: 0,
        g: 253,
        b: 255
      },
      blueberry: {
        r: 0,
        g: 46,
        b: 255
      },
      magenta: {
        r: 255,
        g: 57,
        b: 255
      },
      iron: {
        r: 84,
        g: 84,
        b: 83
      },
      magnesium: {
        r: 184,
        g: 184,
        b: 184
      },
      mocha: {
        r: 137,
        g: 72,
        b: 0
      },
      fern: {
        r: 69,
        g: 132,
        b: 1
      },
      moss: {
        r: 1,
        g: 132,
        b: 72
      },
      ocean: {
        r: 0,
        g: 74,
        b: 136
      },
      eggplant: {
        r: 73,
        g: 26,
        b: 136
      },
      maroon: {
        r: 137,
        g: 22,
        b: 72
      },
      steel: {
        r: 110,
        g: 110,
        b: 110
      },
      aluminum: {
        r: 160,
        g: 159,
        b: 160
      },
      cayenne: {
        r: 137,
        g: 17,
        b: 0
      },
      aspargus: {
        r: 136,
        g: 133,
        b: 1
      },
      clover: {
        r: 2,
        g: 132,
        b: 1
      },
      teal: {
        r: 0,
        g: 134,
        b: 136
      },
      midnight: {
        r: 0,
        g: 24,
        b: 136
      },
      plum: {
        r: 137,
        g: 30,
        b: 136
      },
      tin: {
        r: 135,
        g: 134,
        b: 135
      },
      nickel: {
        r: 136,
        g: 135,
        b: 135
      }
    };

    function appleCrayonRGB(name) {
      const {
        r,
        g,
        b
      } = appleCrayonRGBPalette[name];
      return `rgb(${r},${g},${b})`;
    }

    const colorPalettes = {
      Set1: ["rgb(228,26,28)", "rgb(55,126,184)", "rgb(77,175,74)", "rgb(166,86,40)", "rgb(152,78,163)", "rgb(255,127,0)", "rgb(247,129,191)", "rgb(153,153,153)", "rgb(255,255,51)"],
      Dark2: ["rgb(27,158,119)", "rgb(217,95,2)", "rgb(117,112,179)", "rgb(231,41,138)", "rgb(102,166,30)", "rgb(230,171,2)", "rgb(166,118,29)", "rgb(102,102,102)"],
      Set2: ["rgb(102, 194,165)", "rgb(252,141,98)", "rgb(141,160,203)", "rgb(231,138,195)", "rgb(166,216,84)", "rgb(255,217,47)", "rgb(229,196,148)", "rgb(179,179,179)"],
      Set3: ["rgb(141,211,199)", "rgb(255,255,179)", "rgb(190,186,218)", "rgb(251,128,114)", "rgb(128,177,211)", "rgb(253,180,98)", "rgb(179,222,105)", "rgb(252,205,229)", "rgb(217,217,217)", "rgb(188,128,189)", "rgb(204,235,197)", "rgb(255,237,111)"],
      Pastel1: ["rgb(251,180,174)", "rgb(179,205,227)", "rgb(204,235,197)", "rgb(222,203,228)", "rgb(254,217,166)", "rgb(255,255,204)", "rgb(229,216,189)", "rgb(253,218,236)"],
      Pastel2: ["rgb(173,226,207)", "rgb(253,205,172)", "rgb(203,213,232)", "rgb(244,202,228)", "rgb(230,245,201)", "rgb(255,242,174)", "rgb(243,225,206)"],
      Accent: ["rgb(127,201,127)", "rgb(190,174,212)", "rgb(253,192,134)", "rgb(255,255,153)", "rgb(56,108,176)", "rgb(240,2,127)", "rgb(191,91,23)"]
    };

    class PaletteColorTable {
      constructor(palette) {
        this.colors = colorPalettes[palette];
        if (!Array.isArray(this.colors)) this.colors = [];
        this.colorTable = {};
        this.nextIdx = 0;
        this.colorGenerator = new RandomColorGenerator();
      }

      getColor(key) {
        if (!this.colorTable.hasOwnProperty(key)) {
          if (this.nextIdx < this.colors.length) {
            this.colorTable[key] = this.colors[this.nextIdx];
          } else {
            this.colorTable[key] = this.colorGenerator.get();
          }

          this.nextIdx++;
        }

        return this.colorTable[key];
      }

    }

    class ColorTable {
      constructor(colors) {
        this.colorTable = colors || {};
        this.nextIdx = 0;
        this.colorGenerator = new RandomColorGenerator();
      }

      getColor(key) {
        if (!this.colorTable.hasOwnProperty(key)) {
          if (this.colorTable.hasOwnProperty("*")) {
            return this.colorTable["*"];
          }

          this.colorTable[key] = this.colorGenerator.get();
        }

        return this.colorTable[key];
      }

    } // Random color generator from https://github.com/sterlingwes/RandomColor/blob/master/rcolor.js
    // Free to use & distribute under the MIT license
    // Wes Johnson (@SterlingWes)
    //
    // inspired by http://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/


    function RandomColorGenerator() {
      this.hue = Math.random();
      this.goldenRatio = 0.618033988749895;
      this.hexwidth = 2;
    }

    RandomColorGenerator.prototype.hsvToRgb = function (h, s, v) {
      var h_i = Math.floor(h * 6),
          f = h * 6 - h_i,
          p = v * (1 - s),
          q = v * (1 - f * s),
          t = v * (1 - (1 - f) * s),
          r = 255,
          g = 255,
          b = 255;

      switch (h_i) {
        case 0:
          r = v, g = t, b = p;
          break;

        case 1:
          r = q, g = v, b = p;
          break;

        case 2:
          r = p, g = v, b = t;
          break;

        case 3:
          r = p, g = q, b = v;
          break;

        case 4:
          r = t, g = p, b = v;
          break;

        case 5:
          r = v, g = p, b = q;
          break;
      }

      return [Math.floor(r * 256), Math.floor(g * 256), Math.floor(b * 256)];
    };

    RandomColorGenerator.prototype.padHex = function (str) {
      if (str.length > this.hexwidth) return str;
      return new Array(this.hexwidth - str.length + 1).join('0') + str;
    };

    RandomColorGenerator.prototype.get = function (saturation, value) {
      this.hue += this.goldenRatio;
      this.hue %= 1;
      if (typeof saturation !== "number") saturation = 0.5;
      if (typeof value !== "number") value = 0.95;
      var rgb = this.hsvToRgb(this.hue, saturation, value);
      return "#" + this.padHex(rgb[0].toString(16)) + this.padHex(rgb[1].toString(16)) + this.padHex(rgb[2].toString(16));
    };

    const randomColorGenerator = new RandomColorGenerator();

    function randomColor() {
      return randomColorGenerator.get();
    } // Returns a random number between min (inclusive) and max (exclusive)

    class Locus {
      constructor(_ref) {
        let {
          chr,
          start,
          end
        } = _ref;
        this.chr = chr;
        this.start = start;
        this.end = end;
      }

      contains(locus) {
        return locus.chr === this.chr && locus.start >= this.start && locus.end <= this.end;
      }

      overlaps(locus) {
        return locus.chr === this.chr && !(locus.end < this.start || locus.start > this.end);
      }

      extend(l) {
        if (l.chr !== this.chr) return;
        this.start = Math.min(l.start, this.start);
        this.end = Math.max(l.end, this.end);
      }

      getLocusString() {
        if ('all' === this.chr) {
          return 'all';
        } else {
          const ss = numberFormatter$1(Math.floor(this.start) + 1);
          const ee = numberFormatter$1(Math.round(this.end));
          return `${this.chr}:${ss}-${ee}`;
        }
      }

      static fromLocusString(str) {
        if ('all' === str) {
          return new Locus({
            chr: 'all'
          });
        }

        const parts = str.split(':');
        const chr = parts[0];
        const se = parts[1].split("-");
        const start = Number.parseInt(se[0].replace(/,/g, "")) - 1;
        const end = Number.parseInt(se[1].replace(/,/g, ""));
        return new Locus({
          chr,
          start,
          end
        });
      }

    }

    /*!
     * vanilla-picker v2.12.1
     * https://vanilla-picker.js.org
     *
     * Copyright 2017-2021 Andreas Borgen (https://github.com/Sphinxxxx), Adam Brooks (https://github.com/dissimulate)
     * Released under the ISC license.
     */
    var classCallCheck = function (instance, Constructor) {
      if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
      }
    };

    var createClass = function () {
      function defineProperties(target, props) {
        for (var i = 0; i < props.length; i++) {
          var descriptor = props[i];
          descriptor.enumerable = descriptor.enumerable || false;
          descriptor.configurable = true;
          if ("value" in descriptor) descriptor.writable = true;
          Object.defineProperty(target, descriptor.key, descriptor);
        }
      }

      return function (Constructor, protoProps, staticProps) {
        if (protoProps) defineProperties(Constructor.prototype, protoProps);
        if (staticProps) defineProperties(Constructor, staticProps);
        return Constructor;
      };
    }();

    var slicedToArray = function () {
      function sliceIterator(arr, i) {
        var _arr = [];
        var _n = true;
        var _d = false;
        var _e = undefined;

        try {
          for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
            _arr.push(_s.value);

            if (i && _arr.length === i) break;
          }
        } catch (err) {
          _d = true;
          _e = err;
        } finally {
          try {
            if (!_n && _i["return"]) _i["return"]();
          } finally {
            if (_d) throw _e;
          }
        }

        return _arr;
      }

      return function (arr, i) {
        if (Array.isArray(arr)) {
          return arr;
        } else if (Symbol.iterator in Object(arr)) {
          return sliceIterator(arr, i);
        } else {
          throw new TypeError("Invalid attempt to destructure non-iterable instance");
        }
      };
    }();

    String.prototype.startsWith = String.prototype.startsWith || function (needle) {
      return this.indexOf(needle) === 0;
    };

    String.prototype.padStart = String.prototype.padStart || function (len, pad) {
      var str = this;

      while (str.length < len) {
        str = pad + str;
      }

      return str;
    };

    var colorNames = {
      cb: '0f8ff',
      tqw: 'aebd7',
      q: '-ffff',
      qmrn: '7fffd4',
      zr: '0ffff',
      bg: '5f5dc',
      bsq: 'e4c4',
      bck: '---',
      nch: 'ebcd',
      b: '--ff',
      bvt: '8a2be2',
      brwn: 'a52a2a',
      brw: 'deb887',
      ctb: '5f9ea0',
      hrt: '7fff-',
      chcT: 'd2691e',
      cr: '7f50',
      rnw: '6495ed',
      crns: '8dc',
      crms: 'dc143c',
      cn: '-ffff',
      Db: '--8b',
      Dcn: '-8b8b',
      Dgnr: 'b8860b',
      Dgr: 'a9a9a9',
      Dgrn: '-64-',
      Dkhk: 'bdb76b',
      Dmgn: '8b-8b',
      Dvgr: '556b2f',
      Drng: '8c-',
      Drch: '9932cc',
      Dr: '8b--',
      Dsmn: 'e9967a',
      Dsgr: '8fbc8f',
      DsTb: '483d8b',
      DsTg: '2f4f4f',
      Dtrq: '-ced1',
      Dvt: '94-d3',
      ppnk: '1493',
      pskb: '-bfff',
      mgr: '696969',
      grb: '1e90ff',
      rbrc: 'b22222',
      rwht: 'af0',
      stg: '228b22',
      chs: '-ff',
      gnsb: 'dcdcdc',
      st: '8f8ff',
      g: 'd7-',
      gnr: 'daa520',
      gr: '808080',
      grn: '-8-0',
      grnw: 'adff2f',
      hnw: '0fff0',
      htpn: '69b4',
      nnr: 'cd5c5c',
      ng: '4b-82',
      vr: '0',
      khk: '0e68c',
      vnr: 'e6e6fa',
      nrb: '0f5',
      wngr: '7cfc-',
      mnch: 'acd',
      Lb: 'add8e6',
      Lcr: '08080',
      Lcn: 'e0ffff',
      Lgnr: 'afad2',
      Lgr: 'd3d3d3',
      Lgrn: '90ee90',
      Lpnk: 'b6c1',
      Lsmn: 'a07a',
      Lsgr: '20b2aa',
      Lskb: '87cefa',
      LsTg: '778899',
      Lstb: 'b0c4de',
      Lw: 'e0',
      m: '-ff-',
      mgrn: '32cd32',
      nn: 'af0e6',
      mgnt: '-ff',
      mrn: '8--0',
      mqm: '66cdaa',
      mmb: '--cd',
      mmrc: 'ba55d3',
      mmpr: '9370db',
      msg: '3cb371',
      mmsT: '7b68ee',
      '': '-fa9a',
      mtr: '48d1cc',
      mmvt: 'c71585',
      mnLb: '191970',
      ntc: '5fffa',
      mstr: 'e4e1',
      mccs: 'e4b5',
      vjw: 'dead',
      nv: '--80',
      c: 'df5e6',
      v: '808-0',
      vrb: '6b8e23',
      rng: 'a5-',
      rngr: '45-',
      rch: 'da70d6',
      pgnr: 'eee8aa',
      pgrn: '98fb98',
      ptrq: 'afeeee',
      pvtr: 'db7093',
      ppwh: 'efd5',
      pchp: 'dab9',
      pr: 'cd853f',
      pnk: 'c0cb',
      pm: 'dda0dd',
      pwrb: 'b0e0e6',
      prp: '8-080',
      cc: '663399',
      r: '--',
      sbr: 'bc8f8f',
      rb: '4169e1',
      sbrw: '8b4513',
      smn: 'a8072',
      nbr: '4a460',
      sgrn: '2e8b57',
      ssh: '5ee',
      snn: 'a0522d',
      svr: 'c0c0c0',
      skb: '87ceeb',
      sTb: '6a5acd',
      sTgr: '708090',
      snw: 'afa',
      n: '-ff7f',
      stb: '4682b4',
      tn: 'd2b48c',
      t: '-8080',
      thst: 'd8bfd8',
      tmT: '6347',
      trqs: '40e0d0',
      vt: 'ee82ee',
      whT: '5deb3',
      wht: '',
      hts: '5f5f5',
      w: '-',
      wgrn: '9acd32'
    };

    function printNum(num) {
      var decs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
      var str = decs > 0 ? num.toFixed(decs).replace(/0+$/, '').replace(/\.$/, '') : num.toString();
      return str || '0';
    }

    var Color = function () {
      function Color(r, g, b, a) {
        classCallCheck(this, Color);
        var that = this;

        function parseString(input) {
          if (input.startsWith('hsl')) {
            var _input$match$map = input.match(/([\-\d\.e]+)/g).map(Number),
                _input$match$map2 = slicedToArray(_input$match$map, 4),
                h = _input$match$map2[0],
                s = _input$match$map2[1],
                l = _input$match$map2[2],
                _a = _input$match$map2[3];

            if (_a === undefined) {
              _a = 1;
            }

            h /= 360;
            s /= 100;
            l /= 100;
            that.hsla = [h, s, l, _a];
          } else if (input.startsWith('rgb')) {
            var _input$match$map3 = input.match(/([\-\d\.e]+)/g).map(Number),
                _input$match$map4 = slicedToArray(_input$match$map3, 4),
                _r = _input$match$map4[0],
                _g = _input$match$map4[1],
                _b = _input$match$map4[2],
                _a2 = _input$match$map4[3];

            if (_a2 === undefined) {
              _a2 = 1;
            }

            that.rgba = [_r, _g, _b, _a2];
          } else {
            if (input.startsWith('#')) {
              that.rgba = Color.hexToRgb(input);
            } else {
              that.rgba = Color.nameToRgb(input) || Color.hexToRgb(input);
            }
          }
        }

        if (r === undefined) ;else if (Array.isArray(r)) {
          this.rgba = r;
        } else if (b === undefined) {
          var color = r && '' + r;

          if (color) {
            parseString(color.toLowerCase());
          }
        } else {
          this.rgba = [r, g, b, a === undefined ? 1 : a];
        }
      }

      createClass(Color, [{
        key: 'printRGB',
        value: function printRGB(alpha) {
          var rgb = alpha ? this.rgba : this.rgba.slice(0, 3),
              vals = rgb.map(function (x, i) {
            return printNum(x, i === 3 ? 3 : 0);
          });
          return alpha ? 'rgba(' + vals + ')' : 'rgb(' + vals + ')';
        }
      }, {
        key: 'printHSL',
        value: function printHSL(alpha) {
          var mults = [360, 100, 100, 1],
              suff = ['', '%', '%', ''];
          var hsl = alpha ? this.hsla : this.hsla.slice(0, 3),
              vals = hsl.map(function (x, i) {
            return printNum(x * mults[i], i === 3 ? 3 : 1) + suff[i];
          });
          return alpha ? 'hsla(' + vals + ')' : 'hsl(' + vals + ')';
        }
      }, {
        key: 'printHex',
        value: function printHex(alpha) {
          var hex = this.hex;
          return alpha ? hex : hex.substring(0, 7);
        }
      }, {
        key: 'rgba',
        get: function get() {
          if (this._rgba) {
            return this._rgba;
          }

          if (!this._hsla) {
            throw new Error('No color is set');
          }

          return this._rgba = Color.hslToRgb(this._hsla);
        },
        set: function set(rgb) {
          if (rgb.length === 3) {
            rgb[3] = 1;
          }

          this._rgba = rgb;
          this._hsla = null;
        }
      }, {
        key: 'rgbString',
        get: function get() {
          return this.printRGB();
        }
      }, {
        key: 'rgbaString',
        get: function get() {
          return this.printRGB(true);
        }
      }, {
        key: 'hsla',
        get: function get() {
          if (this._hsla) {
            return this._hsla;
          }

          if (!this._rgba) {
            throw new Error('No color is set');
          }

          return this._hsla = Color.rgbToHsl(this._rgba);
        },
        set: function set(hsl) {
          if (hsl.length === 3) {
            hsl[3] = 1;
          }

          this._hsla = hsl;
          this._rgba = null;
        }
      }, {
        key: 'hslString',
        get: function get() {
          return this.printHSL();
        }
      }, {
        key: 'hslaString',
        get: function get() {
          return this.printHSL(true);
        }
      }, {
        key: 'hex',
        get: function get() {
          var rgb = this.rgba,
              hex = rgb.map(function (x, i) {
            return i < 3 ? x.toString(16) : Math.round(x * 255).toString(16);
          });
          return '#' + hex.map(function (x) {
            return x.padStart(2, '0');
          }).join('');
        },
        set: function set(hex) {
          this.rgba = Color.hexToRgb(hex);
        }
      }], [{
        key: 'hexToRgb',
        value: function hexToRgb(input) {
          var hex = (input.startsWith('#') ? input.slice(1) : input).replace(/^(\w{3})$/, '$1F').replace(/^(\w)(\w)(\w)(\w)$/, '$1$1$2$2$3$3$4$4').replace(/^(\w{6})$/, '$1FF');

          if (!hex.match(/^([0-9a-fA-F]{8})$/)) {
            throw new Error('Unknown hex color; ' + input);
          }

          var rgba = hex.match(/^(\w\w)(\w\w)(\w\w)(\w\w)$/).slice(1).map(function (x) {
            return parseInt(x, 16);
          });
          rgba[3] = rgba[3] / 255;
          return rgba;
        }
      }, {
        key: 'nameToRgb',
        value: function nameToRgb(input) {
          var hash = input.toLowerCase().replace('at', 'T').replace(/[aeiouyldf]/g, '').replace('ght', 'L').replace('rk', 'D').slice(-5, 4),
              hex = colorNames[hash];
          return hex === undefined ? hex : Color.hexToRgb(hex.replace(/\-/g, '00').padStart(6, 'f'));
        }
      }, {
        key: 'rgbToHsl',
        value: function rgbToHsl(_ref) {
          var _ref2 = slicedToArray(_ref, 4),
              r = _ref2[0],
              g = _ref2[1],
              b = _ref2[2],
              a = _ref2[3];

          r /= 255;
          g /= 255;
          b /= 255;
          var max = Math.max(r, g, b),
              min = Math.min(r, g, b);
          var h = void 0,
              s = void 0,
              l = (max + min) / 2;

          if (max === min) {
            h = s = 0;
          } else {
            var d = max - min;
            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);

            switch (max) {
              case r:
                h = (g - b) / d + (g < b ? 6 : 0);
                break;

              case g:
                h = (b - r) / d + 2;
                break;

              case b:
                h = (r - g) / d + 4;
                break;
            }

            h /= 6;
          }

          return [h, s, l, a];
        }
      }, {
        key: 'hslToRgb',
        value: function hslToRgb(_ref3) {
          var _ref4 = slicedToArray(_ref3, 4),
              h = _ref4[0],
              s = _ref4[1],
              l = _ref4[2],
              a = _ref4[3];

          var r = void 0,
              g = void 0,
              b = void 0;

          if (s === 0) {
            r = g = b = l;
          } else {
            var hue2rgb = function hue2rgb(p, q, t) {
              if (t < 0) t += 1;
              if (t > 1) t -= 1;
              if (t < 1 / 6) return p + (q - p) * 6 * t;
              if (t < 1 / 2) return q;
              if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
              return p;
            };

            var q = l < 0.5 ? l * (1 + s) : l + s - l * s,
                p = 2 * l - q;
            r = hue2rgb(p, q, h + 1 / 3);
            g = hue2rgb(p, q, h);
            b = hue2rgb(p, q, h - 1 / 3);
          }

          var rgba = [r * 255, g * 255, b * 255].map(Math.round);
          rgba[3] = a;
          return rgba;
        }
      }]);
      return Color;
    }();

    var EventBucket = function () {
      function EventBucket() {
        classCallCheck(this, EventBucket);
        this._events = [];
      }

      createClass(EventBucket, [{
        key: 'add',
        value: function add(target, type, handler) {
          target.addEventListener(type, handler, false);

          this._events.push({
            target: target,
            type: type,
            handler: handler
          });
        }
      }, {
        key: 'remove',
        value: function remove(target, type, handler) {
          this._events = this._events.filter(function (e) {
            var isMatch = true;

            if (target && target !== e.target) {
              isMatch = false;
            }

            if (type && type !== e.type) {
              isMatch = false;
            }

            if (handler && handler !== e.handler) {
              isMatch = false;
            }

            if (isMatch) {
              EventBucket._doRemove(e.target, e.type, e.handler);
            }

            return !isMatch;
          });
        }
      }, {
        key: 'destroy',
        value: function destroy() {
          this._events.forEach(function (e) {
            return EventBucket._doRemove(e.target, e.type, e.handler);
          });

          this._events = [];
        }
      }], [{
        key: '_doRemove',
        value: function _doRemove(target, type, handler) {
          target.removeEventListener(type, handler, false);
        }
      }]);
      return EventBucket;
    }();

    function parseHTML(htmlString) {
      var div = document.createElement('div');
      div.innerHTML = htmlString;
      return div.firstElementChild;
    }

    function dragTrack(eventBucket, area, callback) {
      var dragging = false;

      function clamp(val, min, max) {
        return Math.max(min, Math.min(val, max));
      }

      function onMove(e, info, starting) {
        if (starting) {
          dragging = true;
        }

        if (!dragging) {
          return;
        }

        e.preventDefault();
        var bounds = area.getBoundingClientRect(),
            w = bounds.width,
            h = bounds.height,
            x = info.clientX,
            y = info.clientY;
        var relX = clamp(x - bounds.left, 0, w),
            relY = clamp(y - bounds.top, 0, h);
        callback(relX / w, relY / h);
      }

      function onMouse(e, starting) {
        var button = e.buttons === undefined ? e.which : e.buttons;

        if (button === 1) {
          onMove(e, e, starting);
        } else {
          dragging = false;
        }
      }

      function onTouch(e, starting) {
        if (e.touches.length === 1) {
          onMove(e, e.touches[0], starting);
        } else {
          dragging = false;
        }
      }

      eventBucket.add(area, 'mousedown', function (e) {
        onMouse(e, true);
      });
      eventBucket.add(area, 'touchstart', function (e) {
        onTouch(e, true);
      });
      eventBucket.add(window, 'mousemove', onMouse);
      eventBucket.add(area, 'touchmove', onTouch);
      eventBucket.add(window, 'mouseup', function (e) {
        dragging = false;
      });
      eventBucket.add(area, 'touchend', function (e) {
        dragging = false;
      });
      eventBucket.add(area, 'touchcancel', function (e) {
        dragging = false;
      });
    }

    var BG_TRANSP = 'linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%) 0 0 / 2em 2em,\n                   linear-gradient(45deg, lightgrey 25%,       white 25%,       white 75%, lightgrey 75%) 1em 1em / 2em 2em';
    var HUES = 360;
    var EVENT_KEY = 'keydown',
        EVENT_CLICK_OUTSIDE = 'mousedown',
        EVENT_TAB_MOVE = 'focusin';

    function $(selector, context) {
      return (context || document).querySelector(selector);
    }

    function stopEvent(e) {
      e.preventDefault();
      e.stopPropagation();
    }

    function onKey(bucket, target, keys, handler, stop) {
      bucket.add(target, EVENT_KEY, function (e) {
        if (keys.indexOf(e.key) >= 0) {
          if (stop) {
            stopEvent(e);
          }

          handler(e);
        }
      });
    }

    var Picker = function () {
      function Picker(options) {
        classCallCheck(this, Picker);
        this.settings = {
          popup: 'right',
          layout: 'default',
          alpha: true,
          editor: true,
          editorFormat: 'hex',
          cancelButton: false,
          defaultColor: '#0cf'
        };
        this._events = new EventBucket();
        this.onChange = null;
        this.onDone = null;
        this.onOpen = null;
        this.onClose = null;
        this.setOptions(options);
      }

      createClass(Picker, [{
        key: 'setOptions',
        value: function setOptions(options) {
          var _this = this;

          if (!options) {
            return;
          }

          var settings = this.settings;

          function transfer(source, target, skipKeys) {
            for (var key in source) {
              if (skipKeys && skipKeys.indexOf(key) >= 0) {
                continue;
              }

              target[key] = source[key];
            }
          }

          if (options instanceof HTMLElement) {
            settings.parent = options;
          } else {
            if (settings.parent && options.parent && settings.parent !== options.parent) {
              this._events.remove(settings.parent);

              this._popupInited = false;
            }

            transfer(options, settings);

            if (options.onChange) {
              this.onChange = options.onChange;
            }

            if (options.onDone) {
              this.onDone = options.onDone;
            }

            if (options.onOpen) {
              this.onOpen = options.onOpen;
            }

            if (options.onClose) {
              this.onClose = options.onClose;
            }

            var col = options.color || options.colour;

            if (col) {
              this._setColor(col);
            }
          }

          var parent = settings.parent;

          if (parent && settings.popup && !this._popupInited) {
            var openProxy = function openProxy(e) {
              return _this.openHandler(e);
            };

            this._events.add(parent, 'click', openProxy);

            onKey(this._events, parent, [' ', 'Spacebar', 'Enter'], openProxy);
            this._popupInited = true;
          } else if (options.parent && !settings.popup) {
            this.show();
          }
        }
      }, {
        key: 'openHandler',
        value: function openHandler(e) {
          if (this.show()) {
            e && e.preventDefault();
            this.settings.parent.style.pointerEvents = 'none';
            var toFocus = e && e.type === EVENT_KEY ? this._domEdit : this.domElement;
            setTimeout(function () {
              return toFocus.focus();
            }, 100);

            if (this.onOpen) {
              this.onOpen(this.colour);
            }
          }
        }
      }, {
        key: 'closeHandler',
        value: function closeHandler(e) {
          var event = e && e.type;
          var doHide = false;

          if (!e) {
            doHide = true;
          } else if (event === EVENT_CLICK_OUTSIDE || event === EVENT_TAB_MOVE) {
            var knownTime = (this.__containedEvent || 0) + 100;

            if (e.timeStamp > knownTime) {
              doHide = true;
            }
          } else {
            stopEvent(e);
            doHide = true;
          }

          if (doHide && this.hide()) {
            this.settings.parent.style.pointerEvents = '';

            if (event !== EVENT_CLICK_OUTSIDE) {
              this.settings.parent.focus();
            }

            if (this.onClose) {
              this.onClose(this.colour);
            }
          }
        }
      }, {
        key: 'movePopup',
        value: function movePopup(options, open) {
          this.closeHandler();
          this.setOptions(options);

          if (open) {
            this.openHandler();
          }
        }
      }, {
        key: 'setColor',
        value: function setColor(color, silent) {
          this._setColor(color, {
            silent: silent
          });
        }
      }, {
        key: '_setColor',
        value: function _setColor(color, flags) {
          if (typeof color === 'string') {
            color = color.trim();
          }

          if (!color) {
            return;
          }

          flags = flags || {};
          var c = void 0;

          try {
            c = new Color(color);
          } catch (ex) {
            if (flags.failSilently) {
              return;
            }

            throw ex;
          }

          if (!this.settings.alpha) {
            var hsla = c.hsla;
            hsla[3] = 1;
            c.hsla = hsla;
          }

          this.colour = this.color = c;

          this._setHSLA(null, null, null, null, flags);
        }
      }, {
        key: 'setColour',
        value: function setColour(colour, silent) {
          this.setColor(colour, silent);
        }
      }, {
        key: 'show',
        value: function show() {
          var parent = this.settings.parent;

          if (!parent) {
            return false;
          }

          if (this.domElement) {
            var toggled = this._toggleDOM(true);

            this._setPosition();

            return toggled;
          }

          var html = this.settings.template || '<div class="picker_wrapper" tabindex="-1"><div class="picker_arrow"></div><div class="picker_hue picker_slider"><div class="picker_selector"></div></div><div class="picker_sl"><div class="picker_selector"></div></div><div class="picker_alpha picker_slider"><div class="picker_selector"></div></div><div class="picker_editor"><input aria-label="Type a color name or hex value"/></div><div class="picker_sample"></div><div class="picker_done"><button>Ok</button></div><div class="picker_cancel"><button>Cancel</button></div></div>';
          var wrapper = parseHTML(html);
          this.domElement = wrapper;
          this._domH = $('.picker_hue', wrapper);
          this._domSL = $('.picker_sl', wrapper);
          this._domA = $('.picker_alpha', wrapper);
          this._domEdit = $('.picker_editor input', wrapper);
          this._domSample = $('.picker_sample', wrapper);
          this._domOkay = $('.picker_done button', wrapper);
          this._domCancel = $('.picker_cancel button', wrapper);
          wrapper.classList.add('layout_' + this.settings.layout);

          if (!this.settings.alpha) {
            wrapper.classList.add('no_alpha');
          }

          if (!this.settings.editor) {
            wrapper.classList.add('no_editor');
          }

          if (!this.settings.cancelButton) {
            wrapper.classList.add('no_cancel');
          }

          this._ifPopup(function () {
            return wrapper.classList.add('popup');
          });

          this._setPosition();

          if (this.colour) {
            this._updateUI();
          } else {
            this._setColor(this.settings.defaultColor);
          }

          this._bindEvents();

          return true;
        }
      }, {
        key: 'hide',
        value: function hide() {
          return this._toggleDOM(false);
        }
      }, {
        key: 'destroy',
        value: function destroy() {
          this._events.destroy();

          if (this.domElement) {
            this.settings.parent.removeChild(this.domElement);
          }
        }
      }, {
        key: '_bindEvents',
        value: function _bindEvents() {
          var _this2 = this;

          var that = this,
              dom = this.domElement,
              events = this._events;

          function addEvent(target, type, handler) {
            events.add(target, type, handler);
          }

          addEvent(dom, 'click', function (e) {
            return e.preventDefault();
          });
          dragTrack(events, this._domH, function (x, y) {
            return that._setHSLA(x);
          });
          dragTrack(events, this._domSL, function (x, y) {
            return that._setHSLA(null, x, 1 - y);
          });

          if (this.settings.alpha) {
            dragTrack(events, this._domA, function (x, y) {
              return that._setHSLA(null, null, null, 1 - y);
            });
          }

          var editInput = this._domEdit;
          {
            addEvent(editInput, 'input', function (e) {
              that._setColor(this.value, {
                fromEditor: true,
                failSilently: true
              });
            });
            addEvent(editInput, 'focus', function (e) {
              var input = this;

              if (input.selectionStart === input.selectionEnd) {
                input.select();
              }
            });
          }

          this._ifPopup(function () {
            var popupCloseProxy = function popupCloseProxy(e) {
              return _this2.closeHandler(e);
            };

            addEvent(window, EVENT_CLICK_OUTSIDE, popupCloseProxy);
            addEvent(window, EVENT_TAB_MOVE, popupCloseProxy);
            onKey(events, dom, ['Esc', 'Escape'], popupCloseProxy);

            var timeKeeper = function timeKeeper(e) {
              _this2.__containedEvent = e.timeStamp;
            };

            addEvent(dom, EVENT_CLICK_OUTSIDE, timeKeeper);
            addEvent(dom, EVENT_TAB_MOVE, timeKeeper);
            addEvent(_this2._domCancel, 'click', popupCloseProxy);
          });

          var onDoneProxy = function onDoneProxy(e) {
            _this2._ifPopup(function () {
              return _this2.closeHandler(e);
            });

            if (_this2.onDone) {
              _this2.onDone(_this2.colour);
            }
          };

          addEvent(this._domOkay, 'click', onDoneProxy);
          onKey(events, dom, ['Enter'], onDoneProxy);
        }
      }, {
        key: '_setPosition',
        value: function _setPosition() {
          var parent = this.settings.parent,
              elm = this.domElement;

          if (parent !== elm.parentNode) {
            parent.appendChild(elm);
          }

          this._ifPopup(function (popup) {
            if (getComputedStyle(parent).position === 'static') {
              parent.style.position = 'relative';
            }

            var cssClass = popup === true ? 'popup_right' : 'popup_' + popup;
            ['popup_top', 'popup_bottom', 'popup_left', 'popup_right'].forEach(function (c) {
              if (c === cssClass) {
                elm.classList.add(c);
              } else {
                elm.classList.remove(c);
              }
            });
            elm.classList.add(cssClass);
          });
        }
      }, {
        key: '_setHSLA',
        value: function _setHSLA(h, s, l, a, flags) {
          flags = flags || {};
          var col = this.colour,
              hsla = col.hsla;
          [h, s, l, a].forEach(function (x, i) {
            if (x || x === 0) {
              hsla[i] = x;
            }
          });
          col.hsla = hsla;

          this._updateUI(flags);

          if (this.onChange && !flags.silent) {
            this.onChange(col);
          }
        }
      }, {
        key: '_updateUI',
        value: function _updateUI(flags) {
          if (!this.domElement) {
            return;
          }

          flags = flags || {};
          var col = this.colour,
              hsl = col.hsla,
              cssHue = 'hsl(' + hsl[0] * HUES + ', 100%, 50%)',
              cssHSL = col.hslString,
              cssHSLA = col.hslaString;
          var uiH = this._domH,
              uiSL = this._domSL,
              uiA = this._domA,
              thumbH = $('.picker_selector', uiH),
              thumbSL = $('.picker_selector', uiSL),
              thumbA = $('.picker_selector', uiA);

          function posX(parent, child, relX) {
            child.style.left = relX * 100 + '%';
          }

          function posY(parent, child, relY) {
            child.style.top = relY * 100 + '%';
          }

          posX(uiH, thumbH, hsl[0]);
          this._domSL.style.backgroundColor = this._domH.style.color = cssHue;
          posX(uiSL, thumbSL, hsl[1]);
          posY(uiSL, thumbSL, 1 - hsl[2]);
          uiSL.style.color = cssHSL;
          posY(uiA, thumbA, 1 - hsl[3]);
          var opaque = cssHSL,
              transp = opaque.replace('hsl', 'hsla').replace(')', ', 0)'),
              bg = 'linear-gradient(' + [opaque, transp] + ')';
          this._domA.style.background = bg + ', ' + BG_TRANSP;

          if (!flags.fromEditor) {
            var format = this.settings.editorFormat,
                alpha = this.settings.alpha;
            var value = void 0;

            switch (format) {
              case 'rgb':
                value = col.printRGB(alpha);
                break;

              case 'hsl':
                value = col.printHSL(alpha);
                break;

              default:
                value = col.printHex(alpha);
            }

            this._domEdit.value = value;
          }

          this._domSample.style.color = cssHSLA;
        }
      }, {
        key: '_ifPopup',
        value: function _ifPopup(actionIf, actionElse) {
          if (this.settings.parent && this.settings.popup) {
            actionIf && actionIf(this.settings.popup);
          } else {
            actionElse && actionElse();
          }
        }
      }, {
        key: '_toggleDOM',
        value: function _toggleDOM(toVisible) {
          var dom = this.domElement;

          if (!dom) {
            return false;
          }

          var displayStyle = toVisible ? '' : 'none',
              toggle = dom.style.display !== displayStyle;

          if (toggle) {
            dom.style.display = displayStyle;
          }

          return toggle;
        }
      }]);
      return Picker;
    }();

    {
      var style = document.createElement('style');
      style.textContent = '.picker_wrapper.no_alpha .picker_alpha{display:none}.picker_wrapper.no_editor .picker_editor{position:absolute;z-index:-1;opacity:0}.picker_wrapper.no_cancel .picker_cancel{display:none}.layout_default.picker_wrapper{display:flex;flex-flow:row wrap;justify-content:space-between;align-items:stretch;font-size:10px;width:25em;padding:.5em}.layout_default.picker_wrapper input,.layout_default.picker_wrapper button{font-size:1rem}.layout_default.picker_wrapper>*{margin:.5em}.layout_default.picker_wrapper::before{content:"";display:block;width:100%;height:0;order:1}.layout_default .picker_slider,.layout_default .picker_selector{padding:1em}.layout_default .picker_hue{width:100%}.layout_default .picker_sl{flex:1 1 auto}.layout_default .picker_sl::before{content:"";display:block;padding-bottom:100%}.layout_default .picker_editor{order:1;width:6.5rem}.layout_default .picker_editor input{width:100%;height:100%}.layout_default .picker_sample{order:1;flex:1 1 auto}.layout_default .picker_done,.layout_default .picker_cancel{order:1}.picker_wrapper{box-sizing:border-box;background:#f2f2f2;box-shadow:0 0 0 1px silver;cursor:default;font-family:sans-serif;color:#444;pointer-events:auto}.picker_wrapper:focus{outline:none}.picker_wrapper button,.picker_wrapper input{box-sizing:border-box;border:none;box-shadow:0 0 0 1px silver;outline:none}.picker_wrapper button:focus,.picker_wrapper button:active,.picker_wrapper input:focus,.picker_wrapper input:active{box-shadow:0 0 2px 1px #1e90ff}.picker_wrapper button{padding:.4em .6em;cursor:pointer;background-color:#f5f5f5;background-image:linear-gradient(0deg, gainsboro, transparent)}.picker_wrapper button:active{background-image:linear-gradient(0deg, transparent, gainsboro)}.picker_wrapper button:hover{background-color:#fff}.picker_selector{position:absolute;z-index:1;display:block;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);border:2px solid #fff;border-radius:100%;box-shadow:0 0 3px 1px #67b9ff;background:currentColor;cursor:pointer}.picker_slider .picker_selector{border-radius:2px}.picker_hue{position:relative;background-image:linear-gradient(90deg, red, yellow, lime, cyan, blue, magenta, red);box-shadow:0 0 0 1px silver}.picker_sl{position:relative;box-shadow:0 0 0 1px silver;background-image:linear-gradient(180deg, white, rgba(255, 255, 255, 0) 50%),linear-gradient(0deg, black, rgba(0, 0, 0, 0) 50%),linear-gradient(90deg, #808080, rgba(128, 128, 128, 0))}.picker_alpha,.picker_sample{position:relative;background:linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%) 0 0/2em 2em,linear-gradient(45deg, lightgrey 25%, white 25%, white 75%, lightgrey 75%) 1em 1em/2em 2em;box-shadow:0 0 0 1px silver}.picker_alpha .picker_selector,.picker_sample .picker_selector{background:none}.picker_editor input{font-family:monospace;padding:.2em .4em}.picker_sample::before{content:"";position:absolute;display:block;width:100%;height:100%;background:currentColor}.picker_arrow{position:absolute;z-index:-1}.picker_wrapper.popup{position:absolute;z-index:2;margin:1.5em}.picker_wrapper.popup,.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{background:#f2f2f2;box-shadow:0 0 10px 1px rgba(0,0,0,.4)}.picker_wrapper.popup .picker_arrow{width:3em;height:3em;margin:0}.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{content:"";display:block;position:absolute;top:0;left:0;z-index:-99}.picker_wrapper.popup .picker_arrow::before{width:100%;height:100%;-webkit-transform:skew(45deg);transform:skew(45deg);-webkit-transform-origin:0 100%;transform-origin:0 100%}.picker_wrapper.popup .picker_arrow::after{width:150%;height:150%;box-shadow:none}.popup.popup_top{bottom:100%;left:0}.popup.popup_top .picker_arrow{bottom:0;left:0;-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.popup.popup_bottom{top:100%;left:0}.popup.popup_bottom .picker_arrow{top:0;left:0;-webkit-transform:rotate(90deg) scale(1, -1);transform:rotate(90deg) scale(1, -1)}.popup.popup_left{top:0;right:100%}.popup.popup_left .picker_arrow{top:0;right:0;-webkit-transform:scale(-1, 1);transform:scale(-1, 1)}.popup.popup_right{top:0;left:100%}.popup.popup_right .picker_arrow{top:0;left:0}';
      document.documentElement.firstElementChild.appendChild(style);
      Picker.StyleElement = style;
    }

    function getChrColor$1(chr) {
      if (chrColorMap$1[chr]) {
        return chrColorMap$1[chr];
      } else if (chrColorMap$1["chr" + chr]) {
        const color = chrColorMap$1["chr" + chr];
        chrColorMap$1[chr] = color;
        return color;
      } else {
        const color = randomRGB();
        chrColorMap$1[chr] = color;
        return color;
      }
    }

    function randomRGB(min, max) {
      var r = Math.round(Math.random() * 255).toString(10);
      var g = Math.round(Math.random() * 255).toString(10);
      var b = Math.round(Math.random() * 255).toString(10);
      return "rgb(" + r + "," + g + "," + b + ")";
    }

    const chrColorMap$1 = {
      "chrX": "rgb(204, 153, 0)",
      "chrY": "rgb(153, 204, 0",
      "chrUn": "rgb(50, 50, 50)",
      "chr1": "rgb(80, 80, 255)",
      "chrI": "rgb(139, 155, 187)",
      "chr2": "rgb(206, 61, 50)",
      "chrII": "rgb(206, 61, 50)",
      "chr2a": "rgb(216, 71, 60)",
      "chr2b": "rgb(226, 81, 70)",
      "chr3": "rgb(116, 155, 88)",
      "chrIII": "rgb(116, 155, 88)",
      "chr4": "rgb(240, 230, 133)",
      "chrIV": "rgb(240, 230, 133)",
      "chr5": "rgb(70, 105, 131)",
      "chr6": "rgb(186, 99, 56)",
      "chr7": "rgb(93, 177, 221)",
      "chr8": "rgb(128, 34, 104)",
      "chr9": "rgb(107, 215, 107)",
      "chr10": "rgb(213, 149, 167)",
      "chr11": "rgb(146, 72, 34)",
      "chr12": "rgb(131, 123, 141)",
      "chr13": "rgb(199, 81, 39)",
      "chr14": "rgb(213, 143, 92)",
      "chr15": "rgb(122, 101, 165)",
      "chr16": "rgb(228, 175, 105)",
      "chr17": "rgb(59, 27, 83)",
      "chr18": "rgb(205, 222, 183)",
      "chr19": "rgb(97, 42, 121)",
      "chr20": "rgb(174, 31, 99)",
      "chr21": "rgb(231, 199, 111)",
      "chr22": "rgb(90, 101, 94)",
      "chr23": "rgb(204, 153, 0)",
      "chr24": "rgb(153, 204, 0)",
      "chr25": "rgb(51, 204, 0)",
      "chr26": "rgb(0, 204, 51)",
      "chr27": "rgb(0, 204, 153)",
      "chr28": "rgb(0, 153, 204)",
      "chr29": "rgb(10, 71, 255)",
      "chr30": "rgb(71, 117, 255)",
      "chr31": "rgb(255, 194, 10)",
      "chr32": "rgb(255, 209, 71)",
      "chr33": "rgb(153, 0, 51)",
      "chr34": "rgb(153, 26, 0)",
      "chr35": "rgb(153, 102, 0)",
      "chr36": "rgb(128, 153, 0)",
      "chr37": "rgb(51, 153, 0)",
      "chr38": "rgb(0, 153, 26)",
      "chr39": "rgb(0, 153, 102)",
      "chr40": "rgb(0, 128, 153)",
      "chr41": "rgb(0, 51, 153)",
      "chr42": "rgb(26, 0, 153)",
      "chr43": "rgb(102, 0, 153)",
      "chr44": "rgb(153, 0, 128)",
      "chr45": "rgb(214, 0, 71)",
      "chr46": "rgb(255, 20, 99)",
      "chr47": "rgb(0, 214, 143)",
      "chr48": "rgb(20, 255, 177)"
    };

    class ChordSetManager {
      constructor(config) {
        this.tracks = [];
        this.chordSets = [];
      }

      addChordSet(chordSet) {
        // If a chord set with this name exists replace it (same track, same region)
        this.chordSets = this.chordSets.filter(g => g.name !== chordSet.name);
        this.chordSets.push(chordSet);
        let track = this.tracks.find(t => chordSet.trackName === t.name);

        if (track) {
          track.chordSets = track.chordSets.filter(cs => cs.name !== chordSet.name);
          track.chordSets.push(chordSet);
        }

        if (!track) {
          track = new IGVTrack(chordSet);
          this.tracks.push(track);
        }
      }

      clearChords() {
        this.tracks = [];
        this.chordSets = [];
      }

      getTrack(name) {
        return this.tracks.find(t => name === t.name);
      }

      getChordSet(name) {
        return this.chordSets.find(cs => name === cs.name);
      }

    }

    class IGVTrack {
      constructor(chordSet) {
        this.name = chordSet.trackName;
        this.color = chordSet.trackColor;
        this.visible = true;
        this.chordSets = [chordSet];
        this.id = guid$1();
      }

      get chords() {
        if (this.chordSets.length === 1) {
          return this.chordSets[0].chords;
        }

        const chords = [];

        for (let cs of this.chordSets) {
          for (let c of cs.chords) {
            chords.push(c);
          }
        }

        return chords;
      }

    }

    function guid$1() {
      return ("0000" + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4);
    }

    const EXP5 = Math.exp(5);

    class CircularView {
      static isInstalled() {
        return window["JBrowseReactCircularGenomeView"] !== undefined && window["React"] !== undefined && window["ReactDOM"] !== undefined;
      }
      /**
       * Create a new CircularView
       *
       * @param parent
       * @param config - configuration options
       *   {
       *       assembly: {name: string, id: string, chromosomes: [{name: string, bpLength: integer, color: string}]
       *       onChordClick: function called upon chord click with chord feature as argument
       *   }
       */


      constructor(parent, config) {
        config = config || {};
        this.config = config;

        if (CircularView.isInstalled()) {
          this.parent = parent;
          this.groupByTrack = config.groupByTrack === true;
          this.chordManager = new ChordSetManager(config); // wrapper for toolbar and circular-view container

          const wrapper = document.createElement('div');
          wrapper.className = 'igv-circview-container';
          parent.appendChild(wrapper); // toolbar

          this.createControls(wrapper);
          this.resetControlPanel(); // circular view container

          const element = document.createElement('div');
          element.className = 'igv-circview-circular-genome-view';
          wrapper.appendChild(element);
          this.container = element;

          if (config.assembly) {
            this.setAssembly(config.assembly);
          }

          this.width = config.width || 500;
          this.height = config.height || 500;
          this.setSize(this.width, this.height);
        } else {
          console.error("JBrowse circular view is not installed");
        }
      }

      createControls(parent) {
        // toolbar
        const toolbarDiv = document.createElement('div');
        toolbarDiv.className = 'igv-circview-toolbar';
        parent.appendChild(toolbarDiv);
        this.toolbar = toolbarDiv; // control panel

        const controlPanelDiv = document.createElement('div');
        controlPanelDiv.className = 'igv-circview-track-panel';
        parent.appendChild(controlPanelDiv);
        this.controlPanel = controlPanelDiv;
        this.controlPanel.style.display = 'none'; // toolbar button container - Track Options - Clear All

        const buttonContainer = document.createElement('div');
        buttonContainer.className = 'igv-circview-toolbar-button-container';
        this.toolbar.appendChild(buttonContainer); // Show Controls

        this.showControlsButton = document.createElement('div');
        this.showControlsButton.className = 'igv-circview-button';
        buttonContainer.appendChild(this.showControlsButton);
        this.showControlsButton.innerText = 'none' === this.controlPanel.style.display ? 'Show Controls' : 'Hide Controls';
        this.showControlsButton.addEventListener('click', event => {
          const panelRows = this.controlPanel.querySelectorAll('div');

          if (panelRows.length > 0) {
            if ('none' === this.controlPanel.style.display) {
              this.controlPanel.style.display = 'flex';
              event.target.innerText = 'Hide Controls';
            } else {
              this.controlPanel.style.display = 'none';
              event.target.innerText = 'Show Controls';
            }
          }
        }); // Clear All

        let button = document.createElement('div');
        button.className = 'igv-circview-button';
        buttonContainer.appendChild(button);
        button.innerText = 'Clear All';
        button.addEventListener('click', () => {
          this.clearChords();
        }); // Close

        if (false !== this.config.showCloseButton) {
          button = document.createElement('div');
          button.className = 'igv-circview-button';
          buttonContainer.appendChild(button);
          button.innerText = 'Close';
          button.addEventListener('click', () => {
            this.visible = false;
          });
        }
      }

      resetControlPanel() {
        this.controlPanel.innerHTML = '';
        this.controlPanel.appendChild(this.createGroupByCB());
        const chordSets = this.groupByTrack ? this.chordManager.tracks : this.chordManager.chordSets;

        for (let cs of chordSets) {
          this.addToControlPanel(cs);
        }
      }

      createGroupByCB() {
        const groupByCB = document.createElement('input');
        groupByCB.type = 'checkbox';
        groupByCB.id = 'groupByCB';
        groupByCB.style.width = '1.4em';
        groupByCB.style.height = '1.4em';
        groupByCB.checked = this.groupByTrack;

        groupByCB.onclick = evt => {
          this.groupByTrack = evt.target.checked;
          this.resetControlPanel();
          this.render();
        };

        const groupByLabel = document.createElement('label');
        groupByLabel.for = 'groupByCB';
        groupByLabel.innerText = 'Group by track';
        groupByLabel.style.color = 'black';
        groupByLabel.style.paddingLeft = '10px';
        const trackPanelRow = document.createElement('div');
        trackPanelRow.style.width = '100%';
        trackPanelRow.style.paddingTop = '5px';
        trackPanelRow.style.paddingBottom = '5px';
        trackPanelRow.style.background = 'rgb(216, 230, 234)';
        trackPanelRow.appendChild(groupByCB);
        trackPanelRow.appendChild(groupByLabel);
        return trackPanelRow;
      }

      addToControlPanel(chordSet) {
        // single track row - container for hide-button | color-picker-swatch | track-name
        const row = document.createElement('div');
        this.controlPanel.appendChild(row); // track hide|show

        const hideShowButton = document.createElement('div');
        hideShowButton.className = 'igv-circview-button';
        row.appendChild(hideShowButton);
        hideShowButton.innerText = true === chordSet.visible ? 'Hide' : 'Show';
        hideShowButton.addEventListener('click', event => {
          if (true === chordSet.visible) {
            this.hideChordSet(chordSet.name);
            event.target.innerText = "Show";
          } else {
            this.showChordSet(chordSet.name);
            event.target.innerText = "Hide";
          }
        }); // The alpha range slider.  Create this here so we can reference it from the color picker

        const alphaSlider = document.createElement('input');

        const valueToAlpha = value => Math.exp(value / 200) / EXP5;

        const alphaToValue = alpha => 200 * Math.log(alpha * EXP5); // color


        const colorPickerButton = document.createElement('div');
        colorPickerButton.className = 'igv-circview-button';
        colorPickerButton.innerHTML = '&nbsp;&nbsp;&nbsp;&nbsp;'; // <- important for button to size properly

        row.appendChild(colorPickerButton);
        colorPickerButton.style.backgroundColor = setAlpha(chordSet.color, 1);
        const pickerConfig = {
          parent: colorPickerButton,
          popup: 'right',
          editorFormat: 'rgb',
          color: chordSet.color,
          onChange: ({
            rgbaString
          }) => {
            colorPickerButton.style.backgroundColor = setAlpha(rgbaString, 1);
            this.setColor(chordSet.name, rgbaString);
            alphaSlider.value = alphaToValue(getAlpha(chordSet.color));
          }
        };
        const picker = new Picker(pickerConfig); // alpha transparency

        alphaSlider.setAttribute('title', 'Adjust transparency of arcs');
        alphaSlider.type = 'range'; //alphaSlider.className = 'igv-circview-alpha-slider'

        alphaSlider.style.width = '100px';
        alphaSlider.style.marginRight = '10px';
        alphaSlider.setAttribute('class', 'range');
        alphaSlider.setAttribute('min', '0');
        alphaSlider.setAttribute('max', '1000');
        alphaSlider.value = alphaToValue(getAlpha(chordSet.color));

        alphaSlider.oninput = () => {
          const v = valueToAlpha(alphaSlider.value);
          this.setColor(chordSet.name, setAlpha(chordSet.color, v));
          picker.setColor(chordSet.color);
        };

        row.appendChild(alphaSlider); // track name

        const trackNameDive = document.createElement('div');
        trackNameDive.style.color = 'black';
        row.appendChild(trackNameDive);
        trackNameDive.innerText = trackNameDive.title = chordSet.name;
      }
      /**
       * Reset view with a new set of chromosomes.
       *
       * @param igvGenome {name: string, id: string, chromosomes: [{name: string, bpLength: integer, color: string}
       */


      setAssembly(igvGenome) {
        const id = this.genomeId || guid();

        if (this.genomeId === id) {
          return;
        }

        this.chordManager.clearChords();
        this.genomeId = id;
        this.chrNames = new Set(igvGenome.chromosomes.map(chr => shortChrName$1(chr.name)));
        const regions = [];
        const colors = [];

        for (let chr of igvGenome.chromosomes) {
          const shortName = shortChrName$1(chr.name);
          colors.push(chr.color || getChrColor$1(shortName));
          regions.push({
            refName: shortName,
            uniqueId: shortName,
            start: 0,
            end: chr.bpLength
          });
        }

        this.assembly = {
          name: igvGenome.name,
          sequence: {
            trackId: id,
            type: 'ReferenceSequenceTrack',
            adapter: {
              type: 'FromConfigSequenceAdapter',
              features: regions
            }
          },
          refNameColors: colors
        };
        this.render();
      }
      /**
       * Append or replace current set of chords to the global set or a specific track.
       *
       * @param newChords array of chord feature objects.  Example:
       * [
       *   {
       *     "uniqueId": "chr1:129763372-129763376_chr1:129806775-129806790",
       *     "color": "rgba(0, 0, 255, 0.1)",
       *     "refName": "1",
       *     "start": 129763372,
       *     "end": 129763376,
       *     "mate": {
       *       "refName": "2",
       *       "start": 129806775,
       *       "end": 129806790
       *     }
       *   }
       * ]
       * @param options {
       *     name: string,    // Track name
       *     color: string,   // Track color
       *     append: boolean  // Replace or append chords to current set.  Default is append (true)
       * }
       */


      addChords(newChords, options = {}) {
        const tmp = options.name || options.track || "*";
        const trackName = tmp.split(' ')[0].replaceAll("%20", " ");
        const chordSetName = tmp.replaceAll("%20", " ");
        const chordSet = {
          name: chordSetName,
          trackName: trackName,
          chords: newChords,
          color: options.color || "black",
          trackColor: options.trackColor || options.color || "black",
          visible: true,
          id: options.id || guid()
        };
        this.chordManager.addChordSet(chordSet);
        this.resetControlPanel();
        this.render();
      }
      /**
       * Set the nominal size of the view in pixels.  Size is reduced some aribtrary amount to account for borders and margins
       */


      setSize(width, height) {
        height = height || width;
        this.width = width;
        this.height = height;

        if (this.viewState) {
          const view = this.viewState.session.view;
          view.setWidth(width);
          view.setHeight(height
          /* this is the height of the area inside the border in pixels */
          );
          view.setBpPerPx(view.minBpPerPx);
        }
      }

      getSize() {
        return Math.min(this.width, this.height);
      }

      clearChords() {
        //this.tracks = []
        this.chordManager.clearChords();
        this.resetControlPanel();
        this.render();
      }

      clearSelection() {
        this.viewState.pluginManager.rootModel.session.clearSelection();
      }
      /**
       * Deprecated, use "visible" property
       */


      show() {
        this.parent.style.display = 'block';
      }
      /**
       * Deprecated, use "visible" property
       */


      hide() {
        this.parent.style.display = 'none';
      }

      get visible() {
        return this.parent.style.display !== 'none';
      }

      set visible(isVisible) {
        this.parent.style.display = isVisible ? 'block' : 'none';
      }

      hideChordSet(trackName) {
        let cs = this.getChordSet(trackName);

        if (cs) {
          cs.visible = false;
          this.render();
        } else {
          console.warn(`No track with name: ${name}`);
        }
      }

      showChordSet(name) {
        let cs = this.getChordSet(name);

        if (cs) {
          cs.visible = true;
          this.render();
        } else {
          console.warn(`No track with name: ${name}`);
        }
      } // showTrack(trackID) {
      //     let idx = this.tracks.findIndex(t => trackID === t.id)
      //     if (idx >= 0) {
      //         const track = this.tracks[idx]
      //         track.visible = true
      //         this.tracks.splice(idx, 1)   // Change z-order
      //         this.tracks.push(track)
      //         this.render()
      //     } else {
      //         console.warn(`No track with name: ${name}`)
      //     }
      // }
      // TODO -- remove corresponding row from track panel


      deleteTrack(trackID) {
        let idx = this.tracks.findIndex(t => trackID === t.name);

        if (idx >= 0) {
          this.tracks.splice(idx, 1);
        }

        this.render();
      }

      getChordSet(name) {
        return this.groupByTrack ? this.chordManager.getTrack(name) : this.chordManager.getChordSet(name);
      }

      setColor(name, color) {
        const t = this.getChordSet(name);

        if (t) {
          t.color = color;
          const trackID = t.id;

          for (let jbrowseTrack of this.viewState.config.tracks) {
            if (trackID === jbrowseTrack.trackId) {
              jbrowseTrack.displays[0].renderer.strokeColor.set(color);
              break;
            }
          }
        }
      }
      /**
       * The main render function.  Render here means build the React DOM.  Trying to change react state dynamically
       * has been buggy, so we completely rebuild the DOM ("render") on any state change.
       */


      render() {
        const {
          createViewState,
          JBrowseCircularGenomeView
        } = JBrowseReactCircularGenomeView; // Remove all children from possible previous renders.  React might do this for us when we render, but just in case.

        ReactDOM.unmountComponentAtNode(this.container);
        const visibleChordSets = (this.groupByTrack ? this.chordManager.tracks : this.chordManager.chordSets).filter(t => t.visible);
        const jbrowseTracks = [];
        const colors = [];

        for (let chordSet of visibleChordSets) {
          jbrowseTracks.push({
            trackId: chordSet.id,
            name: chordSet.name,
            assemblyNames: ['forIGV'],
            type: 'VariantTrack',
            adapter: {
              type: 'FromConfigAdapter',
              features: chordSet.chords
            }
          });
          colors.push(chordSet.color);
        }

        this.viewState = createViewState({
          assembly: this.assembly,
          tracks: jbrowseTracks
        }); // Set view colors

        for (let i = 0; i < visibleChordSets.length; i++) {
          this.viewState.config.tracks[i].displays[0].renderer.strokeColor.set(colors[i]); //this.viewState.config.tracks[i].displays[0].renderer.strokeColor.set("jexl:get(feature, 'color') || 'black'");
          //this.viewState.config.tracks[i].displays[0].renderer.strokeColorSelected.set("jexl:get(feature, 'highlightColor') || 'red'");
        }

        this.element = React.createElement(JBrowseCircularGenomeView, {
          viewState: this.viewState
        });
        this.setSize(this.width, this.height);
        ReactDOM.render(this.element, this.container);
        const onChordClick = this.config.onChordClick || defaultOnChordClick;

        for (let i = 0; i < visibleChordSets.length; i++) {
          this.viewState.session.view.showTrack(this.viewState.config.tracks[i].trackId);

          if (onChordClick) {
            this.viewState.pluginManager.jexl.addFunction('onChordClick', onChordClick);
            this.viewState.config.tracks[i].displays[0].onChordClick.set('jexl:onChordClick(feature, track, pluginManager)');
          }
        }
      }

    }

    function setAlpha(rgba, alpha) {
      const [a, b, c, ignore] = rgba.split(','); // rgba(r g b alpha)

      return `${a},${b},${c},${alpha})`;
    }

    function getAlpha(rgba) {
      if (rgba.startsWith("rgba(")) {
        return Number(rgba.split(',')[3].replace(')', ''));
      } else {
        return 1;
      }
    }

    function shortChrName$1(chrName) {
      return chrName.startsWith("chr") ? chrName.substring(3) : chrName;
    }

    function defaultOnChordClick(feature, chordTrack, pluginManager) {
      console.log(feature);
    }

    function guid() {
      return ("0000" + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4);
    }

    function embedCSS$1() {
      const css = '.igv-circview-container {\n  z-index: 2048;\n  width: fit-content;\n  height: fit-content;\n  box-sizing: content-box;\n  color: dimgray;\n  font-family: \"Open Sans\", sans-serif;\n  font-size: 12px;\n  background-color: white;\n  border-color: dimgray;\n  border-style: solid;\n  border-width: thin;\n  display: flex;\n  flex-direction: column;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: flex-start;\n}\n\n.igv-circview-toolbar {\n  position: relative;\n  width: 100%;\n  height: 32px;\n  background-color: lightgrey;\n  border-bottom-style: solid;\n  border-bottom-color: dimgray;\n  border-bottom-width: thin;\n  display: flex;\n  flex-flow: row;\n  flex-wrap: nowrap;\n  justify-content: space-between;\n  align-items: center;\n}\n\n.igv-circview-toolbar-button-container {\n  height: 100%;\n  width: fit-content;\n  display: flex;\n  flex-flow: row;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: center;\n}\n.igv-circview-toolbar-button-container > div {\n  margin: 4px;\n}\n\n.igv-circview-track-panel {\n  z-index: 1024;\n  position: absolute;\n  top: 33px;\n  left: 0;\n  width: 100%;\n  height: fit-content;\n  border-bottom-style: solid;\n  border-bottom-color: dimgray;\n  border-bottom-width: thin;\n  background-color: white;\n  display: flex;\n  flex-flow: column;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: flex-start;\n}\n.igv-circview-track-panel > div {\n  display: flex;\n  flex-flow: row;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: center;\n}\n.igv-circview-track-panel > div > div {\n  margin: 4px;\n}\n\n.igv-circview-swatch-button {\n  cursor: pointer;\n  padding: 5px;\n  width: 8px;\n  height: 8px;\n  border: 1px solid #8d8b8b;\n  border-radius: 16px;\n}\n\n.igv-circview-button {\n  cursor: pointer;\n  padding: 5px;\n  color: #444;\n  vertical-align: middle;\n  text-align: center;\n  font-family: \"Open Sans\", sans-serif;\n  font-size: 12px;\n  border: 1px solid #8d8b8b;\n  border-radius: 4px;\n  background: #efefef;\n  box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.2);\n}\n\n.igv-circview-button:hover {\n  background: #efefef;\n  box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n.igv-circview-button:active {\n  color: #007bff;\n  box-shadow: 0 0 5px -1px rgba(0, 0, 0, 0.6);\n}\n\n/*# sourceMappingURL=circular-view.css.map */\n';
      const style = document.createElement('style');
      style.setAttribute('type', 'text/css');
      style.innerHTML = css;
      document.head.insertBefore(style, document.head.childNodes[document.head.childNodes.length - 1]);
    }

    if (typeof document !== 'undefined') {
      if (!stylesheetExists("circular-view.css")) {
        embedCSS$1();
      }

      function stylesheetExists(stylesheetName) {
        for (let ss of document.styleSheets) {
          ss = ss.href ? ss.href.replace(/^.*[\\\/]/, '') : '';

          if (ss === stylesheetName) {
            return true;
          }
        }

        return false;
      }
    }

    /**
     * The minimum length for a VCF structural variant.  VCF records < this length are ignored in the circular view
     * @type {number}
     */

    const MINIMUM_SV_LENGTH = 1000000;

    const shortChrName = chrName => {
      return chrName.startsWith("chr") ? chrName.substring(3) : chrName;
    };

    const makePairedAlignmentChords = alignments => {
      const chords = [];

      for (let a of alignments) {
        if (a.paired) {
          if (a.firstAlignment && a.secondAlignment) {
            chords.push({
              uniqueId: a.readName,
              refName: shortChrName(a.firstAlignment.chr),
              start: a.firstAlignment.start,
              end: a.firstAlignment.end,
              mate: {
                refName: shortChrName(a.secondAlignment.chr),
                start: a.secondAlignment.start,
                end: a.secondAlignment.end
              }
            });
          }
        } else {
          const mate = a.mate;

          if (mate && mate.chr && mate.position) {
            chords.push({
              uniqueId: a.readName,
              refName: shortChrName(a.chr),
              start: a.start,
              end: a.end,
              mate: {
                refName: shortChrName(mate.chr),
                start: mate.position - 1,
                end: mate.position
              }
            });
          }
        }
      }

      return chords;
    };

    const makeSupplementalAlignmentChords = alignments => {
      const makeChords = a => {
        const sa = a.tags()['SA'];
        const supAl = createSupplementaryAlignments(sa);
        let n = 0;

        for (let s of supAl) {
          if (s.start !== a.start) {
            chords.push({
              uniqueId: `${a.readName}_${n++}`,
              refName: shortChrName(a.chr),
              start: a.start,
              end: a.end,
              mate: {
                refName: shortChrName(s.chr),
                start: s.start,
                end: s.start + s.lenOnRef
              }
            });
          }
        }
      };

      const chords = [];

      for (let a of alignments) {
        if (a.paired) {
          makeChords(a.firstAlignment);

          if (a.secondAlignment) {
            makeChords(a.secondAlignment);
          }
        } else {
          makeChords(a);
        }
      }

      return chords;
    };

    const makeBedPEChords = features => {
      return features.map(v => {
        // If v is a whole-genome feature, get the true underlying variant.
        const f = v._f || v;
        return {
          uniqueId: `${f.chr1}:${f.start1}-${f.end1}_${f.chr2}:${f.start2}-${f.end2}`,
          refName: shortChrName(f.chr1),
          start: f.start1,
          end: f.end1,
          mate: {
            refName: shortChrName(f.chr2),
            start: f.start2,
            end: f.end2
          }
        };
      });
    };

    const makeVCFChords = features => {
      const svFeatures = features.filter(v => {
        const f = v._f || v;
        const isLargeEnough = f.info && f.info.CHR2 && f.info.END && (f.info.CHR2 !== f.chr || Math.abs(Number.parseInt(f.info.END) - f.pos) > MINIMUM_SV_LENGTH);
        return isLargeEnough;
      });
      return svFeatures.map(v => {
        // If v is a whole-genome feature, get the true underlying variant.
        const f = v._f || v;
        const pos2 = Number.parseInt(f.info.END);
        const start2 = pos2 - 100;
        const end2 = pos2 + 100;
        return {
          uniqueId: `${f.chr}:${f.start}-${f.end}_${f.info.CHR2}:${f.info.END}`,
          refName: shortChrName(f.chr),
          start: f.start,
          end: f.end,
          mate: {
            refName: shortChrName(f.info.CHR2),
            start: start2,
            end: end2
          }
        };
      });
    };

    function makeCircViewChromosomes(genome) {
      const regions = [];
      const colors = [];

      for (let chrName of genome.wgChromosomeNames) {
        const chr = genome.getChromosome(chrName);
        colors.push(getChrColor(chr.name));
        regions.push({
          name: chr.name,
          bpLength: chr.bpLength
        });
      }

      return regions;
    }

    function sendChords(chords, track, refFrame, alpha) {
      const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? track.color : getChrColor(refFrame.chr), alpha);
      const trackColor = IGVColor.addAlpha(track.color || 'rgb(0,0,255)', alpha); // name the chord set to include locus and filtering information

      const encodedName = track.name.replaceAll(' ', '%20');
      const chordSetName = "all" === refFrame.chr ? encodedName : `${encodedName}  ${refFrame.chr}:${refFrame.start}-${refFrame.end}`;
      track.browser.circularView.addChords(chords, {
        track: chordSetName,
        color: chordSetColor,
        trackColor: trackColor
      }); // show circular view if hidden

      if (!track.browser.circularViewVisible) track.browser.circularViewVisible = true;
    }

    function createCircularView(el, browser) {
      const circularView = new CircularView(el, {
        onChordClick: (feature, chordTrack, pluginManager) => {
          const f1 = feature.data;
          const f2 = f1.mate;
          addFrameForFeature(f1);
          addFrameForFeature(f2);

          function addFrameForFeature(feature) {
            feature.chr = browser.genome.getChromosomeName(feature.refName);
            let frameFound = false;

            for (let referenceFrame of browser.referenceFrameList) {
              const l = Locus.fromLocusString(referenceFrame.getLocusString());

              if (l.contains(feature)) {
                frameFound = true;
                break;
              } else if (l.overlaps(feature)) {
                referenceFrame.extend(feature);
                frameFound = true;
                break;
              }
            }

            if (!frameFound) {
              const flanking = 2000;
              const center = (feature.start + feature.end) / 2;
              browser.addMultiLocusPanel(feature.chr, center - flanking, center + flanking);
            }
          }
        }
      });
      return circularView;
    }

    class PairedEndStats {
      constructor(alignments, _ref) {
        let {
          minTLENPercentile,
          maxTLENPercentile
        } = _ref;
        this.totalCount = 0;
        this.frCount = 0;
        this.rfCount = 0;
        this.ffCount = 0;
        this.sumF = 0;
        this.sumF2 = 0;
        this.lp = minTLENPercentile === undefined ? 0.1 : minTLENPercentile;
        this.up = maxTLENPercentile === undefined ? 99.5 : maxTLENPercentile;
        this.isizes = [];
        this.compute(alignments);
      }

      compute(alignments) {
        for (let alignment of alignments) {
          if (alignment.isProperPair()) {
            var tlen = Math.abs(alignment.fragmentLength);
            this.sumF += tlen;
            this.sumF2 += tlen * tlen;
            this.isizes.push(tlen);
            var po = alignment.pairOrientation;

            if (typeof po === "string" && po.length === 4) {
              var tmp = '' + po.charAt(0) + po.charAt(2);

              switch (tmp) {
                case 'FF':
                case 'RR':
                  this.ffCount++;
                  break;

                case "FR":
                  this.frCount++;
                  break;

                case "RF":
                  this.rfCount++;
              }
            }

            this.totalCount++;
          }
        }

        if (this.ffCount / this.totalCount > 0.9) this.orienation = "ff";else if (this.frCount / this.totalCount > 0.9) this.orienation = "fr";else if (this.rfCount / this.totalCount > 0.9) this.orienation = "rf";
        this.minTLEN = this.lp === 0 ? 0 : percentile(this.isizes, this.lp);
        this.maxTLEN = percentile(this.isizes, this.up); // var fMean = this.sumF / this.totalCount
        // var stdDev = Math.sqrt((this.totalCount * this.sumF2 - this.sumF * this.sumF) / (this.totalCount * this.totalCount))
        // this.minTLEN = fMean - 3 * stdDev
        // this.maxTLEN = fMean + 3 * stdDev
      }

    }

    function percentile(array, p) {
      if (array.length === 0) return undefined;
      var k = Math.floor(array.length * (p / 100));
      array.sort(function (a, b) {
        return a - b;
      });
      return array[k];
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const alignmentStartGap = 5;
    const downsampleRowHeight = 5;
    const DEFAULT_COVERAGE_TRACK_HEIGHT = 50;
    const DEFAULT_TRACK_HEIGHT = 300;
    const DEFAULT_ALIGNMENT_COLOR = "rgb(185, 185, 185)";
    const DEFAULT_COVERAGE_COLOR = "rgb(150, 150, 150)";
    const DEFAULT_CONNECTOR_COLOR = "rgb(200, 200, 200)";

    class BAMTrack extends TrackBase {
      constructor(config, browser) {
        super(config, browser);
      }

      init(config) {
        super.init(config);
        this.type = "alignment";

        if (config.alleleFreqThreshold === undefined) {
          config.alleleFreqThreshold = 0.2;
        }

        this.featureSource = new BamSource(config, this.browser);
        this.showCoverage = config.showCoverage === undefined ? true : config.showCoverage;
        this.showAlignments = config.showAlignments === undefined ? true : config.showAlignments;
        this.coverageTrack = new CoverageTrack(config, this);
        this.alignmentTrack = new AlignmentTrack(config, this);
        this.alignmentTrack.setTop(this.coverageTrack, this.showCoverage);
        this.visibilityWindow = config.visibilityWindow || 30000;
        this.viewAsPairs = config.viewAsPairs;
        this.pairsSupported = config.pairsSupported !== false;
        this.showSoftClips = config.showSoftClips;
        this.showAllBases = config.showAllBases;
        this.showInsertions = false !== config.showInsertions;
        this.showMismatches = false !== config.showMismatches;
        this.color = config.color;
        this.coverageColor = config.coverageColor; // The sort object can be an array in the case of multi-locus view, however if multiple sort positions
        // are present for a given reference frame the last one will take precedence

        if (config.sort) {
          if (Array.isArray(config.sort)) {
            // Legacy support
            this.assignSort(config.sort[0]);
          } else {
            this.assignSort(config.sort);
          }
        } // Invoke height setter last to allocated to coverage and alignment tracks


        this.height = config.height !== undefined ? config.height : DEFAULT_TRACK_HEIGHT;
      }

      set height(h) {
        this._height = h;

        if (this.coverageTrack && this.showAlignments) {
          this.alignmentTrack.height = this.showCoverage ? h - this.coverageTrack.height : h;
        }
      }

      get height() {
        return this._height;
      }

      get minTemplateLength() {
        const configMinTLEN = this.config.minTLEN !== undefined ? this.config.minTLEN : this.config.minFragmentLength;
        return configMinTLEN !== undefined ? configMinTLEN : this._pairedEndStats ? this._pairedEndStats.minTLEN : 0;
      }

      get maxTemplateLength() {
        const configMaxTLEN = this.config.maxTLEN !== undefined ? this.config.maxTLEN : this.config.maxFragmentLength;
        return configMaxTLEN !== undefined ? configMaxTLEN : this._pairedEndStats ? this._pairedEndStats.maxTLEN : 1000;
      }

      sort(options) {
        options = this.assignSort(options);

        for (let vp of this.trackView.viewports) {
          if (vp.containsPosition(options.chr, options.position)) {
            const alignmentContainer = vp.cachedFeatures;

            if (alignmentContainer) {
              sortAlignmentRows(options, alignmentContainer);
              vp.repaint();
            }
          }
        }
      }
      /**
       * Fix syntax problems for sort options.
       * @param options
       */


      assignSort(options) {
        // convert old syntax
        if (options.locus) {
          const range = parseLocusString$1(options.locus);
          options.chr = range.chr;
          options.position = range.start;
        } else {
          options.position--;
        }

        options.direction = options.direction === "ASC" || options.direction === true; // chr aliasing

        options.chr = this.browser.genome.getChromosomeName(options.chr);
        this.sortObject = options;
        return this.sortObject;
      }

      async getFeatures(chr, bpStart, bpEnd, bpPerPixel, viewport) {
        const alignmentContainer = await this.featureSource.getAlignments(chr, bpStart, bpEnd);

        if (alignmentContainer.paired && !this._pairedEndStats && !this.config.maxFragmentLength) {
          const pairedEndStats = new PairedEndStats(alignmentContainer.alignments, this.config);

          if (pairedEndStats.totalCount > 99) {
            this._pairedEndStats = pairedEndStats;
          }
        }

        alignmentContainer.alignments = undefined; // Don't need to hold onto these anymore

        const sort = this.sortObject;

        if (sort) {
          if (sort.chr === chr && sort.position >= bpStart && sort.position <= bpEnd) {
            sortAlignmentRows(sort, alignmentContainer);
          }
        }

        return alignmentContainer;
      }
      /**
       * Compute the pixel height required to display all content.  This is not the same as the viewport height
       * (track.height) which might include a scrollbar.
       *
       * @param alignmentContainer
       * @returns {number}
       */


      computePixelHeight(alignmentContainer) {
        return (this.showCoverage ? this.coverageTrack.height : 0) + (this.showAlignments ? this.alignmentTrack.computePixelHeight(alignmentContainer) : 0) + 15;
      }

      draw(options) {
        IGVGraphics.fillRect(options.context, 0, options.pixelTop, options.pixelWidth, options.pixelHeight, {
          'fillStyle': "rgb(255, 255, 255)"
        });

        if (true === this.showCoverage && this.coverageTrack.height > 0) {
          this.trackView.axisCanvas.style.display = 'block';
          this.coverageTrack.draw(options);
        } else {
          this.trackView.axisCanvas.style.display = 'none';
        }

        if (true === this.showAlignments) {
          this.alignmentTrack.setTop(this.coverageTrack, this.showCoverage);
          this.alignmentTrack.draw(options);
        }
      }

      paintAxis(ctx, pixelWidth, pixelHeight) {
        this.coverageTrack.paintAxis(ctx, pixelWidth, this.coverageTrack.height); // if (this.browser.isMultiLocusMode()) {
        //     ctx.clearRect(0, 0, pixelWidth, pixelHeight);
        // } else {
        //     this.coverageTrack.paintAxis(ctx, pixelWidth, this.coverageTrack.height);
        // }
      }

      contextMenuItemList(config) {
        return this.alignmentTrack.contextMenuItemList(config);
      }

      popupData(clickState) {
        if (true === this.showCoverage && clickState.y >= this.coverageTrack.top && clickState.y < this.coverageTrack.height) {
          return this.coverageTrack.popupData(clickState);
        } else {
          return this.alignmentTrack.popupData(clickState);
        }
      }
      /**
       * Return the features (alignment, coverage, downsampled interval) clicked on.  Needed for "onclick" event.
       * @param clickState
       * @param features
       */


      clickedFeatures(clickState) {
        if (true === this.showCoverage && clickState.y >= this.coverageTrack.top && clickState.y < this.coverageTrack.height) {
          return [this.coverageTrack.getClickedObject(clickState)];
        } else {
          return [this.alignmentTrack.getClickedObject(clickState)];
        }
      }

      menuItemList() {
        // Start with overage track items
        let menuItems = [];
        menuItems = menuItems.concat(MenuUtils.numericDataMenuItems(this.trackView)); // Color by items

        menuItems.push('<hr/>');
        const $e = $$1('<div class="igv-track-menu-category">');
        $e.text('Color by:');
        menuItems.push({
          name: undefined,
          object: $e,
          click: undefined,
          init: undefined
        });
        const colorByMenuItems = [{
          key: 'strand',
          label: 'read strand'
        }];

        if (this.alignmentTrack.hasPairs) {
          colorByMenuItems.push({
            key: 'firstOfPairStrand',
            label: 'first-of-pair strand'
          });
          colorByMenuItems.push({
            key: 'pairOrientation',
            label: 'pair orientation'
          });
          colorByMenuItems.push({
            key: 'tlen',
            label: 'insert size (TLEN)'
          });
          colorByMenuItems.push({
            key: 'unexpectedPair',
            label: 'pair orientation & insert size (TLEN)'
          });
        }

        const tagLabel = 'tag' + (this.alignmentTrack.colorByTag ? ' (' + this.alignmentTrack.colorByTag + ')' : '');
        colorByMenuItems.push({
          key: 'tag',
          label: tagLabel
        });

        for (let item of colorByMenuItems) {
          const selected = this.alignmentTrack.colorBy === item.key;
          menuItems.push(this.colorByCB(item, selected));
        } // Show coverage / alignment options


        const adjustTrackHeight = () => {
          if (!this.autoHeight) {
            const h = 15 + (this.showCoverage ? this.coverageTrack.height : 0) + (this.showAlignments ? this.alignmentTrack.height : 0);
            this.trackView.setTrackHeight(h);
          }
        };

        menuItems.push('<hr/>');
        menuItems.push({
          object: $$1(createCheckbox$1("Show Coverage", this.showCoverage)),
          click: () => {
            this.showCoverage = !this.showCoverage;
            adjustTrackHeight();
            this.trackView.checkContentHeight();
            this.trackView.repaintViews();
          }
        });
        menuItems.push({
          object: $$1(createCheckbox$1("Show Alignments", this.showAlignments)),
          click: () => {
            this.showAlignments = !this.showAlignments;
            adjustTrackHeight();
            this.trackView.checkContentHeight();
            this.trackView.repaintViews();
          }
        }); // Show all bases

        menuItems.push('<hr/>');
        menuItems.push({
          object: $$1(createCheckbox$1("Show all bases", this.showAllBases)),
          click: () => {
            this.showAllBases = !this.showAllBases;
            this.config.showAllBases = this.showAllBases;
            this.trackView.repaintViews();
          }
        }); // Show mismatches

        menuItems.push('<hr/>');
        menuItems.push({
          object: $$1(createCheckbox$1("Show mismatches", this.showMismatches)),
          click: () => {
            this.showMismatches = !this.showMismatches;
            this.config.showMismatches = this.showMismatches;
            this.trackView.repaintViews();
          }
        }); // Insertions

        menuItems.push({
          object: $$1(createCheckbox$1("Show insertions", this.showInsertions)),
          click: () => {
            this.showInsertions = !this.showInsertions;
            this.config.showInsertions = this.showInsertions;
            this.getCachedAlignmentContainers();
            this.trackView.repaintViews();
          }
        }); // Soft clips

        menuItems.push({
          object: $$1(createCheckbox$1("Show soft clips", this.showSoftClips)),
          click: () => {
            this.showSoftClips = !this.showSoftClips;
            this.config.showSoftClips = this.showSoftClips;
            this.featureSource.setShowSoftClips(this.showSoftClips);
            const alignmentContainers = this.getCachedAlignmentContainers();

            for (let ac of alignmentContainers) {
              ac.setShowSoftClips(this.showSoftClips);
            }

            this.trackView.repaintViews();
          }
        }); // View as pairs

        if (this.pairsSupported && this.alignmentTrack.hasPairs) {
          menuItems.push('<hr/>');
          menuItems.push({
            object: $$1(createCheckbox$1("View as pairs", this.viewAsPairs)),
            click: () => {
              this.viewAsPairs = !this.viewAsPairs;
              this.config.viewAsPairs = this.viewAsPairs;
              this.featureSource.setViewAsPairs(this.viewAsPairs);
              const alignmentContainers = this.getCachedAlignmentContainers();

              for (let ac of alignmentContainers) {
                ac.setViewAsPairs(this.viewAsPairs);
              }

              this.trackView.repaintViews();
            }
          });
        } // Add chords to JBrowse circular view, if present


        if (this.browser.circularView && (this.alignmentTrack.hasPairs || this.alignmentTrack.hasSupplemental)) {
          menuItems.push('<hr/>');

          if (this.alignmentTrack.hasPairs) {
            menuItems.push({
              label: 'Add discordant pairs to circular view',
              click: () => {
                for (let viewport of this.trackView.viewports) {
                  this.addPairedChordsForViewport(viewport);
                }
              }
            });
          }

          if (this.alignmentTrack.hasSupplemental) {
            menuItems.push({
              label: 'Add split reads to circular view',
              click: () => {
                for (let viewport of this.trackView.viewports) {
                  this.addSplitChordsForViewport(viewport);
                }
              }
            });
          }
        } // Display mode


        menuItems.push('<hr/>');
        const $dml = $$1('<div class="igv-track-menu-category">');
        $dml.text('Display mode:');
        menuItems.push({
          name: undefined,
          object: $dml,
          click: undefined,
          init: undefined
        });
        menuItems.push({
          object: $$1(createCheckbox$1("expand", this.alignmentTrack.displayMode === "EXPANDED")),
          click: () => {
            this.alignmentTrack.displayMode = "EXPANDED";
            this.config.displayMode = "EXPANDED";
            this.trackView.checkContentHeight();
            this.trackView.repaintViews();
          }
        });
        menuItems.push({
          object: $$1(createCheckbox$1("squish", this.alignmentTrack.displayMode === "SQUISHED")),
          click: () => {
            this.alignmentTrack.displayMode = "SQUISHED";
            this.config.displayMode = "SQUISHED";
            this.trackView.checkContentHeight();
            this.trackView.repaintViews();
          }
        });
        return menuItems;
      }
      /**
       * Create a "color by" checkbox menu item, optionally initially checked
       * @param menuItem
       * @param showCheck
       * @returns {{init: undefined, name: undefined, click: clickHandler, object: (jQuery|HTMLElement|jQuery.fn.init)}}
       */


      colorByCB(menuItem, showCheck) {
        const $e = $$1(createCheckbox$1(menuItem.label, showCheck));

        const clickHandler = ev => {
          if (menuItem.key !== 'tag') {
            if (menuItem.key === this.alignmentTrack.colorBy) {
              this.alignmentTrack.colorBy = 'none';
              this.config.colorBy = 'none';
              this.trackView.repaintViews();
            } else {
              this.alignmentTrack.colorBy = menuItem.key;
              this.config.colorBy = menuItem.key;
              this.trackView.repaintViews();
            }
          } else {
            this.browser.inputDialog.present({
              label: 'Tag Name',
              value: this.alignmentTrack.colorByTag ? this.alignmentTrack.colorByTag : '',
              callback: tag => {
                if (tag) {
                  this.alignmentTrack.colorBy = 'tag';
                  this.alignmentTrack.colorByTag = tag;

                  if (!this.alignmentTrack.tagColors) {
                    this.alignmentTrack.tagColors = new PaletteColorTable("Set1");
                  }
                } else {
                  this.alignmentTrack.colorBy = 'none';
                  this.alignmentTrack.colorByTag = '';
                }

                this.trackView.repaintViews();
              }
            }, ev);
          }
        };

        return {
          name: undefined,
          object: $e,
          click: clickHandler,
          init: undefined
        };
      }
      /**
       * Called when the track is removed.  Do any needed cleanup here
       */


      dispose() {
        this.trackView = undefined;
      }
      /**
       * Return the current state of the track.  Used to create sessions and bookmarks.
       *
       * @returns {*|{}}
       */


      getState() {
        const config = super.getState();

        if (this.sortObject) {
          config.sort = {
            chr: this.sortObject.chr,
            position: this.sortObject.position + 1,
            option: this.sortObject.option,
            direction: this.sortObject.direction ? "ASC" : "DESC"
          };
        }

        return config;
      }

      getCachedAlignmentContainers() {
        return this.trackView.viewports.map(vp => vp.cachedFeatures);
      }

      get dataRange() {
        return this.coverageTrack.dataRange;
      }

      set dataRange(dataRange) {
        this.coverageTrack.dataRange = dataRange;
      }

      get logScale() {
        return this.coverageTrack.logScale;
      }

      set logScale(logScale) {
        this.coverageTrack.logScale = logScale;
      }

      get autoscale() {
        return this.coverageTrack.autoscale;
      }

      set autoscale(autoscale) {
        this.coverageTrack.autoscale = autoscale;
      }
      /**
       * Add chords to the circular view for the given viewport, represented by its reference frame
       * @param refFrame
       */


      addPairedChordsForViewport(viewport) {
        const maxTemplateLength = this.maxTemplateLength;
        const inView = [];
        const refFrame = viewport.referenceFrame;

        for (let a of viewport.cachedFeatures.allAlignments()) {
          if (a.end >= refFrame.start && a.start <= refFrame.end) {
            if (a.paired) {
              if (a.end - a.start > maxTemplateLength) {
                inView.push(a);
              }
            } else {
              if (a.mate && a.mate.chr && (a.mate.chr !== a.chr || Math.max(a.fragmentLength) > maxTemplateLength)) {
                inView.push(a);
              }
            }
          }
        }

        const chords = makePairedAlignmentChords(inView);
        sendChords(chords, this, refFrame, 0.02); // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
        // const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
        //
        // // name the chord set to include track name and locus
        // const encodedName = this.name.replaceAll(' ', '%20')
        // const chordSetName = "all" === refFrame.chr ? encodedName :
        //     `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
        // this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
      }

      addSplitChordsForViewport(viewport) {
        const inView = [];
        const refFrame = viewport.referenceFrame;

        for (let a of viewport.cachedFeatures.allAlignments()) {
          const sa = a.hasTag('SA');

          if (a.end >= refFrame.start && a.start <= refFrame.end && sa) {
            inView.push(a);
          }
        }

        const chords = makeSupplementalAlignmentChords(inView);
        sendChords(chords, this, refFrame, 0.02); // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.02)
        // const trackColor = IGVColor.addAlpha(this.color || 'rgb(0,0,255)', 0.02)
        //
        // // name the chord set to include track name and locus
        // const encodedName = this.name.replaceAll(' ', '%20')
        // const chordSetName = "all" === refFrame.chr ? encodedName :
        //     `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end}`
        // this.browser.circularView.addChords(chords, {name: chordSetName, color: chordSetColor, trackColor: trackColor})
      }

    }

    class CoverageTrack {
      constructor(config, parent) {
        this.parent = parent;
        this.featureSource = parent.featureSource;
        this.height = config.coverageTrackHeight !== undefined ? config.coverageTrackHeight : DEFAULT_COVERAGE_TRACK_HEIGHT;
        this.paintAxis = paintAxis;
        this.top = 0;
        this.autoscale = config.autoscale || config.max === undefined;

        if (!this.autoscale) {
          this.dataRange = {
            min: config.min || 0,
            max: config.max
          };
        }
      }

      draw(options) {
        const pixelTop = options.pixelTop;
        pixelTop + options.pixelHeight;
        const nucleotideColors = this.parent.browser.nucleotideColors;

        if (pixelTop > this.height) {
          return; //scrolled out of view
        }

        const ctx = options.context;
        const alignmentContainer = options.features;
        const coverageMap = alignmentContainer.coverageMap;
        let sequence;

        if (coverageMap.refSeq) {
          sequence = coverageMap.refSeq.toUpperCase();
        }

        const bpPerPixel = options.bpPerPixel;
        const bpStart = options.bpStart;
        const pixelWidth = options.pixelWidth;
        const bpEnd = bpStart + pixelWidth * bpPerPixel + 1; // paint for all coverage buckets
        // If alignment track color is != default, use it

        let color;

        if (this.parent.coverageColor) {
          color = this.parent.coverageColor;
        } else if (this.parent.color !== undefined) {
          color = IGVColor.darkenLighten(this.parent.color, -35);
        } else {
          color = DEFAULT_COVERAGE_COLOR;
        }

        IGVGraphics.setProperties(ctx, {
          fillStyle: color,
          strokeStyle: color
        });
        const w = Math.max(1, Math.ceil(1.0 / bpPerPixel));

        for (let i = 0, len = coverageMap.coverage.length; i < len; i++) {
          const bp = coverageMap.bpStart + i;
          if (bp < bpStart) continue;
          if (bp > bpEnd) break;
          const item = coverageMap.coverage[i];
          if (!item) continue;
          const h = Math.round(item.total / this.dataRange.max * this.height);
          const y = this.height - h;
          const x = Math.floor((bp - bpStart) / bpPerPixel); // IGVGraphics.setProperties(ctx, {fillStyle: "rgba(0, 200, 0, 0.25)", strokeStyle: "rgba(0, 200, 0, 0.25)" });

          IGVGraphics.fillRect(ctx, x, y, w, h);
        } // coverage mismatch coloring -- don't try to do this in above loop, color bar will be overwritten when w<1


        if (sequence) {
          for (let i = 0, len = coverageMap.coverage.length; i < len; i++) {
            const bp = coverageMap.bpStart + i;
            if (bp < bpStart) continue;
            if (bp > bpEnd) break;
            const item = coverageMap.coverage[i];
            if (!item) continue;
            const h = item.total / this.dataRange.max * this.height;
            let y = this.height - h;
            const x = Math.floor((bp - bpStart) / bpPerPixel);
            const refBase = sequence[i];

            if (item.isMismatch(refBase)) {
              IGVGraphics.setProperties(ctx, {
                fillStyle: nucleotideColors[refBase]
              });
              IGVGraphics.fillRect(ctx, x, y, w, h);
              let accumulatedHeight = 0.0;

              for (let nucleotide of ["A", "C", "T", "G"]) {
                const count = item["pos" + nucleotide] + item["neg" + nucleotide]; // non-logoritmic

                const hh = count / this.dataRange.max * this.height;
                y = this.height - hh - accumulatedHeight;
                accumulatedHeight += hh;
                IGVGraphics.setProperties(ctx, {
                  fillStyle: nucleotideColors[nucleotide]
                });
                IGVGraphics.fillRect(ctx, x, y, w, hh);
              }
            }
          }
        }
      }

      getClickedObject(clickState) {
        let features = clickState.viewport.cachedFeatures;
        if (!features || features.length === 0) return;
        const genomicLocation = Math.floor(clickState.genomicLocation);
        const coverageMap = features.coverageMap;
        const coverageMapIndex = Math.floor(genomicLocation - coverageMap.bpStart);
        return coverageMap.coverage[coverageMapIndex];
      }

      popupData(clickState) {
        const nameValues = [];
        const coverage = this.getClickedObject(clickState);

        if (coverage) {
          const genomicLocation = Math.floor(clickState.genomicLocation);
          const referenceFrame = clickState.viewport.referenceFrame;
          nameValues.push(referenceFrame.chr + ":" + numberFormatter$1(1 + genomicLocation));
          nameValues.push({
            name: 'Total Count',
            value: coverage.total
          }); // A

          let tmp = coverage.posA + coverage.negA;
          if (tmp > 0) tmp = tmp.toString() + " (" + Math.round(tmp / coverage.total * 100.0) + "%, " + coverage.posA + "+, " + coverage.negA + "- )";
          nameValues.push({
            name: 'A',
            value: tmp
          }); // C

          tmp = coverage.posC + coverage.negC;
          if (tmp > 0) tmp = tmp.toString() + " (" + Math.round(tmp / coverage.total * 100.0) + "%, " + coverage.posC + "+, " + coverage.negC + "- )";
          nameValues.push({
            name: 'C',
            value: tmp
          }); // G

          tmp = coverage.posG + coverage.negG;
          if (tmp > 0) tmp = tmp.toString() + " (" + Math.round(tmp / coverage.total * 100.0) + "%, " + coverage.posG + "+, " + coverage.negG + "- )";
          nameValues.push({
            name: 'G',
            value: tmp
          }); // T

          tmp = coverage.posT + coverage.negT;
          if (tmp > 0) tmp = tmp.toString() + " (" + Math.round(tmp / coverage.total * 100.0) + "%, " + coverage.posT + "+, " + coverage.negT + "- )";
          nameValues.push({
            name: 'T',
            value: tmp
          }); // N

          tmp = coverage.posN + coverage.negN;
          if (tmp > 0) tmp = tmp.toString() + " (" + Math.round(tmp / coverage.total * 100.0) + "%, " + coverage.posN + "+, " + coverage.negN + "- )";
          nameValues.push({
            name: 'N',
            value: tmp
          });
          nameValues.push('<HR/>');
          nameValues.push({
            name: 'DEL',
            value: coverage.del.toString()
          });
          nameValues.push({
            name: 'INS',
            value: coverage.ins.toString()
          });
        }

        return nameValues;
      }

    }

    class AlignmentTrack {
      constructor(config, parent) {
        this.parent = parent;
        this.browser = parent.browser;
        this.featureSource = parent.featureSource;
        this.top = 0 === config.coverageTrackHeight ? 0 : config.coverageTrackHeight + 5;
        this.displayMode = config.displayMode || "EXPANDED";
        this.alignmentRowHeight = config.alignmentRowHeight || 14;
        this.squishedRowHeight = config.squishedRowHeight || 3;
        this.negStrandColor = config.negStrandColor || "rgba(150, 150, 230, 0.75)";
        this.posStrandColor = config.posStrandColor || "rgba(230, 150, 150, 0.75)";
        this.insertionColor = config.insertionColor || "rgb(138, 94, 161)";
        this.deletionColor = config.deletionColor || "black";
        this.skippedColor = config.skippedColor || "rgb(150, 170, 170)";
        this.pairConnectorColor = config.pairConnectorColor;
        this.smallTLENColor = config.smallTLENColor || config.smallFragmentLengthColor || "rgb(0, 0, 150)";
        this.largeTLENColor = config.largeTLENColor || config.largeFragmentLengthColor || "rgb(200, 0, 0)";
        this.pairOrientation = config.pairOrienation || 'fr';
        this.pairColors = {};
        this.pairColors["RL"] = config.rlColor || "rgb(0, 150, 0)";
        this.pairColors["RR"] = config.rrColor || "rgb(20, 50, 200)";
        this.pairColors["LL"] = config.llColor || "rgb(0, 150, 150)";
        this.colorBy = config.colorBy || "unexpectedPair";
        this.colorByTag = config.colorByTag ? config.colorByTag.toUpperCase() : undefined;
        this.bamColorTag = config.bamColorTag === undefined ? "YC" : config.bamColorTag;
        this.hideSmallIndels = config.hideSmallIndels;
        this.indelSizeThreshold = config.indelSizeThreshold || 1;
        this.hasPairs = false; // Until proven otherwise

        this.hasSupplemental = false;
      }

      setTop(coverageTrack, showCoverage) {
        this.top = 0 === coverageTrack.height || false === showCoverage ? 0 : 5 + coverageTrack.height;
      }
      /**
       * Compute the pixel height required to display all content.
       *
       * @param alignmentContainer
       * @returns {number|*}
       */


      computePixelHeight(alignmentContainer) {
        if (alignmentContainer.packedAlignmentRows) {
          const h = alignmentContainer.hasDownsampledIntervals() ? downsampleRowHeight + alignmentStartGap : 0;
          const alignmentRowHeight = this.displayMode === "SQUISHED" ? this.squishedRowHeight : this.alignmentRowHeight;
          return h + alignmentRowHeight * alignmentContainer.packedAlignmentRows.length + 5;
        } else {
          return 0;
        }
      }

      draw(options) {
        const alignmentContainer = options.features;
        const ctx = options.context;
        const bpPerPixel = options.bpPerPixel;
        const bpStart = options.bpStart;
        const pixelWidth = options.pixelWidth;
        const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
        const showSoftClips = this.parent.showSoftClips;
        const showAllBases = this.parent.showAllBases;
        const nucleotideColors = this.browser.nucleotideColors; //alignmentContainer.repack(bpPerPixel, showSoftClips);

        const packedAlignmentRows = alignmentContainer.packedAlignmentRows;
        ctx.save();
        let referenceSequence = alignmentContainer.sequence;

        if (referenceSequence) {
          referenceSequence = referenceSequence.toUpperCase();
        }

        let alignmentRowYInset = 0;
        let pixelTop = options.pixelTop;

        if (this.top) {
          ctx.translate(0, this.top);
        }

        const pixelBottom = pixelTop + options.pixelHeight;

        if (alignmentContainer.hasDownsampledIntervals()) {
          alignmentRowYInset = downsampleRowHeight + alignmentStartGap;
          alignmentContainer.downsampledIntervals.forEach(function (interval) {
            var xBlockStart = (interval.start - bpStart) / bpPerPixel,
                xBlockEnd = (interval.end - bpStart) / bpPerPixel;

            if (xBlockEnd - xBlockStart > 5) {
              xBlockStart += 1;
              xBlockEnd -= 1;
            }

            IGVGraphics.fillRect(ctx, xBlockStart, 2, xBlockEnd - xBlockStart, downsampleRowHeight - 2, {
              fillStyle: "black"
            });
          });
        } else {
          alignmentRowYInset = 0;
        } // Transient variable -- rewritten on every draw, used for click object selection


        this.alignmentsYOffset = alignmentRowYInset;
        const alignmentRowHeight = this.displayMode === "SQUISHED" ? this.squishedRowHeight : this.alignmentRowHeight;

        if (packedAlignmentRows) {
          const nRows = packedAlignmentRows.length;

          for (let rowIndex = 0; rowIndex < nRows; rowIndex++) {
            const alignmentRow = packedAlignmentRows[rowIndex];
            const alignmentY = alignmentRowYInset + alignmentRowHeight * rowIndex;
            const alignmentHeight = alignmentRowHeight <= 4 ? alignmentRowHeight : alignmentRowHeight - 2;

            if (alignmentY > pixelBottom) {
              break;
            } else if (alignmentY + alignmentHeight < pixelTop) {
              continue;
            }

            for (let alignment of alignmentRow.alignments) {
              this.hasPairs = this.hasPairs || alignment.isPaired();

              if (this.browser.circularView) {
                // This is an expensive check, only do it if needed
                this.hasSupplemental = this.hasSupplemental || alignment.hasTag('SA');
              }

              if (alignment.start + alignment.lengthOnRef < bpStart) continue;
              if (alignment.start > bpEnd) break;

              if (true === alignment.hidden) {
                continue;
              }

              if (alignment instanceof PairedAlignment) {
                drawPairConnector.call(this, alignment, alignmentY, alignmentHeight);
                drawSingleAlignment.call(this, alignment.firstAlignment, alignmentY, alignmentHeight);

                if (alignment.secondAlignment) {
                  drawSingleAlignment.call(this, alignment.secondAlignment, alignmentY, alignmentHeight);
                }
              } else {
                drawSingleAlignment.call(this, alignment, alignmentY, alignmentHeight);
              }
            }
          }
        }

        ctx.restore(); // alignment is a PairedAlignment

        function drawPairConnector(alignment, yRect, alignmentHeight) {
          var connectorColor = this.getConnectorColor(alignment.firstAlignment),
              xBlockStart = (alignment.connectingStart - bpStart) / bpPerPixel,
              xBlockEnd = (alignment.connectingEnd - bpStart) / bpPerPixel,
              yStrokedLine = yRect + alignmentHeight / 2;

          if (alignment.connectingEnd < bpStart || alignment.connectingStart > bpEnd) {
            return;
          }

          if (alignment.mq <= 0) {
            connectorColor = IGVColor.addAlpha(connectorColor, 0.15);
          }

          IGVGraphics.setProperties(ctx, {
            fillStyle: connectorColor,
            strokeStyle: connectorColor
          });
          IGVGraphics.strokeLine(ctx, xBlockStart, yStrokedLine, xBlockEnd, yStrokedLine);
        }

        function drawSingleAlignment(alignment, yRect, alignmentHeight) {
          if (alignment.start + alignment.lengthOnRef < bpStart || alignment.start > bpEnd) {
            return;
          }

          const blocks = showSoftClips ? alignment.blocks : alignment.blocks.filter(b => 'S' !== b.type);
          let alignmentColor = this.getAlignmentColor(alignment);
          const outlineColor = alignmentColor;

          if (alignment.mq <= 0) {
            alignmentColor = IGVColor.addAlpha(alignmentColor, 0.15);
          }

          IGVGraphics.setProperties(ctx, {
            fillStyle: alignmentColor,
            strokeStyle: outlineColor
          });

          for (let b = 0; b < blocks.length; b++) {
            // Can't use forEach here -- we need ability to break
            const block = blocks[b]; // Somewhat complex test, neccessary to insure gaps are drawn.
            // If this is not the last block, and the next block starts before the orign (off screen to left) then skip.

            if (b !== blocks.length - 1 && blocks[b + 1].start < bpStart) continue;
            drawBlock.call(this, block, b);

            if (block.start + block.len > bpEnd) {
              // Do this after drawBlock to insure gaps are drawn
              break;
            }
          }

          if (alignment.insertions && this.parent.showInsertions) {
            let lastXBlockStart = -1;

            for (let insertionBlock of alignment.insertions) {
              if (this.hideSmallIndels && insertionBlock.len <= this.indelSizeThreshold) {
                continue;
              }

              if (insertionBlock.start < bpStart) {
                continue;
              }

              if (insertionBlock.start > bpEnd) {
                break;
              }

              const refOffset = insertionBlock.start - bpStart;
              const xBlockStart = refOffset / bpPerPixel - 1;

              if (xBlockStart - lastXBlockStart > 2) {
                const widthBlock = 3;
                IGVGraphics.fillRect(ctx, xBlockStart, yRect - 1, widthBlock, alignmentHeight + 2, {
                  fillStyle: this.insertionColor
                });
                lastXBlockStart = xBlockStart;
              }
            }
          }

          if (alignment.gaps) {
            const yStrokedLine = yRect + alignmentHeight / 2;

            for (let gap of alignment.gaps) {
              const sPixel = (gap.start - bpStart) / bpPerPixel;
              const ePixel = (gap.start + gap.len - bpStart) / bpPerPixel;
              const color = "D" === gap.type ? this.deletionColor : this.skippedColor;
              IGVGraphics.strokeLine(ctx, sPixel, yStrokedLine, ePixel, yStrokedLine, {
                strokeStyle: color
              });
            }
          }

          function drawBlock(block, b) {
            const offsetBP = block.start - alignmentContainer.start;
            const blockStartPixel = (block.start - bpStart) / bpPerPixel;
            const blockEndPixel = (block.start + block.len - bpStart) / bpPerPixel;
            const blockWidthPixel = Math.max(1, blockEndPixel - blockStartPixel); //const arrowHeadWidthPixel = alignmentRowHeight / 2.0;

            const nomPixelWidthOnRef = 100 / bpPerPixel;
            const arrowHeadWidthPixel = Math.min(alignmentRowHeight / 2.0, nomPixelWidthOnRef / 6);
            const isSoftClip = 'S' === block.type;
            const strokeOutline = alignment.mq <= 0 || this.highlightedAlignmentReadNamed === alignment.readName || isSoftClip;
            let blockOutlineColor = outlineColor;
            if (this.highlightedAlignmentReadNamed === alignment.readName) blockOutlineColor = 'red';else if (isSoftClip) blockOutlineColor = 'rgb(50,50,50)';
            const lastBlockPositiveStrand = true === alignment.strand && b === blocks.length - 1;
            const lastBlockReverseStrand = false === alignment.strand && b === 0;
            const lastBlock = lastBlockPositiveStrand | lastBlockReverseStrand;

            if (lastBlock) {
              let xListPixel;
              let yListPixel;

              if (lastBlockPositiveStrand) {
                xListPixel = [blockStartPixel, blockEndPixel, blockEndPixel + arrowHeadWidthPixel, blockEndPixel, blockStartPixel, blockStartPixel];
                yListPixel = [yRect, yRect, yRect + alignmentHeight / 2.0, yRect + alignmentHeight, yRect + alignmentHeight, yRect];
              } // Last block on - strand ?
              else if (lastBlockReverseStrand) {
                xListPixel = [blockEndPixel, blockStartPixel, blockStartPixel - arrowHeadWidthPixel, blockStartPixel, blockEndPixel, blockEndPixel];
                yListPixel = [yRect, yRect, yRect + alignmentHeight / 2.0, yRect + alignmentHeight, yRect + alignmentHeight, yRect];
              }

              IGVGraphics.fillPolygon(ctx, xListPixel, yListPixel, {
                fillStyle: alignmentColor
              });

              if (strokeOutline) {
                IGVGraphics.strokePolygon(ctx, xListPixel, yListPixel, {
                  strokeStyle: blockOutlineColor
                });
              }
            } // Internal block
            else {
              IGVGraphics.fillRect(ctx, blockStartPixel, yRect, blockWidthPixel, alignmentHeight, {
                fillStyle: alignmentColor
              });

              if (strokeOutline) {
                ctx.save();
                ctx.strokeStyle = blockOutlineColor;
                ctx.strokeRect(blockStartPixel, yRect, blockWidthPixel, alignmentHeight);
                ctx.restore();
              }
            } // Read base coloring coloring


            if (isSoftClip || showAllBases || this.parent.showMismatches && referenceSequence && alignment.seq && alignment.seq !== "*") {
              const seq = alignment.seq ? alignment.seq.toUpperCase() : undefined;
              const qual = alignment.qual;
              const seqOffset = block.seqOffset;

              for (let i = 0, len = block.len; i < len; i++) {
                if (offsetBP + i < 0) continue;
                let readChar = seq ? seq.charAt(seqOffset + i) : '';
                const refChar = referenceSequence.charAt(offsetBP + i);

                if (readChar === "=") {
                  readChar = refChar;
                }

                if (readChar === "X" || refChar !== readChar || isSoftClip || showAllBases) {
                  let baseColor;

                  if (!isSoftClip && qual !== undefined && qual.length > seqOffset + i) {
                    const readQual = qual[seqOffset + i];
                    baseColor = shadedBaseColor(readQual, nucleotideColors[readChar]);
                  } else {
                    baseColor = nucleotideColors[readChar];
                  }

                  if (baseColor) {
                    const xPixel = (block.start + i - bpStart) / bpPerPixel;
                    const widthPixel = Math.max(1, 1 / bpPerPixel);
                    renderBlockOrReadChar(ctx, bpPerPixel, {
                      x: xPixel,
                      y: yRect,
                      width: widthPixel,
                      height: alignmentHeight
                    }, baseColor, readChar);
                  }
                }
              }
            }
          }

          function renderBlockOrReadChar(context, bpp, bbox, color, char) {
            var threshold, center;
            threshold = 1.0 / 10.0;

            if (bpp <= threshold && bbox.height >= 8) {
              // render letter
              const fontHeight = Math.min(10, bbox.height);
              context.font = '' + fontHeight + 'px sans-serif';
              center = bbox.x + bbox.width / 2.0;
              IGVGraphics.strokeText(context, char, center - context.measureText(char).width / 2, fontHeight - 1 + bbox.y, {
                strokeStyle: color
              });
            } else {
              // render colored block
              IGVGraphics.fillRect(context, bbox.x, bbox.y, bbox.width, bbox.height, {
                fillStyle: color
              });
            }
          }
        }
      }

      popupData(clickState) {
        const clickedObject = this.getClickedObject(clickState);
        return clickedObject ? clickedObject.popupData(clickState.genomicLocation) : undefined;
      }

      contextMenuItemList(clickState) {
        const viewport = clickState.viewport;
        const list = [];

        const sortByOption = option => {
          const cs = this.parent.sortObject;
          const direction = cs && cs.position === Math.floor(clickState.genomicLocation) ? !cs.direction : true;
          const newSortObject = {
            chr: viewport.referenceFrame.chr,
            position: Math.floor(clickState.genomicLocation),
            option: option,
            direction: direction
          };
          this.parent.sortObject = newSortObject;
          sortAlignmentRows(newSortObject, viewport.cachedFeatures);
          viewport.repaint();
        };

        list.push('<b>Sort by...</b>');
        list.push({
          label: '&nbsp; base',
          click: () => sortByOption("BASE")
        });
        list.push({
          label: '&nbsp; read strand',
          click: () => sortByOption("STRAND")
        });
        list.push({
          label: '&nbsp; insert size',
          click: () => sortByOption("INSERT_SIZE")
        });
        list.push({
          label: '&nbsp; gap size',
          click: () => sortByOption("GAP_SIZE")
        });
        list.push({
          label: '&nbsp; chromosome of mate',
          click: () => sortByOption("MATE_CHR")
        });
        list.push({
          label: '&nbsp; mapping quality',
          click: () => sortByOption("MQ")
        });
        list.push({
          label: '&nbsp; read name',
          click: () => sortByOption("READ_NAME")
        });
        list.push({
          label: '&nbsp; tag',
          click: () => {
            const cs = this.parent.sortObject;
            const direction = cs && cs.position === Math.floor(clickState.genomicLocation) ? !cs.direction : true;
            const config = {
              label: 'Tag Name',
              value: this.sortByTag ? this.sortByTag : '',
              callback: tag => {
                if (tag) {
                  const newSortObject = {
                    chr: viewport.referenceFrame.chr,
                    position: Math.floor(clickState.genomicLocation),
                    option: "TAG",
                    tag: tag,
                    direction: direction
                  };
                  this.sortByTag = tag;
                  this.parent.sortObject = newSortObject;
                  sortAlignmentRows(newSortObject, viewport.cachedFeatures);
                  viewport.repaint();
                }
              }
            };
            this.browser.inputDialog.present(config, clickState.event);
          }
        });
        list.push('<hr/>');
        const clickedObject = this.getClickedObject(clickState);

        if (clickedObject) {
          const showSoftClips = this.parent.showSoftClips;
          const clickedAlignment = typeof clickedObject.alignmentContaining === 'function' ? clickedObject.alignmentContaining(clickState.genomicLocation, showSoftClips) : clickedObject;

          if (clickedAlignment) {
            if (clickedAlignment.isPaired() && clickedAlignment.isMateMapped()) {
              list.push({
                label: 'View mate in split screen',
                click: () => {
                  if (clickedAlignment.mate) {
                    const referenceFrame = clickState.viewport.referenceFrame;

                    if (this.browser.genome.getChromosome(clickedAlignment.mate.chr)) {
                      this.highlightedAlignmentReadNamed = clickedAlignment.readName; //this.browser.presentMultiLocusPanel(clickedAlignment, referenceFrame)

                      const bpWidth = referenceFrame.end - referenceFrame.start;
                      const frameStart = clickedAlignment.mate.position - bpWidth / 2;
                      const frameEnd = clickedAlignment.mate.position + bpWidth / 2;
                      this.browser.addMultiLocusPanel(clickedAlignment.mate.chr, frameStart, frameEnd, referenceFrame);
                    } else {
                      Alert.presentAlert(`Reference does not contain chromosome: ${clickedAlignment.mate.chr}`);
                    }
                  }
                },
                init: undefined
              });
            }

            list.push({
              label: 'View read sequence',
              click: () => {
                const seqstring = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");

                if (!seqstring || "*" === seqstring) {
                  Alert.presentAlert("Read sequence: *");
                } else {
                  Alert.presentAlert(seqstring);
                }
              }
            });

            if (isSecureContext()) {
              list.push({
                label: 'Copy read sequence',
                click: async () => {
                  const seq = clickedAlignment.seq; //.map(b => String.fromCharCode(b)).join("");

                  try {
                    //console.log(`seq: ${seq}`)
                    await navigator.clipboard.writeText(seq);
                  } catch (e) {
                    console.error(e);
                    Alert.presentAlert(`error copying sequence to clipboard ${e}`);
                  }
                }
              });
            }

            list.push('<hr/>');
          }
        } // Experimental JBrowse feature


        if (this.browser.circularView && (this.hasPairs || this.hasSupplemental)) {
          if (this.hasPairs) {
            list.push({
              label: 'Add discordant pairs to circular view',
              click: () => {
                this.parent.addPairedChordsForViewport(viewport);
              }
            });
          }

          if (this.hasSupplemental) {
            list.push({
              label: 'Add split reads to circular view',
              click: () => {
                this.parent.addSplitChordsForViewport(viewport);
              }
            });
          }

          list.push('<hr/>');
        }

        return list;
      }

      getClickedObject(clickState) {
        const viewport = clickState.viewport;
        const y = clickState.y;
        const genomicLocation = clickState.genomicLocation;
        const showSoftClips = this.parent.showSoftClips;
        let features = viewport.cachedFeatures;
        if (!features || features.length === 0) return;
        let packedAlignmentRows = features.packedAlignmentRows;
        let downsampledIntervals = features.downsampledIntervals;
        const alignmentRowHeight = this.displayMode === "SQUISHED" ? this.squishedRowHeight : this.alignmentRowHeight;
        let packedAlignmentsIndex = Math.floor((y - this.top - this.alignmentsYOffset) / alignmentRowHeight);

        if (packedAlignmentsIndex < 0) {
          for (let i = 0; i < downsampledIntervals.length; i++) {
            if (downsampledIntervals[i].start <= genomicLocation && downsampledIntervals[i].end >= genomicLocation) {
              return downsampledIntervals[i];
            }
          }
        } else if (packedAlignmentsIndex < packedAlignmentRows.length) {
          const alignmentRow = packedAlignmentRows[packedAlignmentsIndex];
          const clicked = alignmentRow.alignments.filter(alignment => alignment.containsLocation(genomicLocation, showSoftClips));
          if (clicked.length > 0) return clicked[0];
        }

        return undefined;
      }

      /**
       * Return the color for connectors in paired alignment view.   If explicitly set return that, otherwise return
       * the alignment color, unless the color option can result in split colors (separte color for each mate).
       *
       * @param alignment
       * @returns {string}
       */
      getConnectorColor(alignment) {
        if (this.pairConnectorColor) {
          return this.pairConnectorColor;
        }

        switch (this.colorBy) {
          case "strand":
          case "firstOfPairStrand":
          case "pairOrientation":
          case "tag":
            return this.parent.color || DEFAULT_CONNECTOR_COLOR;

          default:
            return this.getAlignmentColor(alignment);
        }
      }

      getAlignmentColor(alignment) {
        let color = this.parent.color || DEFAULT_ALIGNMENT_COLOR; // The default color if nothing else applies

        const option = this.colorBy;

        switch (option) {
          case "strand":
            color = alignment.strand ? this.posStrandColor : this.negStrandColor;
            break;

          case "firstOfPairStrand":
            if (alignment instanceof PairedAlignment) {
              color = alignment.firstOfPairStrand() ? this.posStrandColor : this.negStrandColor;
            } else if (alignment.isPaired()) {
              if (alignment.isFirstOfPair()) {
                color = alignment.strand ? this.posStrandColor : this.negStrandColor;
              } else if (alignment.isSecondOfPair()) {
                color = alignment.strand ? this.negStrandColor : this.posStrandColor;
              } else {
                console.error("ERROR. Paired alignments are either first or second.");
              }
            }

            break;

          case "unexpectedPair":
          case "pairOrientation":
            if (this.pairOrientation && alignment.pairOrientation) {
              const oTypes = orientationTypes[this.pairOrientation];

              if (oTypes) {
                const pairColor = this.pairColors[oTypes[alignment.pairOrientation]];

                if (pairColor) {
                  color = pairColor;
                  break;
                }
              }
            }

            if ("pairOrientation" === option) {
              break;
            }

          case "tlen":
          case "fragmentLength":
            if (alignment.mate && alignment.isMateMapped()) {
              if (alignment.mate.chr !== alignment.chr) {
                color = getChrColor(alignment.mate.chr);
              } else if (this.parent.minTemplateLength && Math.abs(alignment.fragmentLength) < this.parent.minTemplateLength) {
                color = this.smallTLENColor;
              } else if (this.parent.maxTemplateLength && Math.abs(alignment.fragmentLength) > this.parent.maxTemplateLength) {
                color = this.largeTLENColor;
              }
            }

            break;

          case "tag":
            const tagValue = alignment.tags()[this.colorByTag];

            if (tagValue !== undefined) {
              if (this.bamColorTag === this.colorByTag) {
                // UCSC style color option
                color = "rgb(" + tagValue + ")";
              } else {
                if (!this.tagColors) {
                  this.tagColors = new PaletteColorTable("Set1");
                }

                color = this.tagColors.getColor(tagValue);
              }
            }

            break;

          default:
            color = this.parent.color || DEFAULT_ALIGNMENT_COLOR;
        }

        return color;
      }

    }

    function sortAlignmentRows(options, alignmentContainer) {
      const direction = options.direction;

      for (let row of alignmentContainer.packedAlignmentRows) {
        row.updateScore(options, alignmentContainer);
      }

      alignmentContainer.packedAlignmentRows.sort(function (rowA, rowB) {
        const i = rowA.score > rowB.score ? 1 : rowA.score < rowB.score ? -1 : 0;
        return true === direction ? i : -i;
      });
    }

    function shadedBaseColor(qual, baseColor) {
      const minQ = 5; //prefs.getAsInt(PreferenceManager.SAM_BASE_QUALITY_MIN),

      const maxQ = 20; //prefs.getAsInt(PreferenceManager.SAM_BASE_QUALITY_MAX);

      let alpha;

      if (qual < minQ) {
        alpha = 0.1;
      } else {
        alpha = Math.max(0.1, Math.min(1.0, 0.1 + 0.9 * (qual - minQ) / (maxQ - minQ)));
      } // Round alpha to nearest 0.1


      alpha = Math.round(alpha * 10) / 10.0;

      if (alpha < 1) {
        baseColor = IGVColor.addAlpha(baseColor, alpha);
      }

      return baseColor;
    }

    const orientationTypes = {
      "fr": {
        "F1R2": "LR",
        "F2R1": "LR",
        "F1F2": "LL",
        "F2F1": "LL",
        "R1R2": "RR",
        "R2R1": "RR",
        "R1F2": "RL",
        "R2F1": "RL"
      },
      "rf": {
        "R1F2": "LR",
        "R2F1": "LR",
        "R1R2": "LL",
        "R2R1": "LL",
        "F1F2": "RR",
        "F2F1": "RR",
        "F1R2": "RL",
        "F2R1": "RL"
      },
      "ff": {
        "F2F1": "LR",
        "R1R2": "LR",
        "F2R1": "LL",
        "R1F2": "LL",
        "R2F1": "RR",
        "F1R2": "RR",
        "R2R1": "RL",
        "F1F2": "RL"
      }
    };
    function getChrColor(chr) {
      if (chrColorMap[chr]) {
        return chrColorMap[chr];
      } else if (chrColorMap["chr" + chr]) {
        const color = chrColorMap["chr" + chr];
        chrColorMap[chr] = color;
        return color;
      } else {
        const color = IGVColor.randomRGB(0, 255);
        chrColorMap[chr] = color;
        return color;
      }
    }
    const chrColorMap = {
      "chrX": "rgb(204, 153, 0)",
      "chrY": "rgb(153, 204, 0)",
      "chrUn": "rgb(50, 50, 50)",
      "chr1": "rgb(80, 80, 255)",
      "chrI": "rgb(139, 155, 187)",
      "chr2": "rgb(206, 61, 50)",
      "chrII": "rgb(206, 61, 50)",
      "chr2a": "rgb(216, 71, 60)",
      "chr2b": "rgb(226, 81, 70)",
      "chr3": "rgb(116, 155, 88)",
      "chrIII": "rgb(116, 155, 88)",
      "chr4": "rgb(240, 230, 133)",
      "chrIV": "rgb(240, 230, 133)",
      "chr5": "rgb(70, 105, 131)",
      "chr6": "rgb(186, 99, 56)",
      "chr7": "rgb(93, 177, 221)",
      "chr8": "rgb(128, 34, 104)",
      "chr9": "rgb(107, 215, 107)",
      "chr10": "rgb(213, 149, 167)",
      "chr11": "rgb(146, 72, 34)",
      "chr12": "rgb(131, 123, 141)",
      "chr13": "rgb(199, 81, 39)",
      "chr14": "rgb(213, 143, 92)",
      "chr15": "rgb(122, 101, 165)",
      "chr16": "rgb(228, 175, 105)",
      "chr17": "rgb(59, 27, 83)",
      "chr18": "rgb(205, 222, 183)",
      "chr19": "rgb(97, 42, 121)",
      "chr20": "rgb(174, 31, 99)",
      "chr21": "rgb(231, 199, 111)",
      "chr22": "rgb(90, 101, 94)",
      "chr23": "rgb(204, 153, 0)",
      "chr24": "rgb(153, 204, 0)",
      "chr25": "rgb(51, 204, 0)",
      "chr26": "rgb(0, 204, 51)",
      "chr27": "rgb(0, 204, 153)",
      "chr28": "rgb(0, 153, 204)",
      "chr29": "rgb(10, 71, 255)",
      "chr30": "rgb(71, 117, 255)",
      "chr31": "rgb(255, 194, 10)",
      "chr32": "rgb(255, 209, 71)",
      "chr33": "rgb(153, 0, 51)",
      "chr34": "rgb(153, 26, 0)",
      "chr35": "rgb(153, 102, 0)",
      "chr36": "rgb(128, 153, 0)",
      "chr37": "rgb(51, 153, 0)",
      "chr38": "rgb(0, 153, 26)",
      "chr39": "rgb(0, 153, 102)",
      "chr40": "rgb(0, 128, 153)",
      "chr41": "rgb(0, 51, 153)",
      "chr42": "rgb(26, 0, 153)",
      "chr43": "rgb(102, 0, 153)",
      "chr44": "rgb(153, 0, 128)",
      "chr45": "rgb(214, 0, 71)",
      "chr46": "rgb(255, 20, 99)",
      "chr47": "rgb(0, 214, 143)",
      "chr48": "rgb(20, 255, 177)"
    };

    let timer;
    let currentViewport = undefined;
    const toolTipTimeout = 1e4;

    class RulerViewport extends TrackViewport {
      constructor(trackView, $viewportColumn, referenceFrame, width) {
        super(trackView, $viewportColumn, referenceFrame, width);
      }

      initializationHelper() {
        this.rulerSweeper = new RulerSweeper(this);
        this.$multiLocusCloseButton = $$1('<div>', {
          class: 'igv-multi-locus-close-button'
        });
        this.$viewport.append(this.$multiLocusCloseButton);
        this.$multiLocusCloseButton.get(0).appendChild(createIcon$1("times-circle"));
        this.$multiLocusCloseButton.click(() => {
          this.browser.removeMultiLocusPanel(this.referenceFrame);
        });
        this.$rulerLabel = $$1('<div>', {
          class: 'igv-multi-locus-ruler-label'
        });
        this.$viewport.append(this.$rulerLabel);
        this.$rulerLabel.click(async () => {
          await this.browser.gotoMultilocusPanel(this.referenceFrame); // const removals = this.browser.referenceFrameList.filter(r => this.referenceFrame !== r)
          // for (let referenceFrame of removals) {
          //     await this.browser.removeMultiLocusPanel(referenceFrame)
          // }
        });
        this.$tooltip = $$1('<div>', {
          class: 'igv-ruler-tooltip'
        });
        this.$tooltip.height(this.$viewport.height());
        this.$viewport.append(this.$tooltip);
        this.$tooltipContent = $$1('<div>');
        this.$tooltip.append(this.$tooltipContent);
        this.attachMouseHandlers(GenomeUtils.isWholeGenomeView(this.referenceFrame.chr));
        this.$tooltip.hide();
        this.dismissLocusLabel();
      }

      presentLocusLabel(viewportWidth) {
        const createRulerLabelString = () => {
          const html = `<div>${this.referenceFrame.getMultiLocusLabel(viewportWidth)}</div>`;
          return document.createRange().createContextualFragment(html).firstChild;
        };

        this.$rulerLabel.get(0).innerHTML = '';
        this.$rulerLabel.get(0).style.backgroundColor = getChrColor(this.referenceFrame.chr);
        this.$rulerLabel.get(0).appendChild(createRulerLabelString());
        this.$rulerLabel.show();
        this.$multiLocusCloseButton.show();
      } // Use in conjuction with .igv-multi-locus-ruler-label-square-dot css class (_dom-misc.scss)


      presentLocusLabel_Square_Dot(viewportWidth) {

        const createRulerLabelDot = () => {
          const html = `<div>
                                <?xml version="1.0" encoding="UTF-8"?>
                                <svg width="14px" height="14px" viewBox="0 0 89 89" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
                                    <g>
                                        <circle id="Oval" fill="${getChrColor(this.referenceFrame.chr)}" cx="44.5" cy="44.5" r="44.5"></circle>
                                    </g>
                                </svg>
                            </div>`;
          return document.createRange().createContextualFragment(html).firstChild;
        };

        const createRulerLabelString = () => {
          const html = `<div>${this.referenceFrame.getMultiLocusLabel(viewportWidth)}</div>`;
          return document.createRange().createContextualFragment(html).firstChild;
        };

        this.$rulerLabel.get(0).innerHTML = '';
        this.$rulerLabel.get(0).appendChild(createRulerLabelDot());
        this.$rulerLabel.get(0).appendChild(createRulerLabelString());
        this.$rulerLabel.show();
        this.$multiLocusCloseButton.show();
      }

      dismissLocusLabel() {
        this.$rulerLabel.hide();
        this.$multiLocusCloseButton.hide();
      }

      attachMouseHandlers(isWholeGenomeView) {
        this.namespace = `.ruler_track_viewport_${this.browser.referenceFrameList.indexOf(this.referenceFrame)}`;
        this.$viewport.off(this.namespace); // console.log(`Ruler track ${ true === isWholeGenomeView ? 'is' : 'is not' } whole genome.`)

        if (true === isWholeGenomeView) {
          const index = this.browser.referenceFrameList.indexOf(this.referenceFrame);
          const click = `click${this.namespace}`;
          this.$viewport.on(click, e => {
            const {
              x: pixel
            } = translateMouseCoordinates$1(e, this.$viewport.get(0));
            const bp = Math.round(this.referenceFrame.start + this.referenceFrame.toBP(pixel));
            let searchString;
            const {
              chr
            } = this.browser.genome.getChromosomeCoordinate(bp);

            if (1 === this.browser.referenceFrameList.length) {
              searchString = chr;
            } else {
              let loci = this.browser.referenceFrameList.map(_ref => {
                let {
                  locusSearchString
                } = _ref;
                return locusSearchString;
              });
              loci[index] = chr;
              searchString = loci.join(' ');
            }

            this.browser.search(searchString);
          });
          this.$viewport.get(0).style.cursor = 'pointer';
        } else {
          this.$viewport.get(0).style.cursor = 'default';
        }
      }

      mouseMove(event) {
        if (true === this.browser.cursorGuideVisible) {
          if (undefined === currentViewport) {
            currentViewport = this;
            this.$tooltip.show();
          } else if (currentViewport.guid !== this.guid) {
            if (currentViewport.$tooltip) {
              currentViewport.$tooltip.hide();
            }

            this.$tooltip.show();
            currentViewport = this;
          } else {
            this.$tooltip.show();
          }

          const isWholeGenome = this.browser.isMultiLocusWholeGenomeView() || GenomeUtils.isWholeGenomeView(this.referenceFrame.chr);

          if (isWholeGenome) {
            this.$tooltip.hide();
            return;
          }

          const {
            x
          } = translateMouseCoordinates$1(event, this.$viewport.get(0));
          const {
            start,
            bpPerPixel
          } = this.referenceFrame;
          const bp = Math.round(0.5 + start + Math.max(0, x) * bpPerPixel);
          this.$tooltipContent.text(numberFormatter$1(bp));
          const {
            width: ww
          } = this.$tooltipContent.get(0).getBoundingClientRect();
          const {
            width: w
          } = this.$viewport.get(0).getBoundingClientRect();
          this.$tooltip.css({
            left: `${IGVMath.clamp(x, 0, w - ww)}px`
          }); // hide tooltip when movement stops

          clearTimeout(timer);
          timer = setTimeout(() => {
            if (this.$tooltip) this.$tooltip.hide();
          }, toolTipTimeout);
        }
      }

      startSpinner() {}

      stopSpinner() {}

      dispose() {
        this.rulerSweeper.dispose();
        super.dispose();
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class IdeogramViewport extends TrackViewport {
      constructor(trackView, viewportColumn, referenceFrame, width) {
        super(trackView, viewportColumn, referenceFrame, width);
      }

      initializationHelper() {
        this.canvas = document.createElement('canvas');
        this.canvas.className = 'igv-ideogram-canvas';
        this.$content.append($$1(this.canvas));
        this.ideogram_ctx = this.canvas.getContext('2d');
        this.addMouseHandlers();
      }

      addMouseHandlers() {
        this.addViewportClickHandler(this.$viewport.get(0));
      }

      addViewportClickHandler(viewport) {
        this.boundClickHandler = clickHandler.bind(this);
        viewport.addEventListener('click', this.boundClickHandler);

        function clickHandler(event) {
          const {
            xNormalized,
            width
          } = translateMouseCoordinates$1(event, this.ideogram_ctx.canvas);
          const {
            bpLength
          } = this.browser.genome.getChromosome(this.referenceFrame.chr);
          const locusLength = this.referenceFrame.bpPerPixel * width;
          const chrCoveragePercentage = locusLength / bpLength;
          let xPercentage = xNormalized;

          if (xPercentage - chrCoveragePercentage / 2.0 < 0) {
            xPercentage = chrCoveragePercentage / 2.0;
          }

          if (xPercentage + chrCoveragePercentage / 2.0 > 1.0) {
            xPercentage = 1.0 - chrCoveragePercentage / 2.0;
          }

          const ss = Math.round((xPercentage - chrCoveragePercentage / 2.0) * bpLength);
          const ee = Math.round((xPercentage + chrCoveragePercentage / 2.0) * bpLength);
          this.referenceFrame.start = ss;
          this.referenceFrame.end = ee;
          this.referenceFrame.bpPerPixel = (ee - ss) / width;
          this.browser.updateViews(this.referenceFrame, this.browser.trackViews, true);
        }
      }

      setWidth(width) {
        this.$viewport.width(width);
      }

      drawSVGWithContext(context, width, height, id, x, y, yClipOffset) {
        context.saveWithTranslationAndClipRect(id, x, y, width, height, yClipOffset);
        this.trackView.track.draw({
          context,
          referenceFrame: this.referenceFrame,
          pixelWidth: width,
          pixelHeight: height
        });
        context.restore();
      }

      repaint() {
        this.draw({
          referenceFrame: this.referenceFrame
        });
      }

      draw(_ref) {
        let {
          referenceFrame
        } = _ref;
        IGVGraphics.configureHighDPICanvas(this.ideogram_ctx, this.$viewport.width(), this.$viewport.height());
        this.trackView.track.draw({
          context: this.ideogram_ctx,
          referenceFrame,
          pixelWidth: this.$viewport.width(),
          pixelHeight: this.$viewport.height()
        });
      }

      startSpinner() {}

      stopSpinner() {}

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    function createViewport(trackView, column, referenceFrame, width) {
      if ('ruler' === trackView.track.type) {
        return new RulerViewport(trackView, column, referenceFrame, width);
      } else if ('ideogram' === trackView.track.id) {
        return new IdeogramViewport(trackView, column, referenceFrame, width);
      } else {
        return new TrackViewport(trackView, column, referenceFrame, width);
      }
    }

    const maxFontSize = 10;
    const fontConfigureTemplate = {
      // font: '2pt sans-serif',
      textAlign: 'start',
      textBaseline: 'bottom',
      strokeStyle: 'black',
      fillStyle: 'black'
    };

    class SampleNameViewport {
      constructor(trackView, column, unused, width) {
        this.guid = guid$2();
        this.trackView = trackView;
        this.browser = trackView.browser;
        this.viewport = div$1({
          class: 'igv-viewport'
        });
        column.appendChild(this.viewport);

        if (trackView.track.height) {
          this.viewport.style.height = `${trackView.track.height}px`;
        }

        this.canvas = document.createElement('canvas');
        this.viewport.appendChild(this.canvas);
        this.ctx = this.canvas.getContext("2d");
        this.trackScrollDelta = 0;
        this.contentTop = 0;
        this.setWidth(width);

        if (false === this.browser.showSampleNames) {
          this.hide();
        }

        this.addMouseHandlers();
      }

      checkCanvas() {
        const dpi = window.devicePixelRatio;
        const requiredHeight = this.viewport.clientHeight;
        const requiredWidth = this.browser.sampleNameViewportWidth;

        if (this.canvas.width !== requiredWidth * dpi || this.canvas.height !== requiredHeight * dpi) {
          const canvas = this.canvas;
          canvas.width = requiredWidth * dpi;
          canvas.height = requiredHeight * dpi;
          canvas.style.width = `${requiredWidth}px`;
          canvas.style.height = `${requiredHeight}px`;
          this.ctx = this.canvas.getContext("2d");
          this.ctx.scale(dpi, dpi);
        }
      }

      setTop(contentTop) {
        if (typeof this.trackView.track.getSamples === 'function') {
          this.contentTop = contentTop;
          const samples = this.trackView.track.getSamples();
          this.repaint(samples);
        }
      }

      setWidth(width) {
        this.viewport.innerWidth = width;
        this.checkCanvas();
      }

      show() {
        this.viewport.style.display = 'block';
      }

      hide() {
        this.viewport.style.display = 'none';
      }

      async repaint(samples) {
        this.checkCanvas();
        this.draw({
          context: this.ctx,
          samples
        });
      }

      draw(_ref) {
        let {
          context,
          samples
        } = _ref;

        if (!samples || samples.names.length === 0
        /* || samples.height < 1*/
        ) {
          return;
        }

        configureFont(context, fontConfigureTemplate, samples.height);
        const sampleNameXShim = 4;
        context.clearRect(0, 0, context.canvas.width, context.canvas.height);
        context.fillStyle = appleCrayonRGB('lead');
        const viewportHeight = this.viewport.getBoundingClientRect().height;
        let y = (samples.yOffset || 0) + this.contentTop; // contentTop will always be a negative number (top relative to viewport)

        for (let name of samples.names) {
          if (y > viewportHeight) {
            break;
          }

          if (y + samples.height > 0) {
            const text = name;
            const yFont = getYFont(context, text, y, samples.height);
            context.fillText(text, sampleNameXShim, yFont);
          }

          y += samples.height;
        }
      }

      renderSVGContext(context, _ref2) {
        let {
          deltaX,
          deltaY
        } = _ref2;

        if (typeof this.trackView.track.getSamples === 'function') {
          const samples = this.trackView.track.getSamples();
          const yScrollDelta = 0; // This is not relevant, scrolling is handled in "draw"

          const {
            width,
            height
          } = this.viewport.getBoundingClientRect();
          const str = (this.trackView.track.name || this.trackView.track.id).replace(/\W/g, '');
          const id = `${str}_sample_names_guid_${guid$2()}`;
          context.saveWithTranslationAndClipRect(id, deltaX, deltaY + yScrollDelta, width, height, -yScrollDelta);
          this.draw({
            context,
            samples
          });
          context.restore();
        }
      }

      addMouseHandlers() {
        this.addViewportContextMenuHandler(this.viewport);
      }

      removeMouseHandlers() {
        this.removeViewportContextMenuHandler(this.viewport);
      }

      addViewportContextMenuHandler(viewport) {
        this.boundContextMenuHandler = contextMenuHandler.bind(this);
        viewport.addEventListener('contextmenu', this.boundContextMenuHandler);

        function contextMenuHandler(event) {
          event.preventDefault();
          event.stopPropagation();
          const config = {
            label: 'Name Panel Width',
            value: this.browser.sampleNameViewportWidth,
            callback: newWidth => {
              this.browser.sampleNameViewportWidth = parseInt(newWidth);

              for (let {
                sampleNameViewport
              } of this.browser.trackViews) {
                sampleNameViewport.setWidth(this.browser.sampleNameViewportWidth);
              }

              this.browser.layoutChange();
            }
          };
          this.browser.inputDialog.present(config, event);
        }
      }

      removeViewportContextMenuHandler(viewport) {
        viewport.removeEventListener('contextmenu', this.boundContextMenuHandler);
      }

      dispose() {
        this.removeMouseHandlers();
        this.viewport.remove();
      }

    }

    function getYFont(context, text, y, height) {
      return y + height - getSampleNameYShim(context, text, height);
    }

    function getSampleNameYShim(context, text, h) {
      const {
        actualBoundingBoxAscent,
        actualBoundingBoxDescent
      } = context.measureText(text);
      return (h - (actualBoundingBoxAscent + actualBoundingBoxDescent)) / 2;
    }

    function configureFont(ctx, _ref3, sampleHeight) {
      let {
        textAlign,
        textBaseline,
        strokeStyle,
        fillStyle
      } = _ref3;
      const pixels = Math.min(sampleHeight, maxFontSize);
      ctx.font = `${pixels}px sans-serif`;
      ctx.textAlign = textAlign;
      ctx.textBaseline = textBaseline;
      ctx.fillStyle = fillStyle;
    }

    class GenericColorPicker extends GenericContainer {
      constructor(_ref) {
        let {
          parent,
          width
        } = _ref;
        super({
          parent,
          width,
          border: '1px solid gray'
        });
      }

      configure(defaultColors, colorHandlers) {
        this.colorHandlers = colorHandlers; // active color handler defaults to handler with 'color' as key

        this.setActiveColorHandler('color');
        this.createSwatches(defaultColors);
      }

      setActiveColorHandler(option) {
        this.activeColorHandler = this.colorHandlers[option];
      }

      createSwatches(defaultColors) {
        this.container.querySelectorAll('.igv-ui-color-swatch').forEach(swatch => swatch.remove());
        const hexColorStrings = Object.values(appleCrayonPalette$1);

        for (let hexColorString of hexColorStrings) {
          const swatch = div$1({
            class: 'igv-ui-color-swatch'
          });
          this.container.appendChild(swatch);
          this.decorateSwatch(swatch, hexColorString);
        }

        if (defaultColors) {
          for (let hexColorString of defaultColors) {
            const swatch = div$1({
              class: 'igv-ui-color-swatch'
            });
            this.container.appendChild(swatch);
            this.decorateSwatch(swatch, hexColorString);
          }
        }
      }

      decorateSwatch(swatch, hexColorString) {
        swatch.style.backgroundColor = hexColorString;
        swatch.addEventListener('mouseenter', () => swatch.style.borderColor = hexColorString);
        swatch.addEventListener('mouseleave', () => swatch.style.borderColor = 'white');
        swatch.addEventListener('click', event => {
          event.stopPropagation();
          this.activeColorHandler(hexColorString);
        });
        swatch.addEventListener('touchend', event => {
          event.stopPropagation();
          this.activeColorHandler(hexColorString);
        });
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    const MenuPopup = function (parent) {
      this.popover = div$1({
        class: 'igv-menu-popup'
      });
      parent.appendChild(this.popover);
      const header = div$1({
        class: 'igv-menu-popup-header'
      });
      this.popover.appendChild(header);
      attachDialogCloseHandlerWithParent$1(header, () => this.hide());
      this.popoverContent = div$1();
      this.popover.appendChild(this.popoverContent);
      makeDraggable$1(this.popover, header);
      header.addEventListener('click', e => {
        e.stopPropagation();
        e.preventDefault(); // absorb click to prevent it leaking through to parent DOM element
      });
      this.hide();
    };

    MenuPopup.prototype.hide = function () {
      this.popover.style.display = 'none';
    };

    MenuPopup.prototype.presentMenuList = function (menuList) {
      hideAllMenuPopups();

      if (menuList.length > 0) {
        this.popoverContent.innerHTML = '';
        menuList = MenuUtils.trackMenuItemListHelper(menuList, this);

        for (let item of menuList) {
          if (item.init) {
            item.init();
          }

          let $e = item.object;

          if (0 === menuList.indexOf(item)) {
            $e.removeClass('igv-track-menu-border-top');
          }

          if ($e.hasClass('igv-track-menu-border-top') || $e.hasClass('igv-menu-popup-check-container')) ; else if ($e.is('div')) {
            $e.addClass('igv-menu-popup-shim');
          }

          this.popoverContent.appendChild($e.get(0));
        } // NOTE: style.display most NOT be 'none' when calculating width. a display = 'none' will always
        //       yield a width of zero (0).


        this.popover.style.display = 'flex';
        const {
          width
        } = this.popover.getBoundingClientRect();
        this.popover.style.left = `${-width}px`;
        this.popover.style.top = `${0}px`;
      }
    };

    MenuPopup.prototype.presentTrackContextMenu = function (e, menuItems) {
      this.popoverContent.innerHTML = '';
      const menuElements = createMenuElements(menuItems, this.popover);

      for (let {
        el
      } of menuElements) {
        this.popoverContent.appendChild(el);
      }

      present(e, this.popover);
    };

    MenuPopup.prototype.dispose = function () {
      this.popoverContent.innerHTML = '';
      this.popover.innerHTML = '';
      Object.keys(this).forEach(function (key) {
        this[key] = undefined;
      });
    };

    function createMenuElements(itemList, popover) {
      return itemList.map(item => {
        let el;

        if (typeof item === 'string' && '<hr/>' === item) {
          el = document.createElement('hr');
        } else if (typeof item === 'string') {
          el = div$1({
            class: 'context-menu'
          });
          el.innerHTML = item;
        } else if (typeof item === 'Node') {
          el = item;
        } else {
          if (typeof item.init === 'function') {
            item.init();
          }

          if ("checkbox" === item.type) {
            el = createCheckbox$1("Show all bases", item.value);
          } else if ("color" === item.type) {
            const colorPicker = new GenericColorPicker({
              parent: popover.parentElement,
              width: 364
            });
            colorPicker.configure(undefined, {
              color: color => item.click(color)
            });
            el = div$1({
              class: 'context-menu'
            });

            if (typeof item.label === 'string') {
              el.innerHTML = item.label;
            }

            const clickHandler = e => {
              colorPicker.show();
              hide$1(popover);
              e.preventDefault();
              e.stopPropagation();
            };

            el.addEventListener('click', clickHandler);
            el.addEventListener('touchend', clickHandler);
            el.addEventListener('mouseup', function (e) {
              e.preventDefault();
              e.stopPropagation();
            });
          } else {
            el = div$1({
              class: 'context-menu'
            });

            if (typeof item.label === 'string') {
              el.innerHTML = item.label;
            }
          }

          if (item.click && "color" !== item.type) {
            el.addEventListener('click', handleClick);
            el.addEventListener('touchend', handleClick);
            el.addEventListener('mouseup', function (e) {
              e.preventDefault();
              e.stopPropagation();
            }); // eslint-disable-next-line no-inner-declarations

            function handleClick(e) {
              item.click();
              hide$1(popover);
              e.preventDefault();
              e.stopPropagation();
            }
          }
        }

        return {
          el,
          init: item.init
        };
      });
    }

    function present(e, popover) {
      // NOTE: style.display most NOT be 'none' when calculating width. a display = 'none' will always
      //       yield a width of zero (0).
      popover.style.display = 'flex';
      const {
        x,
        y
      } = translateMouseCoordinates$1(e, popover.parentNode);
      const {
        width
      } = popover.getBoundingClientRect();
      const xmax = x + width;
      const {
        width: parentWidth
      } = popover.parentNode.getBoundingClientRect();
      popover.style.left = `${xmax > parentWidth ? x - (xmax - parentWidth) : x}px`;
      popover.style.top = `${y}px`;
    }

    const hideAllMenuPopups = () => {
      const menus = document.querySelectorAll('.igv-menu-popup');

      for (let i = 0; i < menus.length; i++) {
        menus[i].style.display = 'none';
      }
    };

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const scrollbarExclusionTypes = new Set(['ruler', 'sequence', 'ideogram']);
    const colorPickerExclusionTypes = new Set(['ruler', 'sequence', 'ideogram']);

    class TrackView {
      constructor(browser, columnContainer, track) {
        this.browser = browser;
        this.track = track;
        track.trackView = this;
        this.addDOMToColumnContainer(browser, columnContainer, browser.referenceFrameList);
      }
      /**
       * Start a spinner for the track on any of its viewports.  In practice this is called during initialization
       * when there is only one.
       */


      startSpinner() {
        if (this.viewports && this.viewports.length > 0) {
          this.viewports[0].startSpinner();
        }
      }

      stopSpinner() {
        if (this.viewports && this.viewports.length > 0) {
          this.viewports[0].stopSpinner();
        }
      }

      addDOMToColumnContainer(browser, columnContainer, referenceFrameList) {
        // Axis
        this.axis = this.createAxis(browser, this.track); // Create a viewport for each reference frame

        this.viewports = [];
        const viewportWidth = browser.calculateViewportWidth(referenceFrameList.length);
        const viewportColumns = columnContainer.querySelectorAll('.igv-column');

        for (let i = 0; i < viewportColumns.length; i++) {
          const viewport = createViewport(this, viewportColumns[i], referenceFrameList[i], viewportWidth);
          this.viewports.push(viewport);
        } // SampleName Viewport


        this.sampleNameViewport = new SampleNameViewport(this, browser.columnContainer.querySelector('.igv-sample-name-column'), undefined, browser.sampleNameViewportWidth); // Track Scrollbar

        this.createTrackScrollbar(browser); // Track Drag

        this.createTrackDragHandle(browser); // Track Gear

        this.createTrackGearPopup(browser);
      }

      createAxis(browser, track) {
        const axis = div$1();
        browser.columnContainer.querySelector('.igv-axis-column').appendChild(axis);
        axis.style.height = `${track.height}px`;

        if (typeof track.paintAxis === 'function') {
          if (track.dataRange) {
            axis.addEventListener('click', () => {
              browser.dataRangeDialog.configure(this);
              browser.dataRangeDialog.present($$1(browser.columnContainer));
            });
          }

          const {
            width,
            height
          } = axis.getBoundingClientRect();
          this.axisCanvas = document.createElement('canvas');
          this.axisCanvas.style.width = `${width}px`;
          this.axisCanvas.style.height = `${height}px`;
          axis.appendChild(this.axisCanvas);
        }

        return axis;
      }

      resizeAxisCanvas(width, height) {
        this.axis.style.width = `${width}px`;
        this.axis.style.height = `${height}px`;

        if (typeof this.track.paintAxis === 'function') {
          // Size the canvas in CSS (logical) pixels.  The buffer size will be set when painted.
          this.axisCanvas.style.width = `${width}px`;
          this.axisCanvas.style.height = `${height}px`;
        }
      }

      removeDOMFromColumnContainer() {
        // Axis
        if (this.boundAxisClickHander) {
          this.removeAxisEventListener(this.axis);
        }

        this.axis.remove(); // Track Viewports

        for (let viewport of this.viewports) {
          viewport.$viewport.remove();
        } // SampleName Viewport


        this.sampleNameViewport.dispose(); // empty trackScrollbar Column

        this.removeTrackScrollMouseHandlers();
        this.outerScroll.remove(); // empty trackDrag Column

        this.removeTrackDragMouseHandlers();
        this.dragHandle.remove(); // empty trackGear Column

        this.removeTrackGearMouseHandlers();
        this.gearContainer.remove();
      }

      renderSVGContext(context, _ref) {
        let {
          deltaX,
          deltaY
        } = _ref;
        renderSVGAxis(context, this.track, this.axisCanvas, deltaX, deltaY);
        const {
          width: axisWidth
        } = this.axis.getBoundingClientRect();
        const {
          y
        } = this.viewports[0].$viewport.get(0).getBoundingClientRect();
        let delta = {
          deltaX: axisWidth + deltaX,
          deltaY: y + deltaY
        };

        for (let viewport of this.viewports) {
          viewport.renderSVGContext(context, delta);
          const {
            width
          } = viewport.$viewport.get(0).getBoundingClientRect();
          delta.deltaX += width;
        }

        if (true === this.browser.showSampleNames) {
          this.sampleNameViewport.renderSVGContext(context, delta);
        }
      }

      dataRange() {
        return this.track.dataRange ? this.track.dataRange : undefined;
      }

      setDataRange(min, max) {
        if (min !== undefined) {
          this.track.dataRange.min = min;
        }

        if (max !== undefined) {
          this.track.dataRange.max = max;
        }

        this.track.autoscale = false;
        this.repaintViews();
      }

      presentColorPicker(key) {
        if (false === colorPickerExclusionTypes.has(this.track.type)) {
          const trackColors = [];
          const color = this.track.color || this.track.defaultColor;

          if (isString$3(color)) {
            trackColors.push(color);
          }

          if (this.track.altColor && isString$3(this.track.altColor)) {
            trackColors.push(this.track.altColor);
          }

          const defaultColors = trackColors.map(c => c.startsWith("#") ? c : c.startsWith("rgb(") ? IGVColor.rgbToHex(c) : IGVColor.colorNameToHex(c));
          const colorHandlers = {
            color: color => {
              this.track.color = color;
              this.repaintViews();
            },
            altColor: color => {
              this.track.altColor = color;
              this.repaintViews();
            }
          };
          this.browser.genericColorPicker.configure(defaultColors, colorHandlers);
          this.browser.genericColorPicker.setActiveColorHandler(key);
          this.browser.genericColorPicker.show();
        }
      }

      setTrackHeight(newHeight, force) {
        if (!force) {
          if (this.track.minHeight) {
            newHeight = Math.max(this.track.minHeight, newHeight);
          }

          if (this.track.maxHeight) {
            newHeight = Math.min(this.track.maxHeight, newHeight);
          }
        }

        this.track.height = newHeight;
        this.resizeAxisCanvas(this.axis.clientWidth, this.track.height);

        if (typeof this.track.paintAxis === 'function') {
          this.paintAxis();
        }

        for (let {
          $viewport
        } of this.viewports) {
          $viewport.height(newHeight);
        }

        this.sampleNameViewport.viewport.style.height = `${newHeight}px`; // If the track does not manage its own content height set it equal to the viewport height here

        if (typeof this.track.computePixelHeight !== "function") {
          for (let vp of this.viewports) {
            vp.setContentHeight(newHeight);
          }
        }

        this.repaintViews();

        if (false === scrollbarExclusionTypes.has(this.track.type)) {
          this.updateScrollbar();
        }

        this.dragHandle.style.height = `${newHeight}px`;
        this.gearContainer.style.height = `${newHeight}px`;
      }

      updateScrollbar() {
        const viewportHeight = this.viewports[0].$viewport.height();
        this.outerScroll.style.height = `${viewportHeight}px`;
        const viewportContentHeight = maxViewportContentHeight(this.viewports);
        const innerScrollHeight = Math.round(viewportHeight / viewportContentHeight * viewportHeight);

        if (viewportContentHeight > viewportHeight) {
          this.innerScroll.style.display = 'block';
          this.innerScroll.style.height = `${innerScrollHeight}px`;
        } else {
          this.innerScroll.style.display = 'none';
        }
      }

      moveScroller(delta) {
        const y = $$1(this.innerScroll).position().top + delta;
        const top = Math.min(Math.max(0, y), this.outerScroll.clientHeight - this.innerScroll.clientHeight);
        $$1(this.innerScroll).css('top', `${top}px`);
        const contentHeight = maxViewportContentHeight(this.viewports);
        const contentTop = -Math.round(top * (contentHeight / this.viewports[0].$viewport.height()));

        for (let viewport of this.viewports) {
          viewport.setTop(contentTop);
        }

        this.sampleNameViewport.trackScrollDelta = delta;
        this.sampleNameViewport.setTop(contentTop);
      }

      isLoading() {
        for (let viewport of this.viewports) {
          if (viewport.isLoading()) return true;
        }
      }
      /**
       * Repaint all viewports without loading any new data.   Use this for events that change visual aspect of data,
       * e.g. color, sort order, etc, but do not change the genomic state.
       */


      repaintViews() {
        for (let viewport of this.viewports) {
          if (viewport.isVisible()) {
            viewport.repaint();
          }
        }

        if (typeof this.track.paintAxis === 'function') {
          this.paintAxis();
        } // Repaint sample names last


        this.repaintSamples();
      }

      repaintSamples() {
        if (typeof this.track.getSamples === 'function') {
          const samples = this.track.getSamples();
          this.sampleNameViewport.repaint(samples);
        }
      } // track labels


      setTrackLabelName(name) {
        this.viewports.forEach(viewport => viewport.setTrackLabel(name));
      }
      /**
       * Called in response to a window resize event, change in # of multilocus panels, or other event that changes
       * the width of the track view.
       *
       * @param viewportWidth  The width of each viewport in this track view.
       */


      resize(viewportWidth) {
        for (let viewport of this.viewports) {
          viewport.setWidth(viewportWidth);
        }
      }
      /**
       * Update viewports to reflect current genomic state, possibly loading additional data.
       *
       * @param force - if true, force a repaint even if no new data is loaded
       * @returns {Promise<void>}
       */


      async updateViews() {
        if (!(this.browser && this.browser.referenceFrameList)) return;
        const visibleViewports = this.viewports.filter(viewport => viewport.isVisible()); // Shift viewports left/right to current genomic state (pans canvas)

        visibleViewports.forEach(viewport => viewport.shift()); // If dragging (panning) return

        if (this.browser.dragObject) {
          return;
        } // Get viewports to repaint


        let viewportsToRepaint = this.track.autoscale || this.track.autoscaleGroup || this.track.type === 'ruler' ? visibleViewports : visibleViewports.filter(vp => vp.needsRepaint()); // Filter zoomed out views.  This has the side effect or turning off or no the zoomed out notice

        viewportsToRepaint = viewportsToRepaint.filter(viewport => viewport.checkZoomIn()); // Get viewports that require a data load

        const viewportsToReload = viewportsToRepaint.filter(viewport => viewport.needsReload()); // Trigger viewport to load features needed to cover current genomic range
        // NOTE: these must be loaded synchronously, do not user Promise.all,  not all file readers are thread safe

        for (let viewport of viewportsToReload) {
          await viewport.loadFeatures();
        }

        if (this.disposed) return; // Track was removed during load
        // Special case for variant tracks in multilocus view.  The # of rows to allocate to the variant (site)
        // section depends on data from all the views.  We only need to adjust this however if any data was loaded
        // (i.e. reloadableViewports.length > 0)

        if (this.track && typeof this.track.variantRowCount === 'function' && viewportsToReload.length > 0) {
          let maxRow = 0;

          for (let viewport of this.viewports) {
            if (viewport.featureCache && viewport.featureCache.features) {
              maxRow = Math.max(maxRow, viewport.featureCache.features.reduce((a, f) => Math.max(a, f.row || 0), 0));
            }
          }

          const current = this.track.nVariantRows;

          if (current !== maxRow + 1) {
            this.track.variantRowCount(maxRow + 1);

            for (let viewport of this.viewports) {
              viewport.checkContentHeight();
            }
          }
        }

        if (this.track.autoscale) {
          let allFeatures = [];

          for (let visibleViewport of visibleViewports) {
            const referenceFrame = visibleViewport.referenceFrame;
            const start = referenceFrame.start;
            const end = start + referenceFrame.toBP($$1(visibleViewport.contentDiv).width());

            if (visibleViewport.featureCache && visibleViewport.featureCache.features) {
              if (typeof visibleViewport.featureCache.features.getMax === 'function') {
                const max = visibleViewport.featureCache.features.getMax(start, end);
                allFeatures.push({
                  value: max
                });
              } else {
                allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(visibleViewport.featureCache.features, start, end));
              }
            }
          }

          if (typeof this.track.doAutoscale === 'function') {
            this.track.dataRange = this.track.doAutoscale(allFeatures);
          } else {
            this.track.dataRange = doAutoscale(allFeatures);
          }
        }

        for (let vp of viewportsToRepaint) {
          vp.repaint();
        }

        this.adjustTrackHeight(); // Repaint sample names last

        this.repaintSamples();
        this.updateRulerViewportLabels();
      }

      updateRulerViewportLabels() {
        const viewportWidth = this.browser.calculateViewportWidth(this.viewports.length);

        for (let viewport of this.viewports) {
          if ('ruler' === this.track.type) {
            if (this.viewports.length > 1) {
              viewport.presentLocusLabel(viewportWidth);
            } else {
              viewport.dismissLocusLabel();
            }
          }
        }
      }
      /**
       * Return a promise to get all in-view features across all viewports.  Used for group autoscaling.
       */


      async getInViewFeatures() {
        if (!(this.browser && this.browser.referenceFrameList)) {
          return [];
        }

        let allFeatures = [];

        for (let vp of this.viewports) {
          if (vp.needsReload()) {
            await vp.loadFeatures();
          }

          if (vp.featureCache && vp.featureCache.features) {
            const referenceFrame = vp.referenceFrame;
            const start = referenceFrame.start;
            const end = start + referenceFrame.toBP($$1(vp.contentDiv).width());

            if (typeof vp.featureCache.features.getMax === 'function') {
              const max = vp.featureCache.features.getMax(start, end);
              allFeatures.push({
                value: max
              });
            } else {
              allFeatures = allFeatures.concat(FeatureUtils.findOverlapping(vp.featureCache.features, start, end));
            }
          }
        }

        return allFeatures;
      }

      checkContentHeight() {
        for (let viewport of this.viewports) {
          viewport.checkContentHeight();
        }

        this.adjustTrackHeight();
      }

      adjustTrackHeight() {
        var maxHeight = maxViewportContentHeight(this.viewports);

        if (this.track.autoHeight) {
          this.setTrackHeight(maxHeight, false);
        } else if (this.track.paintAxis) {
          // Avoid duplication, paintAxis is already called in setTrackHeight
          this.paintAxis();
        }

        if (false === scrollbarExclusionTypes.has(this.track.type)) {
          const currentTop = this.viewports[0].getContentTop();
          const heights = this.viewports.map(viewport => viewport.getContentHeight());
          const minContentHeight = Math.min(...heights);
          const newTop = Math.min(0, this.viewports[0].$viewport.height() - minContentHeight);

          if (currentTop < newTop) {
            for (let viewport of this.viewports) {
              viewport.$content.css('top', `${newTop}px`);
            }
          }

          this.updateScrollbar();
        }
      }

      createTrackScrollbar(browser) {
        const outerScroll = div$1();
        browser.columnContainer.querySelector('.igv-scrollbar-column').appendChild(outerScroll);
        outerScroll.style.height = `${this.track.height}px`;
        this.outerScroll = outerScroll;

        if (false === scrollbarExclusionTypes.has(this.track.type)) {
          const innerScroll = div$1();
          outerScroll.appendChild(innerScroll);
          this.innerScroll = innerScroll;
          this.addTrackScrollMouseHandlers(browser);
        }
      }

      createTrackDragHandle(browser) {
        const className = 'ideogram' === this.track.type || 'ruler' === this.track.type ? 'igv-track-drag-shim' : 'igv-track-drag-handle';
        this.dragHandle = div$1({
          class: className
        });
        browser.columnContainer.querySelector('.igv-track-drag-column').appendChild(this.dragHandle);
        this.dragHandle.style.height = `${this.track.height}px`;
        this.addTrackDragMouseHandlers(browser);
      }

      createTrackGearPopup(browser) {
        this.gearContainer = div$1();
        browser.columnContainer.querySelector('.igv-gear-menu-column').appendChild(this.gearContainer);
        this.gearContainer.style.height = `${this.track.height}px`;

        if (true === this.track.ignoreTrackMenu) ; else {
          this.gear = div$1();
          this.gearContainer.appendChild(this.gear);
          this.gear.appendChild(createIcon$1('cog'));
          this.trackGearPopup = new MenuPopup(this.gear);
          this.addTrackGearMouseHandlers();
        }
      }

      addAxisEventListener(axis) {
        this.boundAxisClickHander = axisClickHandler.bind(this);
        axis.addEventListener('click', this.boundAxisClickHander);

        function axisClickHandler(event) {
          this.browser.dataRangeDialog.configure(this);
          this.browser.dataRangeDialog.present($$1(this.browser.columnContainer));
        }
      }

      removeAxisEventListener(axis) {
        axis.removeEventListener('click', this.boundAxisClickHander);
      }

      addTrackScrollMouseHandlers(browser) {
        // Mouse Down
        this.boundTrackScrollMouseDownHandler = trackScrollMouseDownHandler.bind(this);
        this.innerScroll.addEventListener('mousedown', this.boundTrackScrollMouseDownHandler);

        function trackScrollMouseDownHandler(event) {
          event.stopPropagation();
          const {
            y
          } = pageCoordinates$1(event);
          $$1(this.innerScroll).data('yDown', y.toString());
          this.boundColumnContainerMouseMoveHandler = columnContainerMouseMoveHandler.bind(this);
          browser.columnContainer.addEventListener('mousemove', this.boundColumnContainerMouseMoveHandler);

          function columnContainerMouseMoveHandler(event) {
            event.stopPropagation();
            const {
              y
            } = pageCoordinates$1(event);
            this.moveScroller(y - parseInt($$1(this.innerScroll).data('yDown')));
            $$1(this.innerScroll).data('yDown', y.toString());
          }
        }

        this.boundColumnContainerMouseUpHandler = columnContainerMouseUpHandler.bind(this);
        browser.columnContainer.addEventListener('mouseup', this.boundColumnContainerMouseUpHandler);

        function columnContainerMouseUpHandler(event) {
          browser.columnContainer.removeEventListener('mousemove', this.boundColumnContainerMouseMoveHandler);
        }
      }

      removeTrackScrollMouseHandlers() {
        if (false === scrollbarExclusionTypes.has(this.track.type)) {
          this.innerScroll.removeEventListener('mousedown', this.boundTrackScrollMouseDownHandler);
          this.browser.columnContainer.removeEventListener('mouseup', this.boundColumnContainerMouseUpHandler);
          this.browser.columnContainer.removeEventListener('mousemove', this.boundColumnContainerMouseMoveHandler);
        }
      }

      addTrackDragMouseHandlers(browser) {
        if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
          let currentDragHandle = undefined; // Mouse Down

          this.boundTrackDragMouseDownHandler = trackDragMouseDownHandler.bind(this);
          this.dragHandle.addEventListener('mousedown', this.boundTrackDragMouseDownHandler);

          function trackDragMouseDownHandler(event) {
            event.preventDefault();
            currentDragHandle = event.target;
            currentDragHandle.classList.add('igv-track-drag-handle-hover');
            browser.startTrackDrag(this);
          } // Mouse Up


          this.boundDocumentTrackDragMouseUpHandler = documentTrackDragMouseUpHandler.bind(this);
          document.addEventListener('mouseup', this.boundDocumentTrackDragMouseUpHandler);

          function documentTrackDragMouseUpHandler(event) {
            // console.log(`${ Date.now() } TrackView - documentTrackDragMouseUpHandler - target ${ event.target.nodeName }`)
            browser.endTrackDrag();

            if (currentDragHandle && event.target !== currentDragHandle) {
              currentDragHandle.classList.remove('igv-track-drag-handle-hover');
            }

            currentDragHandle = undefined;
          } // Mouse Enter


          this.boundTrackDragMouseEnterHandler = trackDragMouseEnterHandler.bind(this);
          this.dragHandle.addEventListener('mouseenter', this.boundTrackDragMouseEnterHandler);

          function trackDragMouseEnterHandler(event) {
            event.preventDefault();

            if (undefined === currentDragHandle) {
              event.target.classList.add('igv-track-drag-handle-hover');
            }

            browser.updateTrackDrag(this);
          } // Mouse Out


          this.dragHandle.addEventListener('mouseout', e => {
            e.preventDefault();

            if (undefined === currentDragHandle) {
              e.target.classList.remove('igv-track-drag-handle-hover');
            }
          });
          this.boundTrackDragMouseOutHandler = trackDragMouseOutHandler.bind(this);
          this.dragHandle.addEventListener('mouseout', this.boundTrackDragMouseOutHandler);

          function trackDragMouseOutHandler(event) {
            event.preventDefault();

            if (undefined === currentDragHandle) {
              event.target.classList.remove('igv-track-drag-handle-hover');
            }
          }
        }
      }

      removeTrackDragMouseHandlers() {
        if ('ideogram' === this.track.id || 'ruler' === this.track.id) ; else {
          this.dragHandle.removeEventListener('mousedown', this.boundTrackDragMouseDownHandler);
          document.removeEventListener('mouseup', this.boundDocumentTrackDragMouseUpHandler);
          this.dragHandle.removeEventListener('mouseup', this.boundTrackDragMouseEnterHandler);
          this.dragHandle.removeEventListener('mouseout', this.boundTrackDragMouseOutHandler);
        }
      }

      addTrackGearMouseHandlers() {
        if (true === this.track.ignoreTrackMenu) ; else {
          this.boundTrackGearClickHandler = trackGearClickHandler.bind(this);
          this.gear.addEventListener('click', this.boundTrackGearClickHandler);

          function trackGearClickHandler(event) {
            event.preventDefault();
            event.stopPropagation();
            this.trackGearPopup.presentMenuList(MenuUtils.trackMenuItemList(this));
          }
        }
      }

      removeTrackGearMouseHandlers() {
        if (true === this.track.ignoreTrackMenu) ; else {
          this.gear.removeEventListener('click', this.boundTrackGearClickHandler);
        }
      }
      /**
       * Do any cleanup here
       */


      dispose() {
        this.removeAxisEventListener(this.axis);
        this.axis.remove();

        for (let viewport of this.viewports) {
          viewport.dispose();
        }

        this.sampleNameViewport.dispose();
        this.removeTrackScrollMouseHandlers();
        this.outerScroll.remove();
        this.removeTrackDragMouseHandlers();
        this.dragHandle.remove();
        this.removeTrackGearMouseHandlers();
        this.gearContainer.remove();

        if (typeof this.track.dispose === "function") {
          this.track.dispose();
        } // TODO: Perhaps better done in track/trackBase


        const track = this.track;

        if (typeof track.dispose === 'function') {
          track.dispose();
        }

        for (let key of Object.keys(track)) {
          track[key] = undefined;
        }

        for (let key of Object.keys(this)) {
          this[key] = undefined;
        }

        if (this.alert) {
          this.alert.container.remove(); // This is quite obviously a hack, need a "dispose" method on AlertDialog
        }

        this.disposed = true;
      }

      paintAxis() {
        if (typeof this.track.paintAxis === 'function') {
          // Set the canvas buffer size, this is the resolution it is drawn at.  This is done here in case the browser
          // has been drug between screens at different dpi resolutions since the last repaint
          const {
            width,
            height
          } = this.axisCanvas.getBoundingClientRect();
          const dpi = window.devicePixelRatio || 1;
          this.axisCanvas.height = dpi * height;
          this.axisCanvas.width = dpi * width; // Get a scaled context to draw aon

          const axisCanvasContext = this.axisCanvas.getContext('2d');
          axisCanvasContext.scale(dpi, dpi);
          this.track.paintAxis(axisCanvasContext, width, height);
        }
      }

    }

    function renderSVGAxis(context, track, axisCanvas, deltaX, deltaY) {
      if (typeof track.paintAxis === 'function') {
        const {
          y,
          width,
          height
        } = axisCanvas.getBoundingClientRect();
        const str = (track.name || track.id).replace(/\W/g, '');
        const id = `${str}_axis_guid_${guid$2()}`;
        context.saveWithTranslationAndClipRect(id, deltaX, y + deltaY, width, height, 0);
        track.paintAxis(context, width, height);
        context.restore();
      }
    } // css - $igv-axis-column-width: 50px;


    const igv_axis_column_width = 50;

    function maxViewportContentHeight(viewports) {
      const heights = viewports.map(viewport => viewport.getContentHeight());
      return Math.max(...heights);
    }

    /**
     * Decoder for bedpe records.
     *
     * Bedpe format was created by Aaron Quinlan et al as part of the bedtools project.
     * The spec is here:  https://bedtools.readthedocs.io/en/latest/content/general-usage.html,  however there
     * are off spec variants, an important one being a 7 column format with score in place of the standard
     * name column.
     *
     * Another common variant is a "hiccups" output file, which is standard bedpe with the exception of a header line
     * of the form
     * chr1    x1    x2    chr2    y1    y2    name    score    strand1    strand2    color    observed    expectedBL    expectedDonut    expectedH    expectedV    fdrBL    fdrDonut    fdrH    fdrV
     *
     * @param tokens
     * @param ignore
     * @returns {{start1: number, end2: number, end1: number, chr1: *, chr2: *, start2: number}|undefined}
     */

    function decodeBedpe(tokens, header) {
      if (tokens.length < 6) {
        console.log("Skipping line: " + tokens.join(' '));
        return undefined;
      }

      var feature = {
        chr1: tokens[0],
        start1: Number.parseInt(tokens[1]),
        end1: Number.parseInt(tokens[2]),
        chr2: tokens[3],
        start2: Number.parseInt(tokens[4]),
        end2: Number.parseInt(tokens[5])
      };

      if (isNaN(feature.start1) || isNaN(feature.end1) || isNaN(feature.start2) || isNaN(feature.end2)) {
        //throw Error(`Error parsing line: ${tokens.join('\t')}`);
        return undefined;
      }

      if (tokens.length > 6 && tokens[6] !== ".") {
        feature.name = tokens[6];
      }

      if (tokens.length > 7 && tokens[7] !== ".") {
        feature.score = Number(tokens[7]);
      }

      if (tokens.length > 8 && tokens[8] !== ".") {
        feature.strand1 = tokens[8];
      }

      if (tokens.length > 9 && tokens[9] !== ".") {
        feature.strand2 = tokens[9];
      } // Optional extra columns


      if (header) {
        const colorColumn = header.colorColumn;

        if (colorColumn && colorColumn < tokens.length) {
          feature.color = IGVColor.createColorString(tokens[colorColumn]);
        }

        const thicknessColumn = header.thicknessColumn;

        if (thicknessColumn && thicknessColumn < tokens.length) {
          feature.thickness = tokens[thicknessColumn];
        }

        if (tokens.length > 10 && header.columnNames && header.columnNames.length === tokens.length) {
          feature.extras = tokens.slice(10);
        }
      } // Set total extent of feature


      if (feature.chr1 === feature.chr2) {
        feature.chr = feature.chr1;
        feature.start = Math.min(feature.start1, feature.start2);
        feature.end = Math.max(feature.end1, feature.end2);
      }

      return feature;
    }
    /**
     * Hack for non-standard bedPE formats, where numeric score can be in column 7 (name field from spec)
     * @param features
     */


    function fixBedPE(features) {
      if (features.length == 0) return; // Assume all features have same properties

      const firstFeature = features[0];

      if (firstFeature.score === undefined && firstFeature.name !== undefined) {
        // Name field (col 7) is sometimes used for score.
        for (let f of features) {
          if (!(isNumber(f.name) || f.name === '.')) return;
        }

        for (let f of features) {
          f.score = Number(f.name);
          delete f.name;
        }
      } // Make copies of inter-chr features, one for each chromosome


      const interChrFeatures = features.filter(f => f.chr1 !== f.chr2);

      for (let f1 of interChrFeatures) {
        const f2 = Object.assign({}, f1);
        f2.dup = true;
        features.push(f2);
        f1.chr = f1.chr1;
        f1.start = f1.start1;
        f1.end = f1.end1;
        f2.chr = f2.chr2;
        f2.start = f2.start2;
        f2.end = f2.end2;
      }
    }
    /**
     * Special decoder for Hic Domain files.   In these files feature1 == feature2, they are really bed records.
     * @param tokens
     * @param ignore
     * @returns {*}
     */


    function decodeBedpeDomain(tokens, header) {
      if (tokens.length < 8) return undefined;
      return {
        chr: tokens[0],
        start: Number.parseInt(tokens[1]),
        end: Number.parseInt(tokens[2]),
        color: IGVColor.createColorString(tokens[6]),
        value: Number(tokens[7])
      };
    }

    /**
     * Decode UCSC "interact" files.  See https://genome.ucsc.edu/goldenpath/help/interact.html
     *
     0  string chrom;        "Chromosome (or contig, scaffold, etc.). For interchromosomal, use 2 records"
     1  uint chromStart;     "Start position of lower region. For interchromosomal, set to chromStart of this region"
     2  uint chromEnd;       "End position of upper region. For interchromosomal, set to chromEnd of this region"
     3  string name;         "Name of item, for display.  Usually 'sourceName/targetName/exp' or empty"
     4  uint score;          "Score (0-1000)"
     5  double value;        "Strength of interaction or other data value. Typically basis for score"
     6  string exp;          "Experiment name (metadata for filtering). Use . if not applicable"
     7  string color;        "Item color.  Specified as r,g,b or hexadecimal #RRGGBB or html color name, as in //www.w3.org/TR/css3-color/#html4. Use 0 and spectrum setting to shade by score"
     8  string sourceChrom;  "Chromosome of source region (directional) or lower region. For non-directional interchromosomal, chrom of this region."
     9  uint sourceStart;    "Start position in chromosome of source/lower/this region"
     10 uint sourceEnd;      "End position in chromosome of source/lower/this region"
     11 string sourceName;   "Identifier of source/lower/this region"
     12 string sourceStrand; "Orientation of source/lower/this region: + or -.  Use . if not applicable"
     13 string targetChrom;  "Chromosome of target region (directional) or upper region. For non-directional interchromosomal, chrom of other region"
     14 uint targetStart;    "Start position in chromosome of target/upper/this region"
     15 uint targetEnd;      "End position in chromosome of target/upper/this region"
     16 string targetName;   "Identifier of target/upper/this region"
     17 string targetStrand; "Orientation of target/upper/this region: + or -.  Use . if not applicable"
     *
     * @param tokens
     * @param ignore
     * @returns {*}
     */

    function decodeInteract(tokens, header) {
      if (tokens.length < 6) {
        console.log("Skipping line: " + tokens.join(' '));
        return undefined;
      }

      var feature = {
        chr: tokens[0],
        start: tokens[1],
        end: tokens[2],
        chr1: tokens[8],
        start1: Number.parseInt(tokens[9]),
        end1: Number.parseInt(tokens[10]),
        chr2: tokens[13],
        start2: Number.parseInt(tokens[14]),
        end2: Number.parseInt(tokens[15]),
        name: tokens[3],
        score: Number(tokens[4]),
        value: Number(tokens[5]),
        color: tokens[7] === '.' ? undefined : tokens[7] === "0" ? "rgb(0,0,0)" : tokens[7]
      };
      return feature;
    }

    /**
     * Some interpretations of the sequence ontology needed to assemble GFF transcripts.
     *
     */
    const transcriptTypes = new Set(['transcript', 'primary_transcript', 'processed_transcript', 'mRNA', 'mrna', 'lnc_RNA', 'miRNA', 'ncRNA', 'rRNA', 'scRNA', 'snRNA', 'snoRNA', 'tRNA']);
    const cdsTypes = new Set(['CDS', 'cds']);
    const codonTypes = new Set(['start_codon', 'stop_codon']);
    const utrTypes = new Set(['5UTR', '3UTR', 'UTR', 'five_prime_UTR', 'three_prime_UTR', "3'-UTR", "5'-UTR"]);
    const exonTypes = new Set(['exon', 'coding-exon']);
    const transcriptPartTypes = new Set();

    for (let cltn of [cdsTypes, codonTypes, utrTypes, exonTypes]) {
      for (let t of cltn) {
        transcriptPartTypes.add(t);
      }
    }

    function isExon(type) {
      return exonTypes.has(type);
    }

    function isIntron(type) {
      return type.includes("intron");
    }

    function isCoding(type) {
      return cdsTypes.has(type) || codonTypes.has(type);
    }

    function isUTR(type) {
      return utrTypes.has(type);
    }

    function isTranscript(type) {
      return transcriptTypes.has(type) || type.endsWith("RNA") || type.endsWith("transcript");
    }

    function isTranscriptPart(type) {
      return transcriptPartTypes.has(type) || type.endsWith("RNA") || isIntron(type);
    }

    const filterPopupProperties = new Set(["id", "parent", "name"]);

    class GFFFeature {
      constructor(properties) {
        Object.assign(this, properties);
      }

      popupData(genomicLocation) {
        const pd = this.geneObject ? this.geneObject.popupData() : [];

        if (this.geneObject) {
          pd.push('<hr/>');
        }

        if (this.name) {
          pd.push({
            name: 'Name',
            value: this.name
          });
        }

        pd.push({
          name: 'Type',
          value: this.type
        });
        pd.push({
          name: 'Source',
          value: this.source
        });

        if (this.score !== undefined) {
          pd.push({
            name: 'Score',
            value: this.score
          });
        }

        if (this.attributeString) {
          const atts = parseAttributeString(this.attributeString, this.delim);

          for (let [key, value] of atts) {
            if (value !== undefined && value.length > 0 && !filterPopupProperties.has(key.toLowerCase())) {
              pd.push({
                name: key + ":",
                value: value
              });
            }
          }
        }

        pd.push({
          name: 'Location',
          value: `${this.chr}:${numberFormatter$1(this.start + 1)}-${numberFormatter$1(this.end)}`
        });
        return pd;
      }

      getAttributeValue(attributeName) {
        if (this.hasOwnProperty(attributeName)) {
          return this[attributeName];
        } else {
          // TODO -- fetch from attribute string and cache
          if (!this._attributeCache) {
            this._attributeCache = new Map();
          }

          if (this._attributeCache.has(attributeName)) {
            return this._attributeCache.get(attributeName);
          } else {
            const atts = parseAttributeString(this.attributeString, this.delim);
            let v;

            for (let [key, value] of atts) {
              if (key === attributeName) {
                v = value;
                break;
              }
            }

            this._attributeCache.set(attributeName, v);

            return v;
          }
        }
      }

    }

    class GFFTranscript extends GFFFeature {
      constructor(feature) {
        super(feature);
        this.exons = [];
        this.parts = [];
      }

      addExon(feature) {
        this.exons.push(feature); // Expand feature --  for transcripts not explicitly represented in the file (gtf)

        this.start = Math.min(this.start, feature.start);
        this.end = Math.max(this.end, feature.end);
      }

      addPart(feature) {
        this.parts.push(feature);
      }

      assembleParts() {
        if (this.parts.length === 0) return;
        this.parts.sort(function (a, b) {
          return a.start - b.start;
        }); // Create exons, if neccessary

        let lastStart = this.parts[0].start;
        let lastEnd = this.parts[0].end;

        for (let i = 1; i < this.parts.length; i++) {
          const part = this.parts[i];

          if (isIntron(part.type)) {
            continue;
          }

          if (part.start <= lastEnd) {
            lastEnd = Math.max(lastEnd, part.end);
          } else {
            let exon = this.findExonContaining({
              start: lastStart,
              end: lastEnd
            });

            if (!exon) {
              this.exons.push({
                start: lastStart,
                end: lastEnd,
                psuedo: true
              });
            }

            lastStart = part.start;
            lastEnd = part.end;
          }
        }

        let exon = this.findExonContaining({
          start: lastStart,
          end: lastEnd
        });

        if (!exon) {
          this.exons.push({
            start: lastStart,
            end: lastEnd,
            psuedo: true
          });
          this.start = Math.min(this.start, lastStart);
          this.end = Math.max(this.end, lastEnd);
        }

        for (let part of this.parts) {
          const type = part.type;

          if (isCoding(type)) {
            this.addCDS(part);
          } else if (isUTR(type)) {
            this.addUTR(part);
          }
        }
      }

      findExonContaining(_ref) {
        let {
          start,
          end
        } = _ref;

        for (let exon of this.exons) {
          if (exon.end >= end && exon.start <= start) {
            return exon;
          }
        }

        return undefined;
      }

      addCDS(cds) {
        let exon;
        const exons = this.exons;

        for (let e of exons) {
          if (e.start <= cds.start && e.end >= cds.end) {
            exon = e;
            break;
          }
        }

        if (exon) {
          exon.cdStart = exon.cdStart ? Math.min(cds.start, exon.cdStart) : cds.start;
          exon.cdEnd = exon.cdEnd ? Math.max(cds.end, exon.cdEnd) : cds.end;
        } else {
          // cds.cdStart = cds.start
          // cds.cdEnd = cds.end
          // exons.push(cds)
          console.error("No exon found spanning " + cds.start + "-" + cds.end);
        } // Expand feature --  for transcripts not explicitly represented in the file (gtf files)
        // this.start = Math.min(this.start, cds.start);
        // this.end = Math.max(this.end, cds.end);


        this.cdStart = this.cdStart ? Math.min(cds.start, this.cdStart) : cds.start;
        this.cdEnd = this.cdEnd ? Math.max(cds.end, this.cdEnd) : cds.end;
      }

      addUTR(utr) {
        let exon;
        const exons = this.exons; // Find exon containing CDS

        for (let i = 0; i < exons.length; i++) {
          if (exons[i].start <= utr.start && exons[i].end >= utr.end) {
            exon = exons[i];
            break;
          }
        }

        if (exon) {
          if (utr.start === exon.start && utr.end === exon.end) {
            exon.utr = true;
          } else {
            if (utr.end < exon.end) {
              exon.cdStart = utr.end;
            }

            if (utr.start > exon.start) {
              exon.cdEnd = utr.start;
            }
          }
        } else {
          // utr.utr = true
          // exons.push(utr)
          console.error("No exon found spanning " + cds.start + "-" + cds.end);
        } // Expand feature --  for transcripts not explicitly represented in the file
        // this.start = Math.min(this.start, utr.start);
        // this.end = Math.max(this.end, utr.end);

      }

      finish() {
        this.assembleParts();
        var cdStart = this.cdStart;
        var cdEnd = this.cdEnd;
        this.exons.sort(function (a, b) {
          return a.start - b.start;
        }); // Search for UTR exons that were not explicitly tagged

        if (cdStart) {
          this.exons.forEach(function (exon) {
            if (exon.end < cdStart || exon.start > cdEnd) exon.utr = true;
          });
        }
      }

      popupData(genomicLocation) {
        const pd = super.popupData(genomicLocation); // If clicked over an exon add its attributes

        for (let exon of this.exons) {
          if (exon.pseudo) continue; // An implicit exon

          if (genomicLocation >= exon.start && genomicLocation < exon.end && typeof exon.popupData === 'function') {
            pd.push('<hr/>');
            const exonData = exon.popupData(genomicLocation);

            for (let att of exonData) {
              pd.push(att);
            }
          }
        }

        for (let part of this.parts) {
          if (genomicLocation >= part.start && genomicLocation < part.end && typeof part.popupData === 'function') {
            pd.push('<hr/>');
            const partData = part.popupData(genomicLocation);

            for (let att of partData) {
              pd.push(att);
            }
          }
        }

        return pd;
      }

    }

    function decode(tokens, header) {
      const format = header.format;

      if (tokens.length < 9) {
        return undefined; // Not a valid gff record
      }

      const delim = 'gff3' === format ? '=' : ' ';
      return new GFFFeature({
        source: tokens[1],
        type: tokens[2],
        chr: tokens[0],
        start: parseInt(tokens[3]) - 1,
        end: parseInt(tokens[4]),
        score: "." === tokens[5] ? undefined : Number(tokens[5]),
        strand: tokens[6],
        phase: "." === tokens[7] ? 0 : parseInt(tokens[7]),
        attributeString: tokens[8],
        delim: delim
      });
    }
    /**
     * Decode a single gff record (1 line in file).  Aggregations such as gene models are constructed at a higher level.
     *      ctg123 . mRNA            1050  9000  .  +  .  ID=mRNA00001;Parent=gene00001
     * @param tokens
     * @param ignore
     * @returns {*}
     */


    function decodeGFF3(tokens, header) {
      const feature = decode(tokens, header);

      if (!feature) {
        return;
      }

      const attributes = parseAttributeString(feature.attributeString, feature.delim); // Search for color value as case insenstivie key

      for (let [key, value] of attributes) {
        const keyLower = key.toLowerCase();

        if ("color" === keyLower || "colour" === keyLower) {
          feature.color = IGVColor.createColorString(value);
        } else if (key === "ID") {
          feature.id = value;
        } else if (key === "Parent") {
          feature.parent = value;
        }
      }

      return feature;
    }
    /**
     * GTF format example:
     NC_000008.11    BestRefSeq    gene    127735434    127742951    .    +    .    gene_id "MYC"; transcript_id ""; db_xref "GeneID:4609"; db_xref "HGNC:HGNC:7553"; db_xref "MIM:190080"; description "MYC proto-oncogene, bHLH transcription factor"; gbkey "Gene"; gene "MYC"; gene_biotype "protein_coding"; gene_synonym "bHLHe39"; gene_synonym "c-Myc"; gene_synonym "MRTL"; gene_synonym "MYCC";
     NC_000008.11    BestRefSeq    transcript    127735434    127742951    .    +    .    gene_id "MYC"; transcript_id "NM_001354870.1"; db_xref "GeneID:4609"; gbkey "mRNA"; gene "MYC"; product "MYC proto-oncogene, bHLH transcription factor, transcript variant 2"; transcript_biotype "mRNA";
     NC_000008.11    BestRefSeq    exon    127735434    127736623    .    +    .    gene_id "MYC"; transcript_id "NM_001354870.1"; db_xref "GeneID:4609"; gene "MYC"; product "MYC proto-oncogene, bHLH transcription factor, transcript variant 2"; transcript_biotype "mRNA"; exon_number "1";
     *
     * @param tokens
     * @param ignore
     * @returns {*}
     */


    function decodeGTF(tokens, header) {
      const feature = decode(tokens, header);

      if (!feature) {
        return;
      }

      const attributes = parseAttributeString(feature.attributeString, feature.delim); // GTF files specify neither ID nor parent fields, but they can be inferred from common conventions

      let idField;
      let parentField;

      switch (feature.type) {
        case "gene":
          idField = "gene_id";
          break;

        case "transcript":
          idField = "transcript_id";
          parentField = "gene_id";
          break;

        default:
          parentField = "transcript_id";
      }

      for (let [key, value] of attributes) {
        const keyLower = key.toLowerCase();

        if ("color" === keyLower || "colour" === keyLower) {
          feature.color = IGVColor.createColorString(value);
        } else if (key === idField) {
          feature.id = value;
        } else if (key === parentField) {
          feature.parent = value;
        }
      }

      return feature;
    }
    /**
     * Parse the attribute string, returning an array of key-value pairs.  An array is used rather than a map as attribute
     * keys are not required to be unique.
     *
     * @param attributeString
     * @param keyValueDelim
     * @returns {[]}
     */


    function parseAttributeString(attributeString, keyValueDelim) {
      // parse 'attributes' string (see column 9 docs in https://github.com/The-Sequence-Ontology/Specifications/blob/master/gff3.md)
      var attributes = [];

      for (let kv of attributeString.split(';')) {
        kv = kv.trim();
        const idx = kv.indexOf(keyValueDelim);

        if (idx > 0 && idx < kv.length - 1) {
          const key = kv.substring(0, idx);
          let value = stripQuotes(decodeURIComponent(kv.substring(idx + 1).trim()));
          attributes.push([key, value]);
        }
      }

      return attributes;
    }

    function stripQuotes(value) {
      if (value.startsWith('"') && value.endsWith('"')) {
        value = value.substr(1, value.length - 2);
      }

      return value;
    }

    /**
     * Wrapper class to record a decoding error.
     */
    class DecodeError {
      constructor(message) {
        this.message = message;
      }

    }

    /**
     * Decode the UCSC bed format.  Only the first 3 columns (chr, start, end) are required.   The remaining columns
     * must follow standard bed order, but we will tolerate deviations after column 3.
     *
     * @param tokens
     * @param ignore
     * @returns decoded feature, or null if this is not a valid record
     */

    function decodeBed(tokens, header) {
      if (tokens.length < 3) return undefined;
      header && header.gffTags;
      const chr = tokens[0];
      const start = parseInt(tokens[1]);
      const end = tokens.length > 2 ? parseInt(tokens[2]) : start + 1;

      if (isNaN(start) || isNaN(end)) {
        return new DecodeError(`Unparsable bed record.`);
      }

      const feature = new UCSCBedFeature({
        chr: chr,
        start: start,
        end: end,
        score: 1000
      });

      try {
        if (tokens.length > 3) {
          // Potentially parse name field as GFF column 9 style streng.
          if (tokens[3].indexOf(';') > 0 && tokens[3].indexOf('=') > 0) {
            const attributeKVs = parseAttributeString(tokens[3], '=');
            feature.attributes = {};

            for (let kv of attributeKVs) {
              feature.attributes[kv[0]] = kv[1];
            }
          }

          if (!feature.name) {
            feature.name = tokens[3] === '.' ? '' : tokens[3];
          }
        }

        if (tokens.length > 4) {
          feature.score = tokens[4] === '.' ? 0 : Number(tokens[4]);

          if (isNaN(feature.score)) {
            return feature;
          }
        }

        if (tokens.length > 5) {
          feature.strand = tokens[5];

          if (!(feature.strand === '.' || feature.strand === '+' || feature.strand === '-')) {
            return feature;
          }
        }

        if (tokens.length > 6) {
          feature.cdStart = parseInt(tokens[6]);

          if (isNaN(feature.cdStart)) {
            return feature;
          }
        }

        if (tokens.length > 7) {
          feature.cdEnd = parseInt(tokens[7]);

          if (isNaN(feature.cdEnd)) {
            return feature;
          }
        }

        if (tokens.length > 8) {
          if (tokens[8] !== "." && tokens[8] !== "0") feature.color = IGVColor.createColorString(tokens[8]);
        }

        if (tokens.length > 11) {
          const exonCount = parseInt(tokens[9]); // Some basic validation

          if (exonCount > 1000) {
            // unlikely
            return feature;
          }

          const exonSizes = tokens[10].replace(/,$/, '').split(',');
          const exonStarts = tokens[11].replace(/,$/, '').split(',');

          if (!(exonSizes.length === exonStarts.length && exonCount === exonSizes.length)) {
            return feature;
          }

          const exons = [];

          for (let i = 0; i < exonCount; i++) {
            const eStart = start + parseInt(exonStarts[i]);
            const eEnd = eStart + parseInt(exonSizes[i]);
            exons.push({
              start: eStart,
              end: eEnd
            });
          }

          findUTRs$1(exons, feature.cdStart, feature.cdEnd);
          feature.exons = exons;
        } // Optional extra columns


        if (header) {
          let thicknessColumn = header.thicknessColumn;
          let colorColumn = header.colorColumn;

          if (colorColumn && colorColumn < tokens.length) {
            feature.color = IGVColor.createColorString(tokens[colorColumn]);
          }

          if (thicknessColumn && thicknessColumn < tokens.length) {
            feature.thickness = tokens[thicknessColumn];
          }
        }
      } catch (e) {}

      return feature;
    }
    /**
     * Decode a UCSC repeat masker record.
     *
     * Columns, from UCSC documentation
     *
     * 0  bin    585    smallint(5) unsigned    Indexing field to speed chromosome range queries.
     * 1  swScore    1504    int(10) unsigned    Smith Waterman alignment score
     * 2  milliDiv    13    int(10) unsigned    Base mismatches in parts per thousand
     * 3  milliDel    4    int(10) unsigned    Bases deleted in parts per thousand
     * 4  milliIns    13    int(10) unsigned    Bases inserted in parts per thousand
     * 5  genoName    chr1    varchar(255)    Genomic sequence name
     * 6  genoStart    10000    int(10) unsigned    Start in genomic sequence
     * 7  genoEnd    10468    int(10) unsigned    End in genomic sequence
     * 8  genoLeft    -249240153    int(11)    -#bases after match in genomic sequence
     * 9  strand    +    char(1)    Relative orientation + or -
     * 10 repName    (CCCTAA)n    varchar(255)    Name of repeat
     * 11 repClass    Simple_repeat    varchar(255)    Class of repeat
     * 12 repFamily    Simple_repeat    varchar(255)    Family of repeat
     * 13 repStart    1    int(11)    Start (if strand is +) or -#bases after match (if strand is -) in repeat sequence
     * 14 repEnd    463    int(11)    End in repeat sequence
     * 15 repLeft    0    int(11)    -#bases after match (if strand is +) or start (if strand is -) in repeat sequence
     * 16 id    1    char(1)    First digit of id field in RepeatMasker .out file. Best ignored.
     */


    function decodeRepeatMasker(tokens, header) {
      if (tokens.length <= 15) return undefined;
      const feature = {
        swScore: Number.parseInt(tokens[1]),
        milliDiv: Number.parseInt(tokens[2]),
        milliDel: Number.parseInt(tokens[3]),
        milliIns: Number.parseInt(tokens[4]),
        chr: tokens[5],
        start: Number.parseInt(tokens[6]),
        end: Number.parseInt(tokens[7]),
        //genoLeft: tokens[8],
        strand: tokens[9],
        repName: tokens[10],
        repClass: tokens[11],
        repFamily: tokens[12],
        repStart: Number.parseInt(tokens[13]),
        repEnd: Number.parseInt(tokens[14]),
        repLeft: Number.parseInt(tokens[15])
      };
      return feature;
    }
    /**
     * Decode a UCSC "genePred" record.
     *
     * @param tokens
     * @param ignore
     * @returns {*}
     */


    function decodeGenePred(tokens, header) {
      var shift = header.shift === undefined ? 0 : 1;
      if (tokens.length <= 9 + shift) return undefined;
      const cdStart = parseInt(tokens[5 + shift]);
      const cdEnd = parseInt(tokens[6 + shift]);
      var feature = {
        name: tokens[0 + shift],
        chr: tokens[1 + shift],
        strand: tokens[2 + shift],
        start: parseInt(tokens[3 + shift]),
        end: parseInt(tokens[4 + shift]),
        cdStart: cdStart,
        cdEnd: cdEnd,
        id: tokens[0 + shift]
      },
          exonCount = parseInt(tokens[7 + shift]),
          exonStarts = tokens[8 + shift].split(','),
          exonEnds = tokens[9 + shift].split(','),
          exons = [];

      for (let i = 0; i < exonCount; i++) {
        const start = parseInt(exonStarts[i]);
        const end = parseInt(exonEnds[i]);
        exons.push({
          start: start,
          end: end
        });
      }

      findUTRs$1(exons, cdStart, cdEnd);
      feature.exons = exons;
      return feature;
    }
    /**
     * Decode a UCSC "genePredExt" record.  refGene files are in this format.
     *
     * @param tokens
     * @param ignore
     * @returns {*}
     */


    function decodeGenePredExt(tokens, header) {
      var shift = header.shift === undefined ? 0 : 1;
      if (tokens.length <= 11 + shift) return undefined;
      const cdStart = parseInt(tokens[5 + shift]);
      const cdEnd = parseInt(tokens[6 + shift]);
      const feature = {
        name: tokens[11 + shift],
        chr: tokens[1 + shift],
        strand: tokens[2 + shift],
        start: parseInt(tokens[3 + shift]),
        end: parseInt(tokens[4 + shift]),
        cdStart: cdStart,
        cdEnd: cdEnd,
        id: tokens[0 + shift]
      },
            exonCount = parseInt(tokens[7 + shift]),
            exonStarts = tokens[8 + shift].split(','),
            exonEnds = tokens[9 + shift].split(','),
            exons = [];

      for (let i = 0; i < exonCount; i++) {
        const start = parseInt(exonStarts[i]);
        const end = parseInt(exonEnds[i]);
        exons.push({
          start: start,
          end: end
        });
      }

      findUTRs$1(exons, cdStart, cdEnd);
      feature.exons = exons;
      return feature;
    }
    /**
     * Decode a UCSC "refFlat" record
     * @param tokens
     * @param ignore
     * @returns {*}
     */


    function decodeReflat(tokens, header) {
      var shift = header.shift === undefined ? 0 : 1;
      if (tokens.length <= 10 + shift) return undefined;
      const cdStart = parseInt(tokens[6 + shift]);
      const cdEnd = parseInt(tokens[7 + shift]);
      var feature = {
        name: tokens[0 + shift],
        id: tokens[1 + shift],
        chr: tokens[2 + shift],
        strand: tokens[3 + shift],
        start: parseInt(tokens[4 + shift]),
        end: parseInt(tokens[5 + shift]),
        cdStart: cdStart,
        cdEnd: cdEnd
      },
          exonCount = parseInt(tokens[8 + shift]),
          exonStarts = tokens[9 + shift].split(','),
          exonEnds = tokens[10 + shift].split(','),
          exons = [];

      for (let i = 0; i < exonCount; i++) {
        const start = parseInt(exonStarts[i]);
        const end = parseInt(exonEnds[i]);
        exons.push({
          start: start,
          end: end
        });
      }

      findUTRs$1(exons, cdStart, cdEnd);
      feature.exons = exons;
      return feature;
    }

    function findUTRs$1(exons, cdStart, cdEnd) {
      for (let exon of exons) {
        const end = exon.end;
        const start = exon.start;

        if (end < cdStart || start > cdEnd) {
          exon.utr = true;
        } else {
          if (cdStart >= start && cdStart <= end) {
            exon.cdStart = cdStart;
          }

          if (cdEnd >= start && cdEnd <= end) {
            exon.cdEnd = cdEnd;
          }
        }
      }
    }

    function decodePeak(tokens, header) {
      var tokenCount, chr, start, end, strand, name, score, qValue, signal, pValue;
      tokenCount = tokens.length;

      if (tokenCount < 9) {
        return undefined;
      }

      chr = tokens[0];
      start = parseInt(tokens[1]);
      end = parseInt(tokens[2]);
      name = tokens[3];
      score = Number(tokens[4]);
      strand = tokens[5].trim();
      signal = Number(tokens[6]);
      pValue = Number(tokens[7]);
      qValue = Number(tokens[8]);
      if (score === 0) score = signal;
      return {
        chr: chr,
        start: start,
        end: end,
        name: name,
        score: score,
        strand: strand,
        signal: signal,
        pValue: pValue,
        qValue: qValue
      };
    }

    function decodeBedGraph(tokens, header) {
      var chr, start, end, value;
      if (tokens.length <= 3) return undefined;
      chr = tokens[0];
      start = parseInt(tokens[1]);
      end = parseInt(tokens[2]);
      value = Number(tokens[3]);
      const feature = {
        chr: chr,
        start: start,
        end: end,
        value: value
      }; // Optional extra columns

      if (header) {
        let colorColumn = header.colorColumn;

        if (colorColumn && colorColumn < tokens.length) {
          feature.color = IGVColor.createColorString(tokens[colorColumn]);
        }
      }

      return feature;
    }

    function decodeWig(tokens, header) {
      const wig = header.wig;

      if (wig && wig.format === "fixedStep") {
        const ss = wig.index * wig.step + wig.start;
        const ee = ss + wig.span;
        const value = Number(tokens[0]);
        ++wig.index;
        return isNaN(value) ? null : {
          chr: wig.chrom,
          start: ss,
          end: ee,
          value: value
        };
      } else if (wig && wig.format === "variableStep") {
        if (tokens.length < 2) return null;
        const ss = parseInt(tokens[0], 10) - 1;
        const ee = ss + wig.span;
        const value = Number(tokens[1]);
        return isNaN(value) ? null : {
          chr: wig.chrom,
          start: ss,
          end: ee,
          value: value
        };
      } else {
        return decodeBedGraph(tokens);
      }
    }

    function decodeSNP(tokens, header) {
      if (tokens.length < 6) return undefined;
      const autoSql = ['bin', 'chr', 'start', 'end', 'name', 'score', 'strand', 'refNCBI', 'refUCSC', 'observed', 'molType', 'class', 'valid', 'avHet', 'avHetSE', 'func', 'locType', 'weight', 'exceptions', 'submitterCount', 'submitters', 'alleleFreqCount', 'alleles', 'alleleNs', 'alleleFreqs', 'bitfields'];
      const feature = {
        chr: tokens[1],
        start: Number.parseInt(tokens[2]),
        end: Number.parseInt(tokens[3]),
        name: tokens[4],
        score: Number.parseInt(tokens[5])
      };
      const n = Math.min(tokens.length, autoSql.length);

      for (let i = 6; i < n; i++) {
        feature[autoSql[i]] = tokens[i];
      }

      return feature;
    }

    class UCSCBedFeature {
      constructor(properties) {
        Object.assign(this, properties);
      }

      getAttributeValue(attributeName) {
        if (this.hasOwnProperty(attributeName)) {
          return this[attributeName];
        } else if (this.attributes) {
          return this.attributes[attributeName];
        }
      }

    }

    function decodeFusionJuncSpan(tokens, header) {
      /*
       Format:
        0       #scaffold
       1       fusion_break_name
       2       break_left
       3       break_right
       4       num_junction_reads
       5       num_spanning_frags
       6       spanning_frag_coords
        0       B3GNT1--NPSR1
       1       B3GNT1--NPSR1|2203-10182
       2       2203
       3       10182
       4       189
       5       1138
       6       1860-13757,1798-13819,1391-18127,1443-17174,...
        */
      if (tokens.length < 7) return undefined;
      var chr = tokens[0];
      var fusion_name = tokens[1];
      var junction_left = parseInt(tokens[2]);
      var junction_right = parseInt(tokens[3]);
      var num_junction_reads = parseInt(tokens[4]);
      var num_spanning_frags = parseInt(tokens[5]);
      var spanning_frag_coords_text = tokens[6];
      var feature = {
        chr: chr,
        name: fusion_name,
        junction_left: junction_left,
        junction_right: junction_right,
        num_junction_reads: num_junction_reads,
        num_spanning_frags: num_spanning_frags,
        spanning_frag_coords: [],
        start: -1,
        end: -1
      }; // set start and end later based on min/max of span coords

      var min_coord = junction_left;
      var max_coord = junction_right;

      if (num_spanning_frags > 0) {
        var coord_pairs = spanning_frag_coords_text.split(',');

        for (var i = 0; i < coord_pairs.length; i++) {
          var split_coords = coord_pairs[i].split('-');
          var span_left = split_coords[0];
          var span_right = split_coords[1];

          if (span_left < min_coord) {
            min_coord = span_left;
          }

          if (span_right > max_coord) {
            max_coord = span_right;
          }

          feature.spanning_frag_coords.push({
            left: span_left,
            right: span_right
          });
        }
      }

      feature.start = min_coord;
      feature.end = max_coord;
      return feature;
    }

    function decodeGtexGWAS(tokens, header) {
      //chrom	chromStart	chromEnd	Strongest SNP-risk allele	Disease/Phenotype	P-value	Odds ratio or beta	PUBMEDID
      //1	1247493	1247494	rs12103-A	Inflammatory bowel disease	8.00E-13	1.1	23128233
      const tokenCount = tokens.length;

      if (tokenCount < 7) {
        return null;
      }

      const feature = {
        chr: tokens[0],
        start: parseInt(tokens[1]) - 1,
        end: parseInt(tokens[2]),
        'Strongest SNP-risk allele': tokens[3],
        'Disease/Phenotype': tokens[4],
        'P-value': tokens[5],
        'Odds ratio or beta': tokens[6]
      };

      if (tokens.length > 6) {
        feature['PUBMEDID'] = `<a target = "blank" href = "https://www.ncbi.nlm.nih.gov/pubmed/${tokens[7]}">${tokens[7]}</a>`;
      }

      return feature;
    }

    /**
     * Decode a custom columnar format.  Required columns are 'chr' and 'start'
     *
     * @param tokens
     * @param ignore
     * @returns decoded feature, or null if this is not a valid record
     */
    function decodeCustom(tokens, header) {
      const format = header.customFormat;
      if (tokens.length < format.fields.length) return undefined;
      const coords = format.coords || 0;
      const chr = tokens[format.chr];
      const start = parseInt(tokens[format.start]) - coords;
      const end = format.end !== undefined ? parseInt(tokens[format.end]) : start + 1;
      const feature = {
        chr: chr,
        start: start,
        end: end
      };

      if (format.fields) {
        format.fields.forEach(function (field, index) {
          if (index !== format.chr && index !== format.start && index !== format.end) {
            feature[field] = tokens[index];
          }
        });
      }

      return feature;
    } // function expandFormat(format) {

    /**
     * Decode a gcnv record, a bed style format encoding copy number variation
     *
     * @param tokens
     * @param header
     */
    function decodeGcnv(tokens, header) {
      const columnNames = header.columnNames;

      if (!columnNames) {
        throw Error("Sample names are not defined.   Missing column headers?");
      }

      const sampleCount = columnNames.length - 3;
      const chr = tokens[0];
      const start = parseInt(tokens[1]);
      const end = parseInt(tokens[2]);
      const values = tokens.slice(3).map(Number);

      if (values.length == sampleCount) {
        return {
          chr: chr,
          start: start,
          end: end,
          values: values
        };
      } else {
        // TODO Throw error?
        console.warn(`${chr}:${start}-${end} row contains ${values.length} sample columns instead of the expected ${sampleCount} columns. Skipping...`);
        return undefined;
      }
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    /**
     *  Parser for column style (tab delimited, etc) text file formats (bed, gff, vcf, etc).
     *
     *
     */

    /**
     * Return a parser for the given file format.
     */

    class FeatureParser {
      constructor(config) {
        this.config = config;
        this.header = {};

        if (config.nameField) {
          this.header.nameField = config.nameField;
        }

        this.skipRows = 0; // The number of fixed header rows to skip.  Override for specific types as needed

        if (config.decode) {
          this.decode = config.decode;
          this.delimiter = config.delimiter || "\t";
        } else if (config.format) {
          this.header.format = config.format.toLowerCase();
          this.setDecoder(this.header.format);
        }

        if (!this.delimiter) {
          this.delimiter = "\t";
        }
      }
      /**
       * Parse metadata from the file.   A variety of conventions are in use to supply metadata about file contents
       * through header lines (e.g. 'track') and # directives. This method unifies metadata as properties of a
       * 'header' object.
       *
       * @param data
       * @returns {{}}
       */


      async parseHeader(dataWrapper) {
        let header = this.header;
        let columnNames;
        let line;

        while ((line = await dataWrapper.nextLine()) !== undefined) {
          if (line.startsWith("track") || line.startsWith("#track")) {
            let h = parseTrackLine$1(line);
            Object.assign(header, h);
          } else if (line.startsWith("browser")) ; else if (line.startsWith("#columns")) {
            let h = parseColumnsDirective$1(line);
            Object.assign(header, h);
          } else if (line.startsWith("##gff-version 3")) {
            header.format = "gff3";
          } else if (line.startsWith("#gffTags")) {
            header.gffTags = true;
          } else if (line.startsWith("fixedStep") || line.startsWith("variableStep")) {
            // Wig directives -- we are in the data section
            break;
          } else if (line.startsWith("#")) {
            const tokens = line.split(this.delimiter || "\t");

            if (tokens.length > 1) {
              columnNames = tokens; // Possible column names
            }
          } else {
            // All directives that could change the format, and thus decoder, should have been read by now.
            this.setDecoder(header.format); // If the line can be parsed as a feature assume we are beyond the header, if any

            const tokens = line.split(this.delimiter || "\t");

            try {
              const tmpHeader = Object.assign({
                columnNames
              }, header);

              if (this.decode(tokens, tmpHeader)) {
                break;
              } else {
                if (tokens.length > 1) {
                  columnNames = tokens; // possible column names
                }
              }
            } catch (e) {
              // Not a feature
              if (tokens.length > 1) {
                columnNames = tokens; // possible column names
              }
            }
          }
        }

        if (columnNames) {
          header.columnNames = columnNames;

          for (let n = 0; n < columnNames.length; n++) {
            if (columnNames[n] === "color" || columnNames[n] === "colour") {
              header.colorColumn = n;
            } else if (columnNames[n] === "thickness") {
              header.thicknessColumn = n;
            }
          }
        }

        this.header = header; // Directives might be needed for parsing lines

        return header;
      }

      async parseFeatures(dataWrapper) {
        const allFeatures = [];
        const decode = this.decode;
        const format = this.header.format;
        const delimiter = this.delimiter || "\t";
        let i = 0;
        let errorCount = 0;
        let line;

        while ((line = await dataWrapper.nextLine()) !== undefined) {
          i++;
          if (i <= this.skipRows) continue;

          if (!line || line.startsWith("track") || line.startsWith("#") || line.startsWith("browser")) {
            continue;
          } else if (format === "wig" && line.startsWith("fixedStep")) {
            this.header.wig = parseFixedStep(line);
            continue;
          } else if (format === "wig" && line.startsWith("variableStep")) {
            this.header.wig = parseVariableStep(line);
            continue;
          }

          const tokens = line.split(delimiter);

          if (tokens.length < 1) {
            continue;
          }

          const feature = decode(tokens, this.header);

          if (feature instanceof DecodeError) {
            errorCount++;

            if (errorCount > 0) {
              console.error(`Error parsing line '${line}': ${feature.message}`);
            }

            continue;
          }

          if (feature) {
            allFeatures.push(feature);
          }
        } // Special hack for bedPE


        if (decode === decodeBedpe) {
          fixBedPE(allFeatures);
        }

        return allFeatures;
      }

      setDecoder(format) {
        switch (format) {
          case "narrowpeak":
          case "broadpeak":
          case "regionpeak":
          case "peaks":
            this.decode = decodePeak;
            this.delimiter = this.config.delimiter || /\s+/;
            break;

          case "bedgraph":
            this.decode = decodeBedGraph;
            this.delimiter = /\s+/;
            break;

          case "wig":
            this.decode = decodeWig;
            this.delimiter = this.config.delimiter || /\s+/;
            break;

          case "gff3":
          case "gff":
            this.decode = decodeGFF3;
            this.delimiter = "\t";
            break;

          case "gtf":
            this.decode = decodeGTF;
            this.delimiter = "\t";
            break;

          case "fusionjuncspan":
            // bhaas, needed for FusionInspector view
            this.decode = decodeFusionJuncSpan;
            this.delimiter = this.config.delimiter || /\s+/;
            break;

          case "gtexgwas":
            this.skipRows = 1;
            this.decode = decodeGtexGWAS;
            this.delimiter = "\t";
            break;

          case "refflat":
            this.decode = decodeReflat;
            this.delimiter = this.config.delimiter || /\s+/;
            break;

          case "genepred":
            this.decode = decodeGenePred;
            this.delimiter = this.config.delimiter || /\s+/;
            break;

          case "genepredext":
            this.decode = decodeGenePredExt;
            this.delimiter = this.config.delimiter || /\s+/;
            break;

          case "ensgene":
            this.decode = decodeGenePred;
            this.header.shift = 1;
            this.delimiter = this.config.delimiter || /\s+/;
            break;

          case "refgene":
            this.decode = decodeGenePredExt;
            this.delimiter = this.config.delimiter || /\s+/;
            this.header.shift = 1;
            break;

          case "bed":
            this.decode = decodeBed;
            this.delimiter = this.config.delimiter || /\s+/;
            break;

          case "bedpe":
            this.decode = decodeBedpe;
            this.delimiter = this.config.delimiter || "\t";
            break;

          case "bedpe-domain":
            this.decode = decodeBedpeDomain;
            this.headerLine = true;
            this.delimiter = this.config.delimiter || "\t";
            break;

          case "bedpe-loop":
            this.decode = decodeBedpe;
            this.delimiter = this.config.delimiter || "\t";
            this.header = {
              colorColumn: 7
            };
            break;

          case "interact":
            this.decode = decodeInteract;
            this.delimiter = this.config.delimiter || /\s+/;
            break;

          case "snp":
            this.decode = decodeSNP;
            this.delimiter = "\t";
            break;

          case "rmsk":
            this.decode = decodeRepeatMasker;
            this.delimiter = "\t";
            break;

          case "gcnv":
            this.decode = decodeGcnv;
            this.delimiter = "\t";
            break;

          default:
            const customFormat = getFormat(format);

            if (customFormat !== undefined) {
              this.decode = decodeCustom;
              this.header.customFormat = customFormat;
              this.delimiter = customFormat.delimiter || "\t";
            } else {
              this.decode = decodeBed;
              this.delimiter = this.config.delimiter || /\s+/;
            }

        }
      }

    }

    function parseTrackLine$1(line) {
      const properties = {};
      const tokens = line.split(/(?:")([^"]+)(?:")|([^\s"]+)(?=\s+|$)/g); // Clean up tokens array

      let curr;
      const tmp = [];

      for (let tk of tokens) {
        if (!tk || tk.trim().length === 0) continue;

        if (tk.endsWith("=")) {
          curr = tk;
        } else if (curr) {
          tmp.push(curr + tk);
          curr = undefined;
        } else {
          tmp.push(tk);
        }
      }

      for (let str of tmp) {
        if (!str) return;
        var kv = str.split('=', 2);

        if (kv.length === 2) {
          const key = kv[0].trim();
          const value = kv[1].trim();

          if (properties.hasOwnProperty(key)) {
            let currentValue = properties[key];

            if (Array.isArray(currentValue)) {
              currentValue.push(value);
            } else {
              properties[key] = [currentValue, value];
            }
          } else {
            properties[key] = value;
          }
        }
      }

      if ("interact" == properties["type"]) {
        properties["format"] = "interact";
      } else if ("gcnv" === properties["type"]) {
        properties["format"] = "gcnv";
      }

      return properties;
    }

    function parseColumnsDirective$1(line) {
      let properties = {};
      let t1 = line.split(/\s+/);

      if (t1.length === 2) {
        let t2 = t1[1].split(";");
        t2.forEach(function (keyValue) {
          let t = keyValue.split("=");

          if (t[0] === "color") {
            properties.colorColumn = Number.parseInt(t[1]) - 1;
          } else if (t[0] === "thickness") {
            properties.thicknessColumn = Number.parseInt(t[1]) - 1;
          }
        });
      }

      return properties;
    }

    function parseFixedStep(line) {
      const tokens = line.split(/\s+/);
      const chrom = tokens[1].split("=")[1];
      const start = parseInt(tokens[2].split("=")[1], 10) - 1;
      const step = parseInt(tokens[3].split("=")[1], 10);
      const span = tokens.length > 4 ? parseInt(tokens[4].split("=")[1], 10) : 1;
      return {
        format: "fixedStep",
        chrom,
        start,
        step,
        span,
        index: 0
      };
    }

    function parseVariableStep(line) {
      const tokens = line.split(/\s+/);
      const chrom = tokens[1].split("=")[1];
      const span = tokens.length > 2 ? parseInt(tokens[2].split("=")[1], 10) : 1;
      return {
        format: "variableStep",
        chrom,
        span
      };
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    /**
     *  Define parser for seg files  (.bed, .gff, .vcf, etc).  A parser should implement 2 methods
     *
     *     parseHeader(data) - return an object representing a header.  Details are format specific
     *
     *     parseFeatures(data) - return a list of features
     *
     */

    class SegParser {
      constructor(type) {
        this.type = type || 'seg'; // One of seg, mut, or maf

        switch (this.type) {
          case 'mut':
            this.sampleColumn = 3;
            this.chrColumn = 0;
            this.startColumn = 1;
            this.endColumn = 2;
            this.dataColumn = 4;
            break;

          case 'maf':
            this.sampleColumn = 15;
            this.chrColumn = 4;
            this.startColumn = 5;
            this.endColumn = 6;
            this.dataColumn = 8;
            break;

          default:
            this.sampleColumn = 0;
            this.chrColumn = 1;
            this.startColumn = 2;
            this.endColumn = 3;
          // Data column determined after reading header
        }
      }

      async parseHeader(dataWrapper) {
        let line;

        while ((line = await dataWrapper.nextLine()) !== undefined) {
          if (line.startsWith("#")) ; else {
            const tokens = line.split("\t");
            this.header = {
              headings: tokens
            };
            break;
          }
        }

        return this.header;
      }

      async parseFeatures(dataWrapper) {
        const allFeatures = [];
        let extraHeaders;

        if (!this.header) {
          this.header = await this.parseHeader(dataWrapper); // This will only work for non-indexed files
        }

        if ('seg' === this.type) {
          this.dataColumn = this.header.headings.length - 1;
        }

        if (this.header.headings.length > 5) {
          extraHeaders = this.extractExtraColumns(this.header.headings);
        }

        const valueColumnName = this.header.headings[this.dataColumn];
        let line;

        while ((line = await dataWrapper.nextLine()) !== undefined) {
          const tokens = line.split("\t");
          const value = 'seg' === this.type ? Number(tokens[this.dataColumn]) : tokens[this.dataColumn];

          if (tokens.length > this.dataColumn) {
            const feature = new SegFeature({
              sample: tokens[this.sampleColumn],
              chr: tokens[this.chrColumn],
              start: parseInt(tokens[this.startColumn]) - 1,
              end: parseInt(tokens[this.endColumn]),
              value,
              valueColumnName
            });

            if (extraHeaders) {
              const extraValues = this.extractExtraColumns(tokens);
              feature.setAttributes({
                names: extraHeaders,
                values: extraValues
              });
            }

            allFeatures.push(feature);
          }
        }

        return allFeatures;
      }

      extractExtraColumns(tokens) {
        const extras = [];

        for (let i = 0; i < tokens.length; i++) {
          if (i !== this.chrColumn && i !== this.startColumn && i !== this.endColumn && i !== this.sampleColumn) {
            extras.push(tokens[i]);
          }
        }

        return extras;
      }

    }

    class SegFeature {
      constructor(_ref) {
        let {
          sample,
          chr,
          start,
          end,
          value,
          valueColumnName
        } = _ref;
        this.sample = sample;
        this.chr = chr;
        this.start = start;
        this.end = end;
        this.value = value;
        this.valueColumnName = valueColumnName;
      }

      setAttributes(_ref2) {
        let {
          names,
          values
        } = _ref2;
        this.attributeNames = names;
        this.attributeValues = values;
      }

      getAttribute(name) {
        if (this.attributeNames) {
          const idx = this.attributeNames.indexOf(name);

          if (idx >= 0) {
            return this.attributeValues[idx];
          }
        }

        return undefined;
      }

      popupData(type, genomeID) {
        const filteredProperties = new Set(['chr', 'start', 'end', 'sample', 'value', 'row', 'color', 'sampleKey', 'uniqueSampleKey', 'sampleId', 'chromosome', 'uniquePatientKey']);
        const locationString = this.chr + ":" + numberFormatter$1(this.start + 1) + "-" + numberFormatter$1(this.end);
        const pd = [{
          name: "Sample",
          value: this.sample
        }, {
          name: "Location",
          value: locationString
        }, {
          name: this.valueColumnName ? capitalize(this.valueColumnName) : "Value",
          value: this.value
        }]; // TODO -- the Cravat stuff should probably be in the track (SegTrack)

        if ("mut" === type && "hg38" === genomeID) {
          const l = this.extractCravatLink(genomeID);

          if (l) {
            pd.push('<hr/>');
            pd.push({
              html: l
            });
            pd.push('<hr/>');
          }
        }

        if (this.attributeNames && this.attributeNames.length > 0) {
          for (let i = 0; i < this.attributeNames.length; i++) {
            if (!filteredProperties.has(this.attributeNames[i]) & this.valueColumnName !== this.attributeNames[i]) {
              pd.push({
                name: capitalize(this.attributeNames[i]),
                value: this.attributeValues[i]
              });
            }
          }
        }

        return pd;
      }

      extractCravatLink(genomeId) {
        let ref, alt;

        if (this.attributeNames && this.attributeNames.length > 0) {
          for (let i = 0; i < this.attributeNames.length; i++) {
            if (!ref && "Reference_Allele" === this.attributeNames[i]) {
              ref = this.attributeValues[i];
            }

            if (!alt && this.attributeNames[i].startsWith("Tumor_Seq_Allele") && this.attributeValues[i] !== ref) {
              alt = this.attributeValues[i];
            }

            if (ref && alt) {
              return TrackBase.getCravatLink(this.chr, this.start + 1, ref, alt, genomeId);
            }
          }
        }
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    /**
     * Create a variant from an array of tokens representing a line in a "VCF" file
     * @param tokens
     */

    function createVCFVariant(tokens) {
      const variant = new Variant();
      variant.chr = tokens[0]; // TODO -- use genome aliases

      variant.pos = parseInt(tokens[1]);
      variant.names = tokens[2]; // id in VCF

      variant.referenceBases = tokens[3];
      variant.alternateBases = tokens[4];
      variant.quality = tokens[5];
      variant.filter = tokens[6];
      variant.info = {};
      const infoStr = tokens[7];

      if (infoStr) {
        for (let elem of infoStr.split(';')) {
          var element = elem.split('=');
          variant.info[element[0]] = element[1];
        }
      }

      variant.init();
      return variant;
    }
    /**
     * @deprecated - the GA4GH API has been deprecated.  This code no longer maintained.
     * @param json
     * @returns {Variant}
     */


    function createGAVariant(json) {
      var variant = new Variant();
      variant.chr = json.referenceName;
      variant.start = parseInt(json.start); // Might get overriden below

      variant.end = parseInt(json.end); // Might get overriden below

      variant.pos = variant.start + 1; // GA4GH is 0 based.

      variant.names = arrayToString(json.names, "; ");
      variant.referenceBases = json.referenceBases;
      variant.alternateBases = arrayToString(json.alternateBases);
      variant.quality = json.quality;
      variant.filter = arrayToString(json.filter); // Flatten GA4GH attributes array

      variant.info = {};

      if (json.info) {
        Object.keys(json.info).forEach(function (key) {
          var value,
              valueArray = json.info[key];

          if (Array.isArray(valueArray)) {
            value = valueArray.join(",");
          } else {
            value = valueArray;
          }

          variant.info[key] = value;
        });
      } // Need to build a hash of calls for fast lookup
      // Note from the GA4GH spec on call ID:
      //
      // The ID of the call set this variant call belongs to. If this field is not present,
      // the ordering of the call sets from a SearchCallSetsRequest over this GAVariantSet
      // is guaranteed to match the ordering of the calls on this GAVariant.
      // The number of results will also be the same.


      variant.calls = {};
      var id;

      if (json.calls) {
        json.calls.forEach(function (call) {
          id = call.callSetId;
          variant.calls[id] = call;
        });
      }

      init(variant);
      return variant;
    }

    class Variant {
      init() {
        const ref = this.referenceBases;
        const altBases = this.alternateBases;

        if (this.info) {
          if (this.info["VT"]) {
            this.type = this.info["VT"];
          } else if (this.info["SVTYPE"]) {
            this.type = "SV";
          } else if (this.info["PERIOD"]) {
            this.type = "STR";
          }
        }

        if (this.type === undefined) {
          this.type = determineType(ref, altBases);
        }

        if (this.type === "NONVARIANT") {
          this.heterozygosity = 0;
        } // Determine start/end coordinates -- these are the coordinates representing the actual variant,
        // not the leading or trailing reference


        if (this.info["END"]) {
          this.start = this.pos - 1;

          if (this.info["CHR2"] && this.info["CHR2"] !== this.chr) {
            this.end = this.start + 1;
          } else {
            this.end = Number.parseInt(this.info["END"]);
          }
        } else {
          if (this.type === "NONVARIANT") {
            this.start = this.pos - 1; // convert to 0-based coordinate convention

            this.end = this.start + ref.length;
          } else {
            const altTokens = altBases.split(",").filter(token => token.length > 0);
            this.alleles = [];
            this.start = undefined;
            this.end = undefined;

            for (let alt of altTokens) {
              this.alleles.push(alt); // We don't yet handle  SV and other special alt representations

              if ("SV" !== this.type && isKnownAlt(alt)) {
                let altLength = alt.length;
                let lengthOnRef = ref.length;
                const lmin = Math.min(altLength, lengthOnRef); // Trim off matching bases.  Try first match, then right -> left,  then any remaining left -> right

                let s = 0;

                while (s < lmin && ref.charCodeAt(s) === alt.charCodeAt(s)) {
                  s++;
                  altLength--;
                  lengthOnRef--;
                } // right -> left from end


                while (altLength > 0 && lengthOnRef > 0) {
                  const altIdx = s + altLength - 1;
                  const refIdx = s + lengthOnRef - 1;

                  if (alt.charCodeAt(altIdx) === ref.charCodeAt(refIdx)) {
                    altLength--;
                    lengthOnRef--;
                  } else {
                    break;
                  }
                } // if any remaining, left -> right


                while (altLength > 0 && lengthOnRef > 0) {
                  const altIdx = s;
                  const refIdx = s;

                  if (alt.charCodeAt(altIdx) === ref.charCodeAt(refIdx)) {
                    s++;
                    altLength--;
                    lengthOnRef--;
                  } else {
                    break;
                  }
                }

                const alleleStart = this.pos + s - 1; // -1 for zero based coordinates

                const alleleEnd = alleleStart + lengthOnRef;
                this.start = this.start === undefined ? alleleStart : Math.min(this.start, alleleStart);
                this.end = this.end === undefined ? alleleEnd : Math.max(this.end, alleleEnd);
              }
            } // Default to single base representation @ position for variant types not otherwise handled


            if (this.start === undefined) {
              this.start = this.pos - 1;
              this.end = this.pos;
            }
          }
        }
      }

      popupData(genomicLocation, genomeId) {
        const posString = `${numberFormatter$1(this.pos)}`;
        const locString = this.start === this.end ? `${numberFormatter$1(this.start)} | ${numberFormatter$1(this.start + 1)}` : `${numberFormatter$1(this.start + 1)}-${numberFormatter$1(this.end)}`;
        const fields = [{
          name: "Chr",
          value: this.chr
        }, {
          name: "Pos",
          value: posString
        }, {
          name: "Loc",
          value: locString
        }, {
          name: "Names",
          value: this.names ? this.names : ""
        }, {
          name: "Ref",
          value: this.referenceBases
        }, {
          name: "Alt",
          value: this.alternateBases.replace("<", "&lt;")
        }, {
          name: "Qual",
          value: this.quality
        }, {
          name: "Filter",
          value: this.filter
        }];

        if ("SNP" === this.type) {
          let ref = this.referenceBases;

          if (ref.length === 1) {
            let altArray = this.alternateBases.split(",");

            for (let alt of altArray) {
              if (alt.length === 1) {
                let l = TrackBase.getCravatLink(this.chr, this.pos, ref, alt, genomeId);

                if (l) {
                  fields.push('<hr/>');
                  fields.push({
                    html: l
                  });
                }
              }
            }
          }
        }

        if (this.hasOwnProperty("heterozygosity")) {
          fields.push({
            name: "Heterozygosity",
            value: this.heterozygosity
          });
        }

        if (this.info) {
          fields.push({
            html: '<hr style="border-top: dotted 1px;border-color: #c9c3ba" />'
          });

          for (let key of Object.keys(this.info)) {
            fields.push({
              name: key,
              value: arrayToString(decodeURIComponent(this.info[key]))
            });
          }
        }

        return fields;
      }

      isRefBlock() {
        return "NONVARIANT" === this.type;
      }

    }

    const knownAltBases = new Set(["A", "C", "T", "G"].map(c => c.charCodeAt(0)));

    function isKnownAlt(alt) {
      for (let i = 0; i < alt.length; i++) {
        if (!knownAltBases.has(alt.charCodeAt(i))) {
          return false;
        }
      }

      return true;
    }

    function determineType(ref, altAlleles) {
      const refLength = ref.length;

      if (altAlleles === undefined) {
        return "UNKNOWN";
      } else if (altAlleles.trim().length === 0 || altAlleles === "<NON_REF>" || altAlleles === "<*>" || altAlleles === ".") {
        return "NONVARIANT";
      } else {
        const alleles = altAlleles.split(",");
        const types = alleles.map(function (a) {
          if (refLength === 1 && a.length === 1) {
            return "SNP";
          } else {
            return "<NON_REF>" === a ? "NONVARIANT" : "OTHER";
          }
        });
        let type = types[0];

        for (let t of types) {
          if (t !== type) {
            return "MIXED";
          }
        }

        return type;
      }
    }

    function arrayToString(value, delim) {
      if (delim === undefined) delim = ",";

      if (!Array.isArray(value)) {
        return value;
      }

      return value.join(delim);
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    /**
     * Parser for VCF files.
     */

    class VcfParser {
      construtor() {}

      async parseHeader(dataWrapper, genome) {
        const header = {};
        header.chrAliasTable = new Map(); // First line must be file format

        let line = await dataWrapper.nextLine();

        if (line.startsWith("##fileformat")) {
          header.version = line.substr(13);
        } else {
          throw new Error("Invalid VCF file: missing fileformat line");
        }

        while ((line = await dataWrapper.nextLine()) !== undefined) {
          if (line.startsWith("#")) {
            let id;
            const values = {};

            if (line.startsWith("##")) {
              if (line.startsWith("##INFO") || line.startsWith("##FILTER") || line.startsWith("##FORMAT")) {
                const ltIdx = line.indexOf("<");
                const gtIdx = line.lastIndexOf(">");

                if (!(ltIdx > 2 && gtIdx > 0)) {
                  console.log("Malformed VCF header line: " + line);
                  continue;
                }

                const type = line.substring(2, ltIdx - 1);
                if (!header[type]) header[type] = {}; //##INFO=<ID=AF,Number=A,Type=Float,Description="Allele frequency based on Flow Evaluator observation counts">
                // ##FILTER=<ID=NOCALL,Description="Generic filter. Filtering details stored in FR info tag.">
                // ##FORMAT=<ID=AF,Number=A,Type=Float,Description="Allele frequency based on Flow Evaluator observation counts">

                const tokens = splitStringRespectingQuotes(line.substring(ltIdx + 1, gtIdx - 1), ",");

                for (let token of tokens) {
                  var kv = token.split("=");

                  if (kv.length > 1) {
                    if (kv[0] === "ID") {
                      id = kv[1];
                    } else {
                      values[kv[0]] = kv[1];
                    }
                  }
                }

                if (id) {
                  header[type][id] = values;
                }
              } else if (line.startsWith("##contig") && genome) {
                const idx1 = line.indexOf("<ID=");
                let idx2 = line.indexOf(",", idx1);

                if (idx2 == -1) {
                  idx2 = line.indexOf(">", idx1);
                }

                const chr = line.substring(idx1 + 4, idx2);
                const canonicalChromosome = genome.getChromosomeName(chr);
                header.chrAliasTable.set(canonicalChromosome, chr);
              } else ;
            } else if (line.startsWith("#CHROM")) {
              const tokens = line.split("\t");

              if (tokens.length > 8) {
                // call set names -- use column index for id
                header.callSets = [];

                for (let j = 9; j < tokens.length; j++) {
                  header.callSets.push({
                    id: j,
                    name: tokens[j]
                  });
                }
              }
            }
          } else {
            break;
          }
        }

        this.header = header; // Will need to intrepret genotypes and info field

        return header;
      }
      /**
       * Parse data as a collection of Variant objects.
       *
       * @param data
       * @returns {Array}
       */


      async parseFeatures(dataWrapper) {
        const allFeatures = [];
        const callSets = this.header.callSets;
        const nExpectedColumns = 8 + (callSets ? callSets.length + 1 : 0);
        let line;

        while ((line = await dataWrapper.nextLine()) !== undefined) {
          if (line && !line.startsWith("#")) {
            const tokens = line.split("\t");

            if (tokens.length === nExpectedColumns) {
              const variant = createVCFVariant(tokens);
              variant.header = this.header; // Keep a pointer to the header to interpret fields for popup text
              //variant.line = line              // Uncomment for debugging

              allFeatures.push(variant);

              if (tokens.length > 9) {
                // Format
                const callFields = extractCallFields(tokens[8].split(":"));
                variant.calls = {};

                for (let index = 9; index < tokens.length; index++) {
                  const token = tokens[index];
                  const callSet = callSets[index - 9];
                  const call = {
                    callSetName: callSet.name,
                    info: {}
                  };
                  variant.calls[callSet.id] = call;
                  token.split(":").forEach(function (callToken, idx) {
                    switch (idx) {
                      case callFields.genotypeIndex:
                        call.genotype = [];
                        callToken.split(/[\|\/]/).forEach(function (s) {
                          call.genotype.push('.' === s ? s : parseInt(s));
                        });
                        break;

                      default:
                        call.info[callFields.fields[idx]] = callToken;
                    }
                  });
                }
              }

              if (variant.info && variant.info.CHR2 && variant.info.END) {
                allFeatures.push(svComplement(variant));
              }
            }
          }
        }

        return allFeatures;
      }

    }

    function extractCallFields(tokens) {
      const callFields = {
        genotypeIndex: -1,
        fields: tokens
      };

      for (let i = 0; i < tokens.length; i++) {
        if ("GT" === tokens[i]) {
          callFields.genotypeIndex = i;
        }
      }

      return callFields;
    }

    function svComplement(v) {
      const chr2 = v.info.CHR2;
      const pos2 = Number.parseInt(v.info.END);
      return {
        chr: chr2,
        start: pos2 - 1,
        end: pos2,
        _f: v
      };
    }

    /**
     * Parser for IGV desktop GWAS files.  See http://software.broadinstitute.org/software/igv/GWAS
     */
    class GWASParser {
      constructor(config) {
        // Defaults - can be overriden by header
        this.config = config;

        if (config.columns) {
          if (config.columns.chromosome === undefined || config.columns.position === undefined || config.columns.value === undefined) {
            throw Error("columns property must define chrCol, posCol, and valueCol");
          }

          this.posCol = config.columns.position - 1;
          this.chrCol = config.columns.chromosome - 1;
          this.pvalueCol = config.columns.value - 1;
        } else {
          // Defaults -- can be overriden in header
          this.posCol = 2;
          this.chrCol = 1;
          this.pvalueCol = 3;
        }
      }

      async parseHeader(dataWrapper) {
        const headerLine = await dataWrapper.nextLine();
        return this.parseHeaderLine(headerLine);
      }

      parseHeaderLine(headerLine) {
        this.columns = headerLine.split(/\t/);

        if (!this.config.columns) {
          for (let i = 0; i < this.columns.length; i++) {
            const c = this.columns[i].toLowerCase();

            switch (c) {
              case 'chr':
              case 'chromosome':
              case 'chr_id':
                this.chrCol = i;
                break;

              case 'bp':
              case 'pos':
              case 'position':
              case 'chr_pos':
                this.posCol = i;
                break;

              case 'p':
              case 'pval':
              case 'pvalue':
              case 'p-value':
              case 'p.value':
                this.valueCol = i;
                break;
            }
          }
        }

        return this.columns;
      }

      async parseFeatures(dataWrapper) {
        const allFeatures = [];
        const headerLine = dataWrapper.nextLine();

        if (!this.columns) {
          this.parseHeaderLine(headerLine);
        }

        let line;

        while ((line = dataWrapper.nextLine()) !== undefined) {
          const tokens = line.split(/\t/);

          if (tokens.length === this.columns.length) {
            const chr = tokens[this.chrCol];
            const start = parseInt(tokens[this.posCol]) - 1;
            const end = start + 1;
            const value = Number(tokens[this.valueCol]);
            allFeatures.push(new GWASFeature({
              chr: chr,
              start: start,
              end: end,
              value: value,
              line: line,
              columns: this.columns
            }));
          }
        }

        return allFeatures;
      }

    }

    class GWASFeature {
      constructor(_ref) {
        let {
          chr,
          start,
          end,
          value,
          line,
          columns
        } = _ref;
        this.chr = chr;
        this.start = start;
        this.end = end;
        this.value = value;
        this.line = line;
        this.columns = columns;
      }

      popupData() {
        const tokens = this.line.split(/\t/);
        return this.columns.map(function (c, index) {
          return {
            name: c,
            value: tokens[index]
          };
        });
      }

      getAttribute(attrName) {
        const tokens = this.line.split(/\t/);

        for (let i = 0; i < this.columns.length; i++) {
          if (this.columns[i] === attrName) {
            return tokens[i];
          }
        }

        return undefined;
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    /**
     *  Define parsers for bed-like files  (.bed, .gff, .vcf, etc).  A parser should implement 2 methods
     *
     *     parseHeader(data) - return an object representing a header or metadata.  Details are format specific
     *
     *     parseFeatures(data) - return an array of features
     *
     */

    var aedRegexpNoNamespace = new RegExp("([^:]*)\\(([^)]*)\\)"); // name(type) for AED parsing (namespace undefined)

    var aedRegexpNamespace = new RegExp("([^:]*):([^(]*)\\(([^)]*)\\)"); // namespace:name(type) for AED parsing

    class AEDParser {
      constructor(config) {
        const decode = config ? config.decode : undefined;
        this.nameField = config ? config.nameField : undefined;
        this.skipRows = 0; // The number of fixed header rows to skip.  Override for specific types as needed

        if (decode) {
          this.decode = decode;
        } else {
          this.decode = decodeAed;
        }

        this.delimiter = "\t";
      }

      async parseHeader(dataWrapper) {
        let line;
        let header;

        while (line = await dataWrapper.nextLine()) {
          if (line.startsWith("track") || line.startsWith("#") || line.startsWith("browser")) {
            if (line.startsWith("track") || line.startsWith("#track")) {
              let h = parseTrackLine(line);

              if (header) {
                Object.assign(header, h);
              } else {
                header = h;
              }
            } else if (line.startsWith("#columns")) {
              let h = parseColumnsDirective(line);

              if (header) {
                Object.assign(header, h);
              } else {
                header = h;
              }
            } else if (line.startsWith("##gff-version 3")) {
              this.format = "gff3";
              if (!header) header = {};
              header["format"] = "gff3";
            }
          } else {
            break;
          }
        }

        this.header = header; // Directives might be needed for parsing lines

        return header;
      }

      async parseFeatures(dataWrapper) {
        const allFeatures = [];
        const decode = this.decode;
        const delimiter = this.delimiter || "\t";
        let i = 0;
        let line;
        let wig;

        while ((line = dataWrapper.nextLine()) !== undefined) {
          i++;

          if (i <= this.skipRows || line.startsWith("track") || line.startsWith("#") || line.startsWith("browser")) {
            continue;
          }

          let tokens = readTokensAed();

          if (tokens.length < 1) {
            continue;
          }

          if (!this.aed) {
            // Store information about the aed header in the parser itself
            // This is done only once - on the first row
            this.aed = parseAedHeaderRow(tokens);
            continue;
          }

          const feature = decode.call(this, tokens, wig);

          if (feature) {
            allFeatures.push(feature);
          }
        }

        return allFeatures; // Double quoted strings can contain newlines in AED
        // "" is an escape for a ".
        // Parse all this, clean it up, split into tokens in a custom way

        function readTokensAed() {
          var tokens = [],
              token = "",
              quotedString = false,
              n,
              c;

          while (line || line === '') {
            for (n = 0; n < line.length; n++) {
              c = line.charAt(n);

              if (c === delimiter) {
                if (!quotedString) {
                  tokens.push(token);
                  token = "";
                } else {
                  token += c;
                }
              } else if (c === "\"") {
                // Look ahead to the next character
                if (n + 1 < line.length && line.charAt(n + 1) === "\"") {
                  if (quotedString) {
                    // Turn "" into a single " in the output string
                    token += "\"";
                  } // Skip the next double quote


                  n++;
                } else {
                  // We know the next character is NOT a double quote, flip our state
                  quotedString = !quotedString;
                }
              } else {
                token += c;
              }
            } // We are at the end of the line


            if (quotedString) {
              token += '\n'; // Add newline to the token

              line = nextLine(); // Keep going
            } else {
              // We can end the loop
              break;
            }
          } // Push the last token


          tokens.push(token);
          return tokens;
        }
      }

    }

    function parseAedToken(value) {
      // Example: refseq:accessionNumber(aed:String)
      // refseq - namespace, will be declared later
      // accessionNumber - name of the field
      // aed:String - type of the field
      // The namespace part may be missing
      var match = aedRegexpNamespace.exec(value);

      if (match) {
        return {
          namespace: match[1],
          name: match[2],
          type: match[3]
        };
      }

      match = aedRegexpNoNamespace.exec(value);

      if (match) {
        return {
          namespace: '?',
          name: match[1],
          type: match[2]
        };
      } else {
        throw new Error("Error parsing the header row of AED file - column not in ns:name(ns:type) format");
      }
    }

    function parseAedHeaderRow(tokens) {
      // First row of AED file defines column names
      // Each header item is an aed token - see parseAedToken
      var aed, k, token, aedToken; // Initialize aed section to be filled in

      aed = {
        columns: [// Information about the namespace, name and type of each column
          // Example entry:
          // { namespace: 'bio', name: 'start', type: 'aed:Integer' }
        ],
        metadata: {// Metadata about the entire AED file
          // Example:
          // {
          //    aed: {
          //       application: { value: "CHaS Browser 3.3.0.139 (r10838)", type: "aed:String" },
          //       created: { value: "2018-01-02T10:20:30.123+01:00", type: "aed:DateTime" },
          //       modified: { value: "2018-03-04T11:22:33.456+01:00", type: "aed:DateTime" },
          //    }
          //    affx: {
          //       ucscGenomeVersion: { value: "hg19", type: "aed:String" }
          //    },
          //    namespace: {
          //       omim: { value: "http://affymetrix.com/ontology/www.ncbi.nlm.nih.gov/omim/", type: "aed:URI" },
          //       affx: { value: "http://affymetrix.com/ontology/", type: "aed:URI" },
          //       refseq: { value: "http://affymetrix.com/ontology/www.ncbi.nlm.nih.gov/RefSeq/", type: "aed:URI" }
          //    }
          // }
        }
      };

      for (k = 0; k < tokens.length; k++) {
        token = tokens[k];
        aedToken = parseAedToken(token);
        aed.columns.push(aedToken);
      }

      return aed;
    }

    function parseTrackLine(line) {
      const properties = {};
      const tokens = line.split(/(?:")([^"]+)(?:")|([^\s"]+)(?=\s+|$)/g); // Clean up tokens array

      let curr;
      const tmp = [];

      for (let tk of tokens) {
        if (!tk || tk.trim().length === 0) continue;

        if (tk.endsWith("=") > 0) {
          curr = tk;
        } else if (curr) {
          tmp.push(curr + tk);
          curr = undefined;
        } else {
          tmp.push(tk);
        }
      }

      for (let str of tmp) {
        if (!str) return;
        var kv = str.split('=', 2);

        if (kv.length === 2) {
          const key = kv[0].trim();
          const value = kv[1].trim();
          properties[key] = value;
        }
      }

      return properties;
    }

    function parseColumnsDirective(line) {
      let properties = {};
      let t1 = line.split(/\s+/);

      if (t1.length === 2) {
        let t2 = t1[1].split(";");
        t2.forEach(function (keyValue) {
          let t = keyValue.split("=");

          if (t[0] === "color") {
            properties.colorColumn = Number.parseInt(t[1]) - 1;
          } else if (t[0] === "thickness") {
            properties.thicknessColumn = Number.parseInt(t[1]) - 1;
          }
        });
      }

      return properties;
    }
    /**
     * AED file feature.
     *
     * @param aed link to the AED file object containing file-level metadata and column descriptors
     * @param allColumns All columns as parsed from the AED
     *
     * Other values are parsed one by one
     */


    function AedFeature(aed, allColumns) {
      var token,
          aedColumn,
          aedColumns = aed.columns; // Link to AED file (for metadata)

      this.aed = aed; // Unparsed columns from AED file

      this.allColumns = allColumns; // Prepare space for the parsed values

      this.chr = null;
      this.start = null;
      this.end = null;
      this.score = 1000;
      this.strand = '.';
      this.cdStart = null;
      this.cdEnd = null;
      this.name = null;
      this.color = null;

      for (let i = 0; i < allColumns.length; i++) {
        token = allColumns[i];

        if (!token) {
          // Skip empty fields
          continue;
        }

        aedColumn = aedColumns[i];

        if (aedColumn.type === 'aed:Integer') {
          token = parseInt(token);
        }

        var arr = [];

        if (aedColumn.namespace.length > 0) {
          for (let j = 0; j < aedColumn.namespace.length; j++) {
            arr.push(aedColumn.namespace.charCodeAt(j));
          }
        }

        if (aedColumn.namespace.trim() === 'bio') {
          if (aedColumn.name === 'sequence') {
            this.chr = token;
          } else if (aedColumn.name === 'start') {
            this.start = token;
          } else if (aedColumn.name === 'end') {
            this.end = token;
          } else if (aedColumn.name === 'cdsMin') {
            this.cdStart = token;
          } else if (aedColumn.name === 'cdsMax') {
            this.cdEnd = token;
          } else if (aedColumn.name === 'strand') {
            this.strand = token;
          }
        } else if (aedColumn.namespace === 'aed') {
          if (aedColumn.name === 'name') {
            this.name = token;
          }
        } else if (aedColumn.namespace === 'style') {
          if (aedColumn.name === 'color') {
            this.color = IGVColor.createColorString(token);
          }
        }
      }
    }

    AedFeature.prototype.popupData = function () {
      var data = [],
          aed = this.aed; // Just dump everything we have for now

      for (var i = 0; i < this.allColumns.length; i++) {
        var featureValue = this.allColumns[i];
        var name = aed.columns[i].name; // Skip columns that are not interesting - you know the sequence, and you can see color

        if (name !== 'sequence' && name !== 'color') {
          if (featureValue) {
            data.push({
              name: name,
              value: featureValue
            });
          }
        }
      }

      return data;
    };
    /**
     * Decode the AED file format
     * @param tokens
     * @param ignore
     * @returns decoded feature, or null if this is not a valid record
     */


    function decodeAed(tokens, ignore) {
      var name,
          value,
          token,
          nonEmptyTokens = 0,
          aedColumns = this.aed.columns,
          aedColumn,
          aedKey,
          i; // Each aed row must match the exact number of columns or we skip it

      if (tokens.length !== aedColumns.length) {
        console.log('Corrupted AED file row: ' + tokens.join(','));
        return undefined;
      }

      for (i = 0; i < tokens.length; i++) {
        aedColumn = aedColumns[i];
        token = tokens[i];

        if (token !== '') {
          nonEmptyTokens++;
        }

        if (aedColumn.name === 'name' && aedColumn.namespace === 'aed') {
          name = token;
        } else if (aedColumn.name === 'value' && aedColumn.namespace === 'aed') {
          value = token;
        }
      }

      if (nonEmptyTokens === 2 && name && value) {
        // Special row that defines metadata for the entire file
        aedKey = parseAedToken(name); // Store in the metadata section

        if (!this.aed.metadata[aedKey.namespace]) {
          this.aed.metadata[aedKey.namespace] = {};
        }

        if (!this.aed.metadata[aedKey.namespace][aedKey.name]) {
          this.aed.metadata[aedKey.namespace][aedKey.name] = {
            type: aedKey.type,
            value: value
          };
        } // Ignore this value


        return undefined;
      }

      var feature = new AedFeature(this.aed, tokens);

      if (!feature.chr || !feature.start && feature.start !== 0 || !feature.end) {
        console.log('Cannot parse feature: ' + tokens.join(','));
        return undefined;
      }

      return feature;
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    function getDataWrapper(data) {
      if (typeof data == 'string' || data instanceof String) {
        return new StringDataWrapper(data);
      } else {
        return new ByteArrayDataWrapper(data);
      }
    } // Data might be a string, or an UInt8Array


    class StringDataWrapper {
      constructor(string) {
        this.data = string;
        this.ptr = 0;
      }

      nextLine() {
        var start = this.ptr,
            idx = this.data.indexOf('\n', start),
            data = this.data;

        if (idx > 0) {
          this.ptr = idx + 1; // Advance pointer for next line

          if (idx > start && data.charAt(idx - 1) === '\r') {
            // Trim CR manually in CR/LF sequence
            return data.substring(start, idx - 1);
          }

          return data.substring(start, idx);
        } else {
          var length = data.length;
          this.ptr = length; // Return undefined only at the very end of the data

          return start >= length ? undefined : data.substring(start);
        }
      }

    }

    class ByteArrayDataWrapper {
      constructor(array) {
        this.data = array;
        this.length = this.data.length;
        this.ptr = 0;
      }

      nextLine() {
        var c, result;
        result = "";
        if (this.ptr >= this.length) return undefined;

        for (var i = this.ptr; i < this.length; i++) {
          c = String.fromCharCode(this.data[i]);
          if (c === '\r') continue;
          if (c === '\n') break;
          result = result + c;
        }

        this.ptr = i + 1;
        return result;
      }

    }

    class BGZipLineReader {
      constructor(config) {
        this.config = config;
        this.filePtr = 0;
        this.bufferPtr = 0;
        this.buffer;
      }

      async nextLine() {
        let result = undefined;

        try {
          while (true) {
            const length = this.buffer ? this.buffer.length : 0;

            while (this.bufferPtr < length) {
              const c = String.fromCharCode(this.buffer[this.bufferPtr++]);
              if (c === '\r') continue;

              if (c === '\n') {
                return result;
              }

              result = result ? result + c : c;
            }

            if (this.eof) {
              return result;
            } else {
              await this.readNextBlock();
            }
          }
        } catch (e) {
          console.warn(e);
          this.eof = true;
          return result;
        }
      }

      async readNextBlock() {
        const bsizeOptions = buildOptions(this.config, {
          range: {
            start: this.filePtr,
            size: 26
          }
        });
        const abuffer = await igvxhr.loadArrayBuffer(this.config.url, bsizeOptions);
        const bufferSize = bgzBlockSize(abuffer); //console.log(`next block ${this.filePtr}  ${bufferSize}`);

        if (bufferSize === 0) {
          this.eof = true;
          this.buffer = undefined;
        } else {
          const options = buildOptions(this.config, {
            range: {
              start: this.filePtr,
              size: bufferSize
            }
          });
          const data = await igvxhr.loadArrayBuffer(this.config.url, options);

          if (data.byteLength < bufferSize) {
            this.eof = true; // Assumption
          }

          this.buffer = unbgzf(data);
          this.bufferPtr = 0;
          this.filePtr += data.byteLength; //data.byteLength;
        }
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    /**
     * Reader for "bed like" files (tab delimited files with 1 feature per line: bed, gff, vcf, etc)
     *
     * @param config
     * @constructor
     */

    class FeatureFileReader {
      constructor(config, genome) {
        var uriParts;
        this.config = config || {};
        this.genome = genome;
        this.indexURL = config.indexURL;
        this.indexed = config.indexed;

        if (isFile(this.config.url)) {
          this.filename = this.config.url.name;
        } else if (isDataURL(this.config.url)) {
          this.indexed = false; // by definition

          this.dataURI = config.url;
        } else {
          uriParts = parseUri(this.config.url);
          this.filename = config.filename || uriParts.file;
        }

        this.parser = this.getParser(this.config);

        if (this.config.format === "vcf" && !this.config.indexURL) {
          console.warn("Warning: index file not specified.  The entire vcf file will be loaded.");
        }
      }

      async defaultVisibilityWindow() {
        if (this.config.indexURL) {
          const index = await this.getIndex();

          if (index && index.lastBlockPosition) {
            let gl = 0;
            const s = 10000;

            for (let c of index.chromosomeNames) {
              const chromosome = this.genome.getChromosome(c);

              if (chromosome) {
                gl += chromosome.bpLength;
              }
            }

            return Math.round(gl / index.lastBlockPosition * s);
          }
        }
      }
      /**
       * Return a promise to load features for the genomic interval
       * @param chr
       * @param start
       * @param end
       */


      async readFeatures(chr, start, end) {
        const index = await this.getIndex();

        if (index) {
          this.indexed = true;
          return this.loadFeaturesWithIndex(chr, start, end);
        } else if (this.dataURI) {
          this.indexed = false;
          return this.loadFeaturesFromDataURI();
        } else {
          this.indexed = false;
          return this.loadFeaturesNoIndex();
        }
      }

      async readHeader() {
        if (this.dataURI) {
          await this.loadFeaturesFromDataURI(this.dataURI);
          return this.header;
        } else {
          if (this.config.indexURL) {
            const index = await this.getIndex();

            if (!index) {
              // Note - it should be impossible to get here
              throw new Error("Unable to load index: " + this.config.indexURL);
            }

            let dataWrapper;

            if (index.tabix) {
              dataWrapper = new BGZipLineReader(this.config);
            } else {
              // Tribble
              const maxSize = Object.values(index.chrIndex).flatMap(chr => chr.blocks).map(block => block.max).reduce((previous, current) => Math.min(previous, current), Number.MAX_SAFE_INTEGER);
              const options = buildOptions(this.config, {
                bgz: index.tabix,
                range: {
                  start: 0,
                  size: maxSize
                }
              });
              const data = await igvxhr.loadString(this.config.url, options);
              dataWrapper = getDataWrapper(data);
            }

            this.header = await this.parser.parseHeader(dataWrapper); // Cache header, might be needed to parse features

            return this.header;
          } else {
            // If this is a non-indexed file we will load all features in advance
            const options = buildOptions(this.config);
            const data = await igvxhr.loadString(this.config.url, options);
            let dataWrapper = getDataWrapper(data);
            this.header = await this.parser.parseHeader(dataWrapper); // Reset data wrapper and parse features

            dataWrapper = getDataWrapper(data);
            this.features = await this.parser.parseFeatures(dataWrapper); // cache features

            return this.header;
          }
        }
      }

      getParser(config) {
        switch (config.format) {
          case "vcf":
            return new VcfParser(config);

          case "seg":
            return new SegParser("seg");

          case "mut":
            return new SegParser("mut");

          case "maf":
            return new SegParser("maf");

          case "gwas":
            return new GWASParser(config);

          case "aed":
            return new AEDParser(config);

          default:
            return new FeatureParser(config);
        }
      }

      async loadFeaturesNoIndex() {
        if (this.features) {
          // An optimization hack for non-indexed files, features are temporarily cached when header is read.
          const tmp = this.features;
          delete this.features;
          return tmp;
        } else {
          const options = buildOptions(this.config); // Add oauth token, if any

          const data = await igvxhr.loadString(this.config.url, options);

          if (!this.header) {
            const dataWrapper = getDataWrapper(data);
            this.header = await this.parser.parseHeader(dataWrapper);
          }

          const dataWrapper = getDataWrapper(data);
          const features = await this.parser.parseFeatures(dataWrapper); // <= PARSING DONE HERE

          return features;
        }
      }

      async loadFeaturesWithIndex(chr, start, end) {
        //console.log("Using index"
        const config = this.config;
        const parser = this.parser;
        const tabix = this.index.tabix;
        const refId = tabix ? this.index.sequenceIndexMap[chr] : chr;

        if (refId === undefined) {
          return [];
        }

        const genome = this.genome;
        const blocks = this.index.blocksForRange(refId, start, end);

        if (!blocks || blocks.length === 0) {
          return [];
        } else {
          const allFeatures = [];

          for (let block of blocks) {
            const startPos = block.minv.block;
            const startOffset = block.minv.offset;
            const endOffset = block.maxv.offset;
            let endPos;

            if (tabix) {
              let lastBlockSize = 0;

              if (endOffset > 0) {
                const bsizeOptions = buildOptions(config, {
                  range: {
                    start: block.maxv.block,
                    size: 26
                  }
                });
                const abuffer = await igvxhr.loadArrayBuffer(config.url, bsizeOptions);
                lastBlockSize = bgzBlockSize(abuffer);
              }

              endPos = block.maxv.block + lastBlockSize;
            } else {
              endPos = block.maxv.block;
            }

            const options = buildOptions(config, {
              range: {
                start: startPos,
                size: endPos - startPos + 1
              }
            });
            let inflated;

            if (tabix) {
              const data = await igvxhr.loadArrayBuffer(config.url, options);
              inflated = unbgzf(data);
            } else {
              inflated = await igvxhr.loadString(config.url, options);
            }

            const slicedData = startOffset ? inflated.slice(startOffset) : inflated;
            const dataWrapper = getDataWrapper(slicedData);
            let slicedFeatures = await parser.parseFeatures(dataWrapper); // Filter psuedo-features (e.g. created mates for VCF SV records)

            slicedFeatures = slicedFeatures.filter(f => f._f === undefined); // Filter features not in requested range.

            let inInterval = false;

            for (let i = 0; i < slicedFeatures.length; i++) {
              const f = slicedFeatures[i];
              const canonicalChromosome = genome ? genome.getChromosomeName(f.chr) : f.chr;

              if (canonicalChromosome !== chr) {
                if (allFeatures.length === 0) {
                  continue; //adjacent chr to the left
                } else {
                  break; //adjacent chr to the right
                }
              }

              if (f.start > end) {
                allFeatures.push(f); // First feature beyond interval

                break;
              }

              if (f.end >= start && f.start <= end) {
                if (!inInterval) {
                  inInterval = true;

                  if (i > 0) {
                    allFeatures.push(slicedFeatures[i - 1]);
                  }
                }

                allFeatures.push(f);
              }
            }
          }

          allFeatures.sort(function (a, b) {
            return a.start - b.start;
          });
          return allFeatures;
        }
      }

      async getIndex() {
        if (this.index) {
          return this.index;
        } else if (this.config.indexURL) {
          this.index = await this.loadIndex();
          return this.index;
        }
      }
      /**
       * Return a Promise for the async loaded index
       */


      async loadIndex() {
        const indexURL = this.config.indexURL;
        return loadIndex(indexURL, this.config, this.genome);
      }

      async loadFeaturesFromDataURI() {
        if (this.features) {
          // An optimization hack for non-indexed files, features are temporarily cached when header is read.
          const tmp = this.features;
          delete this.features;
          return tmp;
        } else {
          const plain = decodeDataURI$1(this.dataURI);
          let dataWrapper = getDataWrapper(plain);
          this.header = await this.parser.parseHeader(dataWrapper);

          if (this.header instanceof String && this.header.startsWith("##gff-version 3")) {
            this.format = 'gff3';
          }

          dataWrapper = getDataWrapper(plain);
          this.features = await this.parser.parseFeatures(dataWrapper);
          return this.features;
        }
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016 University of California San Diego
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const isString$1 = isString$3;

    class CustomServiceReader {
      constructor(config) {
        this.config = config;
      }

      async readFeatures(chr, start, end) {
        let url;

        if (typeof this.config.url === 'function') {
          url = this.config.url({
            chr,
            start,
            end
          });
        } else {
          url = this.config.url.replace("$CHR", chr).replace("$START", start).replace("$END", end);
        }

        let config = Object.assign({}, this.config);

        if (this.config.body !== undefined) {
          if (typeof this.config.body === 'function') {
            config.body = this.config.body({
              chr,
              start,
              end
            });
          } else {
            config.body = this.config.body.replace("$CHR", chr).replace("$START", start).replace("$END", end);
          }
        }

        let features;
        const data = await igvxhr.load(url, config);

        if (data) {
          if (typeof this.config.parser === "function") {
            features = this.config.parser(data);
          } else if (isString$1(data)) {
            features = JSON.parse(data);
          } else {
            features = data;
          }
        }

        if (this.config.mappings) {
          let mappingKeys = Object.keys(this.config.mappings);

          for (let f of features) {
            for (let key of mappingKeys) {
              f[key] = f[this.config.mappings[key]];
            }
          }
        }

        return features;
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    const UCSCServiceReader = function (config, genome) {
      this.config = config;
      this.genome = genome;
      this.expandQueryInterval = false;
    };

    UCSCServiceReader.prototype.readFeatures = function (chr, start, end) {
      const s = Math.max(0, Math.floor(start));
      let e = Math.ceil(end);

      if (this.genome) {
        const c = this.genome.getChromosome(chr);

        if (c && e > c.bpLength) {
          e = c.bpLength;
        }
      }

      const url = this.config.url + '?db=' + this.config.db + '&table=' + this.config.tableName + '&chr=' + chr + '&start=' + s + '&end=' + e;
      return igvxhr.loadJson(url, this.config).then(function (data) {
        if (data) {
          data.forEach(function (sample) {
            if (sample.hasOwnProperty('exonStarts') && sample.hasOwnProperty('exonEnds') && sample.hasOwnProperty('exonCount') && sample.hasOwnProperty('cdsStart') && sample.hasOwnProperty('cdsEnd')) {
              addExons(sample);
            }
          });
          return data;
        } else {
          return null;
        }
      });
    };

    function addExons(sample) {
      var exonCount, exonStarts, exonEnds, exons, eStart, eEnd;
      exonCount = sample['exonCount'];
      exonStarts = sample['exonStarts'].split(',');
      exonEnds = sample['exonEnds'].split(',');
      exons = [];

      for (var i = 0; i < exonCount; i++) {
        eStart = parseInt(exonStarts[i]);
        eEnd = parseInt(exonEnds[i]);
        var exon = {
          start: eStart,
          end: eEnd
        };
        if (sample.cdsStart > eEnd || sample.cdsEnd < sample.cdsStart) exon.utr = true; // Entire exon is UTR

        if (sample.cdsStart >= eStart && sample.cdsStart <= eEnd) exon.cdStart = sample.cdsStart;
        if (sample.cdsEnd >= eStart && sample.cdsEnd <= eEnd) exon.cdEnd = sample.cdsEnd;
        exons.push(exon);
      }

      sample.exons = exons;
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2021 University of California San Diego
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const gffNameFields = ["Name", "gene_name", "gene", "gene_id", "alias", "locus", "name"];

    class GFFHelper {
      constructor(options) {
        this.format = options.format;
        this.nameField = options.nameField;
        this.filterTypes = options.filterTypes === undefined ? new Set(['chromosome']) : new Set(options.filterTypes);
      }

      combineFeatures(features, genomicInterval) {
        let combinedFeatures;
        const filterTypes = this.filterTypes;
        features = features.filter(f => filterTypes === undefined || !filterTypes.has(f.type));

        if ("gff3" === this.format) {
          const tmp = this.combineFeaturesById(features);
          combinedFeatures = this.combineFeaturesByType(tmp);
        } else {
          combinedFeatures = this.combineFeaturesByType(features);
        }

        combinedFeatures.sort(function (a, b) {
          return a.start - b.start;
        });
        this.numberExons(combinedFeatures, genomicInterval);
        this.nameFeatures(combinedFeatures);
        return combinedFeatures;
      }
      /**
       * Combine multiple non-transcript model features with the same ID on the same chromosome into a single feature.
       * Features that are part of the transcript model (e.g. exon, mRNA, etc) are combined later.
       *
       * @param features
       * @returns {[]}
       */


      combineFeaturesById(features) {
        const chrIdMap = new Map();
        const combinedFeatures = [];

        for (let f of features) {
          if (isTranscriptPart(f.type) || isTranscript(f.type) || !f.id) {
            combinedFeatures.push(f);
          } else {
            let idMap = chrIdMap.get(f.chr);

            if (!idMap) {
              idMap = new Map();
              chrIdMap.set(f.chr, idMap);
            }

            let featureArray = idMap.get(f.id);

            if (featureArray) {
              featureArray.push(f);
            } else {
              idMap.set(f.id, [f]);
            }
          }
        }

        for (let idMap of chrIdMap.values()) {
          for (let featureArray of idMap.values()) {
            if (featureArray.length > 1) {
              // Use the first feature as prototypical (for column 9 attributes), and adjust start/end
              // Parts are represented as "exons", as that is how they are presented visually
              const cf = featureArray[0];
              cf.exons = [];

              for (let f of featureArray) {
                cf.start = Math.min(cf.start, f.start);
                cf.end = Math.max(cf.end, f.end);
                cf.exons.push({
                  start: f.start,
                  end: f.end
                });
              }

              combinedFeatures.push(cf);
            } else {
              combinedFeatures.push(featureArray[0]);
            }
          }
        }

        return combinedFeatures;
      }

      combineFeaturesByType(features) {
        // Build dictionary of genes
        const genes = features.filter(f => "gene" === f.type || f.type.endsWith("_gene"));
        const geneMap = Object.create(null);

        for (let g of genes) {
          geneMap[g.id] = g;
        } // 1. Build dictionary of transcripts


        const transcripts = Object.create(null);
        const combinedFeatures = [];
        const consumedFeatures = new Set();
        const filterTypes = this.filterTypes;
        features = features.filter(f => filterTypes === undefined || !filterTypes.has(f.type));

        for (let f of features) {
          if (f.type === "biological_region") {
            console.log();
          }

          if (isTranscript(f.type)) {
            const transcriptId = f.id; // getAttribute(f.attributeString, "transcript_id", /\s+/);

            if (undefined !== transcriptId) {
              const gffTranscript = new GFFTranscript(f);
              transcripts[transcriptId] = gffTranscript;
              combinedFeatures.push(gffTranscript);
              consumedFeatures.add(f);
              const g = geneMap[f.parent];

              if (g) {
                gffTranscript.geneObject = g;
                consumedFeatures.add(g);
              }
            }
          }
        } // Add exons and transcript parts


        for (let f of features) {
          if (isTranscriptPart(f.type)) {
            const parents = getParents(f);

            if (parents) {
              for (let id of parents) {
                let transcript = transcripts[id];

                if (!transcript && this.format === "gtf") {
                  // GTF does not require explicit transcript record, start one with this feature.
                  const psuedoTranscript = Object.assign({}, f);
                  psuedoTranscript.type = "transcript";
                  transcript = new GFFTranscript(psuedoTranscript);
                  transcripts[id] = transcript;
                  combinedFeatures.push(transcript);
                }

                if (transcript !== undefined) {
                  if (isExon(f.type)) {
                    if (parents.length > 1) {
                      // Make a copy as exon can be modified differently by CDS, etc, for each transcript
                      const e2 = new GFFFeature(f);
                      transcript.addExon(e2);
                    } else {
                      transcript.addExon(f);
                    }
                  } else {
                    transcript.addPart(f);
                  }

                  consumedFeatures.add(f);
                }
              }
            }
          }
        } // Finish transcripts


        combinedFeatures.forEach(function (f) {
          if (typeof f.finish === "function") {
            f.finish();
          }
        }); // Add other features

        const others = features.filter(f => !consumedFeatures.has(f));

        for (let f of others) {
          combinedFeatures.push(f);
        }

        return combinedFeatures;

        function getParents(f) {
          if (f.parent && f.parent.trim() !== "") {
            return f.parent.trim().split(",");
          } else {
            return null;
          }
        }
      }

      numberExons(features, genomicInterval) {
        for (let f of features) {
          if (f.exons && (!genomicInterval || f.end <= genomicInterval.end && f.start > genomicInterval.start)) {
            for (let i = 0; i < f.exons.length; i++) {
              const exon = f.exons[i];
              exon.number = f.strand === "-" ? f.exons.length - i : i + 1;
            }
          }
        }
      }

      nameFeatures(features) {
        // Find name (label) property
        for (let f of features) {
          if (f.attributeString) {
            const delim = 'gff3' === this.format ? '=' : ' ';
            const attributes = parseAttributeString(f.attributeString, delim);
            const attributesMap = new Map(attributes);

            if (this.nameField) {
              f.name = attributesMap.get(this.nameField);
            } else {
              for (let nameField of gffNameFields) {
                if (attributesMap.has(nameField)) {
                  f.name = attributesMap.get(nameField);
                  break;
                }
              }
            }
          }
        }
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2015 UC San Diego
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    const GtexReader = function (config) {
      this.config = config;
      this.url = config.url;
      this.tissueId = config.tissueSiteDetailId;
      this.indexed = true;
      this.datasetId = config.datasetId || "gtex_v8";
    };

    GtexReader.prototype.readFeatures = async function (chr, bpStart, bpEnd) {
      let self = this,
          queryChr = chr.startsWith("chr") ? chr : "chr" + chr,
          queryStart = Math.floor(bpStart),
          queryEnd = Math.ceil(bpEnd),
          datasetId = this.datasetId,
          queryURL = this.url + "?chromosome=" + queryChr + "&start=" + queryStart + "&end=" + queryEnd + "&tissueSiteDetailId=" + this.tissueId + "&datasetId=" + datasetId;
      const json = await igvxhr.loadJson(queryURL, {
        withCredentials: self.config.withCredentials
      });

      if (json && json.singleTissueEqtl) {
        //variants = json.variants;
        //variants.sort(function (a, b) {
        //    return a.POS - b.POS;
        //});
        //source.cache = new FeatureCache(chr, queryStart, queryEnd, variants);
        json.singleTissueEqtl.forEach(function (eqtl) {
          eqtl.chr = eqtl.chromosome;
          eqtl.position = eqtl.pos;
          eqtl.start = eqtl.pos - 1;
          eqtl.end = eqtl.start + 1;
          eqtl.snp = eqtl.snpId;
          eqtl.geneName = eqtl.geneSymbol;
          eqtl.geneId = eqtl.gencodeId;
        });
        return json.singleTissueEqtl;
      } else {
        return undefined;
      }
    };

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    // http://immvar.broadinstitute.org:3000/load_data?chromosome=&start=&end=&categories=

    const ImmVarReader = function (config) {
      this.config = config;
      this.url = config.url;
      this.cellConditionId = config.cellConditionId;
      this.valueThreshold = config.valueThreshold ? config.valueThreshold : 5E-2;
    };

    ImmVarReader.prototype.readFeatures = function (queryChr, queryStart, queryEnd) {
      var self = this,
          queryURL = this.url + "?chromosome=" + queryChr + "&start=" + queryStart + "&end=" + queryEnd + "&cell_condition_id=" + this.cellConditionId;
      return new Promise(function (fulfill, reject) {
        igvxhr.loadJson(queryURL, {
          withCredentials: self.config.withCredentials
        }).then(function (json) {
          if (json) {
            //variants = json.variants;
            //variants.sort(function (a, b) {
            //    return a.POS - b.POS;
            //});
            //source.cache = new FeatureCache(chr, queryStart, queryEnd, variants);
            json.eqtls.forEach(function (eqtl) {
              eqtl.chr = eqtl.chromosome;
              eqtl.start = eqtl.position;
              eqtl.end = eqtl.position + 1;
            });
            fulfill(json.eqtls);
          } else {
            fulfill(null);
          }
        }).catch(function (error) {
          reject(error);
        });
      });
    };

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    const Ga4ghVariantReader = function (config, genome) {
      this.config = config;
      this.genome = genome;
      this.url = config.url;
      this.variantSetId = config.variantSetId;
      this.callSetIds = config.callSetIds;
      this.includeCalls = config.includeCalls === undefined ? true : config.includeCalls;
    }; // Simulate a VCF file header


    Ga4ghVariantReader.prototype.readHeader = function () {
      var self = this;

      if (self.header) {
        return Promise.resolve(self.header);
      } else {
        self.header = {};

        if (self.includeCalls === false) {
          return Promise.resolve(self.header);
        } else {
          var readURL = self.url + "/callsets/search";
          return ga4ghSearch({
            url: readURL,
            fields: "nextPageToken,callSets(id,name)",
            body: {
              "variantSetIds": Array.isArray(self.variantSetId) ? self.variantSetId : [self.variantSetId],
              "pageSize": "10000"
            },
            decode: function (json) {
              // If specific callSetIds are specified filter to those
              if (self.callSetIds) {
                var callSets = [],
                    csIdSet = new Set();
                self.callSetIds.forEach(function (csid) {
                  csIdSet.add(csid);
                });
                json.callSets.forEach(function (cs) {
                  if (csIdSet.has(cs.id)) {
                    callSets.push(cs);
                  }
                });
                return callSets;
              } else {
                return json.callSets;
              }
            }
          }).then(function (callSets) {
            self.header.callSets = callSets;
            return self.header;
          });
        }
      }
    };

    Ga4ghVariantReader.prototype.readFeatures = function (chr, bpStart, bpEnd) {
      const self = this;
      const genome = this.genome;
      return self.readHeader().then(function (header) {
        return getChrAliasTable();
      }).then(function (chrAliasTable) {
        var queryChr = chrAliasTable.hasOwnProperty(chr) ? chrAliasTable[chr] : chr,
            readURL = self.url + "/variants/search";
        return ga4ghSearch({
          url: readURL,
          fields: self.includeCalls ? undefined : "nextPageToken,variants(id,variantSetId,names,referenceName,start,end,referenceBases,alternateBases,quality,filter,info)",
          body: {
            "variantSetIds": Array.isArray(self.variantSetId) ? self.variantSetId : [self.variantSetId],
            "callSetIds": self.callSetIds ? self.callSetIds : undefined,
            "referenceName": queryChr,
            "start": bpStart.toString(),
            "end": bpEnd.toString(),
            "pageSize": "10000"
          },
          decode: function (json) {
            var v;
            var variants = [];
            json.variants.forEach(function (json) {
              v = createGAVariant(json);

              if (!v.isRefBlock()) {
                variants.push(v);
              }
            });
            return variants;
          }
        });
      });

      function getChrAliasTable() {
        return new Promise(function (fulfill, reject) {
          if (self.chrAliasTable) {
            fulfill(self.chrAliasTable);
          } else {
            self.readMetadata().then(function (json) {
              self.metadata = json.metadata;
              self.chrAliasTable = {};

              if (json.referenceBounds && genome) {
                json.referenceBounds.forEach(function (rb) {
                  var refName = rb.referenceName,
                      alias = genome.getChromosomeName(refName);
                  self.chrAliasTable[alias] = refName;
                });
              }

              fulfill(self.chrAliasTable);
            });
          }
        });
      }
    };

    Ga4ghVariantReader.prototype.readMetadata = function () {
      return ga4ghGet({
        url: this.url,
        entity: "variantsets",
        entityId: this.variantSetId
      });
    };

    class CivicReader {
      constructor(config) {
        this.config = config;
      }

      async readFeatures(chr, start, end) {
        const json = await igvxhr.loadJson(this.config.url + "/variants/?count=50000");
        const records = json.records;
        const features = [];

        for (let record of records) {
          if (record.coordinates) {
            record.id;
            const coordinates = record.coordinates;

            if (coordinates.chromosome) {
              features.push(new CivicVariant(coordinates.chromosome, coordinates.start - 1, // UCSC 0 convention
              coordinates.stop, record));
            }

            if (coordinates.chromosome2) {
              features.push(new CivicVariant(coordinates.chromosome2, coordinates.start2 - 1, // UCSC 0 convention
              coordinates.stop2, record));
            }
          }
        }

        return features;
      }

    }

    class CivicVariant {
      constructor(chr, start, end, record) {
        this.chr = chr;
        this.start = start;
        this.end = end;
        this.id = record.id;
        this.entrezName = record.entrez_name;
        this.name = record.name;
        this.actionabilityScore = record.civic_actionability_score;

        if (record.coordinates.reference_bases) {
          this.refBases = record.coordinates.reference_bases;
        }

        if (record.coordinates.variant_bases) {
          this.altBases = record.coordinates.variant_bases;
        }

        if (record.variant_types) {
          this.variant_types = record.variant_types;
        }

        this.locationString = this.chr + ":" + numberFormatter$1(this.start + 1) + "-" + numberFormatter$1(this.end); // Color based on actionability score

        if (this.actionabilityScore !== undefined) {
          let alpha;

          if (this.actionabilityScore <= 10) {
            alpha = 0.2;
          } else {
            const v = Math.min(30, this.actionabilityScore);
            alpha = 0.2 + 0.8 * Math.log10((v - 10) / 2);
          }

          this.alpha = alpha;
        }
      }

      popupData() {
        const link = createLink("CIViC", "https://civicdb.org/links/variants/" + this.id);
        this.refBases !== this.altBases && this.refBases && this.refBases.length === 1 && this.altBases && this.altBases.length === 1;
        const pd = [link];
        pd.push({
          name: "Entrez",
          value: createLink(this.entrezName, "https://ghr.nlm.nih.gov/gene/" + this.entrezName)
        });
        pd.push({
          name: "Name",
          value: this.name
        });

        if (this.variant_types && this.variant_types.length > 0) {
          const name = this.variant_types.length === 1 ? "Type" : "Types";
          let typeString;

          for (let vt of this.variant_types) {
            if (!typeString) typeString = vt.display_name;else typeString += ", " + vt.display_name;
          }

          pd.push({
            name: name,
            value: typeString
          });
        }

        pd.push({
          name: "Actionability",
          value: this.actionabilityScore
        });
        pd.push({
          name: "Location",
          value: this.locationString
        });
        return pd;

        function createLink(text, href) {
          return "<a target='_blank' " + "href='" + href + "'>" + text + "</a>";
        }
      }

    }

    // Assigns a row # to each feature.  If the feature does not fit in any row and #rows == maxRows no
    // row number is assigned.
    function pack(featureList, maxRows) {
      maxRows = maxRows || Number.MAX_SAFE_INTEGER;
      const rows = [];
      featureList.sort(function (a, b) {
        return a.start - b.start;
      });
      rows.push(-1000);

      for (let feature of featureList) {
        let r = 0;
        const len = Math.min(rows.length, maxRows);

        for (r = 0; r < len; r++) {
          if (feature.start >= rows[r]) {
            feature.row = r;
            rows[r] = feature.end;
            break;
          }
        }

        feature.row = r;
        rows[r] = feature.end;
      }
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class HtsgetVariantReader extends HtsgetReader {
      constructor(config, genome) {
        super(config, genome);
        this.parser = new VcfParser();
      }

      async readHeader() {
        if (!this.header) {
          const data = await this.readHeaderData();
          const dataWrapper = getDataWrapper(data);
          this.header = await this.parser.parseHeader(dataWrapper, this.genome);
          this.chrAliasTable = this.header.chrAliasTable;
        }

        return this.header;
      }

      async readFeatures(chr, start, end) {
        if (this.config.format && this.config.format.toUpperCase() !== "VCF") {
          throw Error(`htsget format ${this.config.format} is not supported`);
        }

        if (!this.chrAliasTable) {
          await this.readHeader();
        }

        let queryChr = this.chrAliasTable.has(chr) ? this.chrAliasTable.get(chr) : chr;
        const data = await this.readData(queryChr, start, end);
        const dataWrapper = getDataWrapper(data);
        return this.parser.parseFeatures(dataWrapper); //  return dataWrapper;
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014-2015 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const DEFAULT_MAX_WG_COUNT = 10000;
    /**
     * feature source for "bed like" files (tab or whitespace delimited files with 1 feature per line: bed, gff, vcf, etc)
     *
     * @param config
     * @constructor
     */

    class TextFeatureSource {
      constructor(config, genome) {
        this.config = config || {};
        this.genome = genome;
        this.sourceType = config.sourceType === undefined ? "file" : config.sourceType;
        this.maxWGCount = config.maxWGCount || DEFAULT_MAX_WG_COUNT;
        const queryableFormats = new Set(["bigwig", "bw", "bigbed", "bb", "biginteract", "biggenepred", "bignarrowpeak", "tdf"]);

        if (config.features && Array.isArray(config.features)) {
          // Explicit array of features
          let features = fixFeatures(config.features, genome);
          packFeatures(features);

          if (config.mappings) {
            mapProperties(features, config.mappings);
          }

          this.queryable = false;
          this.featureCache = new FeatureCache$1(features, genome);
        } else if (config.reader) {
          // Explicit reader implementation
          this.reader = config.reader;
          this.queryable = config.queryable !== false;
        } else if (config.sourceType === "ga4gh") {
          this.reader = new Ga4ghVariantReader(config, genome);
          this.queryable = true;
        } else if (config.sourceType === "immvar") {
          this.reader = new ImmVarReader(config);
          this.queryable = true;
        } else if (config.type === "eqtl" && config.sourceType === "gtex-ws") {
          this.reader = new GtexReader(config);
          this.queryable = true;
        } else if ("htsget" === config.sourceType) {
          this.reader = new HtsgetVariantReader(config, genome);
        } else if (config.sourceType === 'ucscservice') {
          this.reader = new UCSCServiceReader(config.source);
          this.queryable = true;
        } else if (config.sourceType === 'custom') {
          this.reader = new CustomServiceReader(config.source);
          this.queryable = false !== config.source.queryable;
        } else if ("civic-ws" === config.sourceType) {
          this.reader = new CivicReader(config);
          this.queryable = false;
        } else {
          // File of some type (i.e. not a webservice)
          this.reader = new FeatureFileReader(config, genome);

          if (config.queryable !== undefined) {
            this.queryable = config.queryable;
          } else if (queryableFormats.has(config.format) || this.reader.indexed) {
            this.queryable = true;
          } else ;
        }
      }

      async defaultVisibilityWindow() {
        if (this.reader && typeof this.reader.defaultVisibilityWindow === 'function') {
          return this.reader.defaultVisibilityWindow();
        }
      }

      async trackType() {
        const header = await this.getHeader();

        if (header) {
          return header.type;
        } else {
          return undefined; // Convention for unknown or unspecified
        }
      }

      async getHeader() {
        if (!this.header) {
          if (this.reader && typeof this.reader.readHeader === "function") {
            const header = await this.reader.readHeader();

            if (header) {
              this.header = header;

              if (header.format) {
                this.config.format = header.format;
              }
            } else {
              this.header = {};
            }
          } else {
            this.header = {};
          }
        }

        return this.header;
      }
      /**
       * Required function for all data source objects.  Fetches features for the
       * range requested.
       *
       * This function is quite complex due to the variety of reader types backing it, some indexed, some queryable,
       * some not.  The whole scheme could use a refactoring.
       *
       * @param chr
       * @param start
       * @param end
       * @param bpPerPixel
       */


      async getFeatures(_ref) {
        let {
          chr,
          start,
          end,
          bpPerPixel,
          visibilityWindow
        } = _ref;
        const genome = this.genome;
        const queryChr = genome ? genome.getChromosomeName(chr) : chr;
        const isWholeGenome = "all" === queryChr.toLowerCase(); // Various conditions that can create a feature load
        // * view is "whole genome" but no features are loaded
        // * cache is disabled
        // * cache does not contain requested range

        if (isWholeGenome && !this.getWGFeatures || this.config.disableCache || !this.featureCache || !this.featureCache.containsRange(new GenomicInterval(queryChr, start, end))) {
          await this.loadFeatures(queryChr, start, end, visibilityWindow);
        }

        if (isWholeGenome) {
          if (!this.wgFeatures) {
            if (this.queryable) {
              // queryable sources don't support whole genome view
              this.wgFeatures = [];
            } else {
              this.wgFeatures = this.getWGFeatures(this.featureCache.getAllFeatures());
            }
          }

          return this.wgFeatures;
        } else {
          return this.featureCache.queryFeatures(queryChr, start, end);
        }
      } // TODO -- experimental, will only work for non-indexed sources


      getAllFeatures() {
        if (this.queryable) {
          // queryable sources don't support all features
          return [];
        } else {
          return this.getWGFeatures(this.featureCache.getAllFeatures());
        }
      }

      async loadFeatures(queryChr, start, end, visibilityWindow) {
        const reader = this.reader;
        let intervalStart = start;
        let intervalEnd = end; // Use visibility window to potentially expand query interval.
        // This can save re-queries as we zoom out.  Visibility window <= 0 is a special case
        // indicating whole chromosome should be read at once.

        if ((!visibilityWindow || visibilityWindow <= 0) && this.config.expandQuery !== false) {
          // Whole chromosome
          const chromosome = this.genome ? this.genome.getChromosome(queryChr) : undefined;
          intervalStart = 0;
          intervalEnd = Math.max(chromosome ? chromosome.bpLength : Number.MAX_SAFE_INTEGER, end);
        } else if (visibilityWindow > end - start && this.config.expandQuery !== false) {
          const expansionWindow = Math.min(4.1 * (end - start), visibilityWindow);
          intervalStart = Math.max(0, (start + end) / 2 - expansionWindow);
          intervalEnd = start + expansionWindow;
        }

        let features = await reader.readFeatures(queryChr, intervalStart, intervalEnd);

        if (this.queryable === undefined) {
          this.queryable = reader.indexed;
        }

        const genomicInterval = this.queryable ? new GenomicInterval(queryChr, intervalStart, intervalEnd) : undefined;

        if (features) {
          if (this.config.assembleGFF !== false && ("gtf" === this.config.format || "gff3" === this.config.format || "gff" === this.config.format)) {
            features = new GFFHelper(this.config).combineFeatures(features, genomicInterval);
          } // Assign overlapping features to rows


          if (this.config.format !== "wig" && this.config.type !== "junctions") {
            const maxRows = this.config.maxRows || Number.MAX_SAFE_INTEGER;
            packFeatures(features, maxRows);
          } // Note - replacing previous cache with new one.  genomicInterval is optional (might be undefined => includes all features)


          this.featureCache = new FeatureCache$1(features, this.genome, genomicInterval); // If track is marked "searchable"< cache features by name -- use this with caution, memory intensive

          if (this.config.searchable || this.config.searchableFields) {
            this.addFeaturesToDB(features);
          }
        } else {
          this.featureCache = new FeatureCache$1([], genomicInterval); // Empty cache
        }
      }

      addFeaturesToDB(featureList) {
        for (let feature of featureList) {
          if (feature.name) {
            this.genome.featureDB[feature.name.toUpperCase()] = feature;
          }

          if (feature.gene && feature.gene.name) {
            this.genome.featureDB[feature.gene.name.toUpperCase()] = feature;
          }

          if (this.config.searchableFields) {
            for (let f of this.config.searchableFields) {
              const value = feature.getAttributeValue(f);

              if (value) {
                if (value.indexOf(" ") > 0) {
                  this.genome.featureDB[value.replaceAll(" ", "+").toUpperCase()] = feature;
                  this.genome.featureDB[value.replaceAll(" ", "%20").toUpperCase()] = feature;
                } else {
                  this.genome.featureDB[value.toUpperCase()] = feature;
                }
              }
            }
          }
        }
      } // TODO -- filter by pixel size


      getWGFeatures(allFeatures) {
        const makeWGFeature = f => {
          const wg = Object.assign({}, f);
          wg.chr = "all";
          wg.start = genome.getGenomeCoordinate(f.chr, f.start);
          wg.end = genome.getGenomeCoordinate(f.chr, f.end);
          wg._f = f; // Don't draw exons in whole genome view

          if (wg["exons"]) delete wg["exons"];
          return wg;
        };

        const genome = this.genome;
        const wgChromosomeNames = new Set(genome.wgChromosomeNames);
        const wgFeatures = [];
        let count = 0;
        const max = this.maxWGCount;

        for (let c of genome.wgChromosomeNames) {
          const features = allFeatures[c];

          if (features) {
            for (let f of features) {
              let queryChr = genome.getChromosomeName(f.chr);

              if (wgChromosomeNames.has(queryChr)) {
                if (wgFeatures.length < max) {
                  wgFeatures.push(makeWGFeature(f));
                } else {
                  //Reservoir sampling
                  const samplingProb = max / (count + 1);

                  if (Math.random() < samplingProb) {
                    const idx = Math.floor(Math.random() * (max - 1));
                    wgFeatures[idx] = makeWGFeature(f);
                  }
                }
              }

              count++;
            }
          }
        }

        wgFeatures.sort(function (a, b) {
          return a.start - b.start;
        });
        return wgFeatures;
      }

    }

    function packFeatures(features, maxRows) {
      maxRows = maxRows || 1000;

      if (features == null || features.length === 0) {
        return;
      } // Segregate by chromosome


      const chrFeatureMap = {};
      const chrs = [];

      for (let feature of features) {
        const chr = feature.chr;
        let flist = chrFeatureMap[chr];

        if (!flist) {
          flist = [];
          chrFeatureMap[chr] = flist;
          chrs.push(chr);
        }

        flist.push(feature);
      } // Loop through chrosomosomes and pack features;


      for (let chr of chrs) {
        pack(chrFeatureMap[chr], maxRows);
      }
    }
    /**
     * This function is used to apply properties normally added during parsing to  features supplied directly in the
     * config as an array of objects.   At the moment the only application is bedpe type features.
     * @param features
     */


    function fixFeatures(features, genome) {
      if (!features || features.length === 0) return [];
      const isBedPE = features[0].chr === undefined && features[0].chr1 !== undefined;

      if (isBedPE) {
        const interChrFeatures = [];

        for (let feature of features) {
          if (genome) {
            feature.chr1 = genome.getChromosomeName(feature.chr1);
            feature.chr2 = genome.getChromosomeName(feature.chr2);
          } // Set total extent of feature


          if (feature.chr1 === feature.chr2) {
            feature.chr = feature.chr1;
            feature.start = Math.min(feature.start1, feature.start2);
            feature.end = Math.max(feature.end1, feature.end2);
          } else {
            interChrFeatures.push(feature);
          }
        } // Make copies of inter-chr features, one for each chromosome


        for (let f1 of interChrFeatures) {
          const f2 = Object.assign({
            dup: true
          }, f1);
          features.push(f2);
          f1.chr = f1.chr1;
          f1.start = f1.start1;
          f1.end = f1.end1;
          f2.chr = f2.chr2;
          f2.start = f2.start2;
          f2.end = f2.end2;
        }
      } else if (genome) {
        for (let feature of features) {
          feature.chr = genome.getChromosomeName(feature.chr);
        }
      }

      return features;
    }

    function mapProperties(features, mappings) {
      let mappingKeys = Object.keys(mappings);
      features.forEach(function (f) {
        mappingKeys.forEach(function (key) {
          f[key] = f[mappings[key]];
        });
      });
    }

    class BufferedReader {
      constructor(config, contentLength, bufferSize) {
        this.path = config.url;
        this.bufferSize = bufferSize ? bufferSize : 512000;
        this.range = {
          start: -1,
          size: -1
        };
        this.config = config;
      }
      /**
       *
       * @param requestedRange - byte rangeas {start, size}
       * @param fulfill - function to receive result
       * @param asUint8 - optional flag to return result as an UInt8Array
       */


      async dataViewForRange(requestedRange, asUint8) {
        const hasData = this.data && this.range.start <= requestedRange.start && this.range.start + this.range.size >= requestedRange.start + requestedRange.size;

        if (!hasData) {
          let bufferSize; // If requested range size is specified, potentially expand buffer size

          if (requestedRange.size) {
            bufferSize = Math.max(this.bufferSize, requestedRange.size);
          } else {
            bufferSize = this.bufferSize;
          }

          const loadRange = {
            start: requestedRange.start,
            size: bufferSize
          };
          const arrayBuffer = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
            range: loadRange
          }));
          this.data = arrayBuffer;
          this.range = loadRange;
        }

        const len = this.data.byteLength;
        const bufferStart = requestedRange.start - this.range.start;
        return asUint8 ? new Uint8Array(this.data, bufferStart, len - bufferStart) : new DataView(this.data, bufferStart, len - bufferStart);
      }

    }

    function getDecoder(definedFieldCount, fieldCount, autoSql, format) {
      if ("biginteract" === format || autoSql && 'chromatinInteract' === autoSql.table || 'interact' === autoSql.table) {
        return decodeInteract;
      } else {
        const standardFieldCount = definedFieldCount - 3;
        return function (feature, tokens) {
          if (standardFieldCount > 0) {
            feature.name = tokens[0];
          }

          if (standardFieldCount > 1) {
            feature.score = Number(tokens[1]);
          }

          if (standardFieldCount > 2) {
            feature.strand = tokens[2];
          }

          if (standardFieldCount > 3) {
            feature.cdStart = parseInt(tokens[3]);
          }

          if (standardFieldCount > 4) {
            feature.cdEnd = parseInt(tokens[4]);
          }

          if (standardFieldCount > 5) {
            if (tokens[5] !== "." && tokens[5] !== "0" && tokens[5] !== "-1") {
              const c = IGVColor.createColorString(tokens[5]);
              feature.color = c.startsWith("rgb") ? c : undefined;
            }
          }

          if (standardFieldCount > 8) {
            const exonCount = parseInt(tokens[6]);
            const exonSizes = tokens[7].split(',');
            const exonStarts = tokens[8].split(',');
            const exons = [];

            for (let i = 0; i < exonCount; i++) {
              const eStart = feature.start + parseInt(exonStarts[i]);
              const eEnd = eStart + parseInt(exonSizes[i]);
              exons.push({
                start: eStart,
                end: eEnd
              });
            }

            findUTRs(exons, feature.cdStart, feature.cdEnd);
            feature.exons = exons;
          }

          if (autoSql) {
            // TODO -- these should be equal, validate?  fieldCount-definedFieldCount, as.fields.length, tokens.length-3
            const extraStart = definedFieldCount;

            for (let i = extraStart; i < fieldCount; i++) {
              if (i < autoSql.fields.length) {
                const name = autoSql.fields[i].name;
                const value = tokens[i - 3];
                feature[name] = value;
              }
            }
          }
        };
      } //table chromatinInteract
      // "Chromatin interaction between two regions"
      //     (
      //     string chrom;      "Chromosome (or contig, scaffold, etc.). For interchromosomal, use 2 records"
      //     uint chromStart;   "Start position of lower region. For interchromosomal, set to chromStart of this region"
      //     uint chromEnd;     "End position of upper region. For interchromosomal, set to chromEnd of this region"
      //     string name;       "Name of item, for display"
      //     uint score;        "Score from 0-1000"
      //     double value;      "Strength of interaction or other data value. Typically basis for score"
      //     string exp;        "Experiment name (metadata for filtering). Use . if not applicable"
      //     string color;      "Item color.  Specified as r,g,b or hexadecimal #RRGGBB or html color name, as in //www.w3.org/TR/css3-color/#html4."
      //     string region1Chrom;  "Chromosome of lower region. For non-directional interchromosomal, chrom of this region."
      //     uint region1Start;  "Start position of lower/this region"
      //     uint region1End;    "End position in chromosome of lower/this region"
      //     string region1Name;  "Identifier of lower/this region"
      //     string region1Strand; "Orientation of lower/this region: + or -.  Use . if not applicable"
      //     string region2Chrom; "Chromosome of upper region. For non-directional interchromosomal, chrom of other region"
      //     uint region2Start;  "Start position in chromosome of upper/this region"
      //     uint region2End;    "End position in chromosome of upper/this region"
      //     string region2Name; "Identifier of upper/this region"
      //     string region2Strand; "Orientation of upper/this region: + or -.  Use . if not applicable"
      //     )


      function decodeInteract(feature, tokens) {
        feature.chr1 = tokens[5];
        feature.start1 = Number.parseInt(tokens[6]);
        feature.end1 = Number.parseInt(tokens[7]);
        feature.chr2 = tokens[10];
        feature.start2 = Number.parseInt(tokens[11]);
        feature.end2 = Number.parseInt(tokens[12]);
        feature.name = tokens[0];
        feature.score = Number(tokens[1]);
        feature.value = Number(tokens[2]);
        feature.color = tokens[4] === '.' ? undefined : tokens[4] === "0" ? "rgb(0,0,0)" : tokens[4];
        return feature;
      }
    }

    function findUTRs(exons, cdStart, cdEnd) {
      for (let exon of exons) {
        const end = exon.end;
        const start = exon.start;

        if (end < cdStart || start > cdEnd) {
          exon.utr = true;
        } else {
          if (cdStart >= start && cdStart <= end) {
            exon.cdStart = cdStart;
          }

          if (cdEnd >= start && cdEnd <= end) {
            exon.cdEnd = cdEnd;
          }
        }
      }
    }

    function scoreShade(score, color) {
      const alpha = Math.min(1, 0.11 + 0.89 * (score / 779));
      return alpha.toString();
    }

    function parseAutoSQL(str) {
      let table;
      const fields = [];
      let startDecoding = false;
      const lines = str.trim().split(/\s*[\r\n]+\s*/g);

      for (let line of lines) {
        if (line.startsWith('table')) {
          table = line.split(/\s+/)[1].trim();
        } else if (line.startsWith('(')) {
          startDecoding = true;
        } else if (line.startsWith(')')) ; else if (startDecoding) {
          if (line.length > 0) {
            const idx = line.indexOf(';');
            const tokens = line.substr(0, idx).split(/\s+/);
            const description = line.substr(idx + 1).replace(/"/g, '').trim();
            fields.push({
              type: tokens[0],
              name: tokens[1],
              description: description
            });
          }
        }
      }

      return {
        table: table,
        fields: fields
      };
    }

    let BIGWIG_MAGIC_LTH = 0x888FFC26; // BigWig Magic Low to High

    let BIGWIG_MAGIC_HTL = 0x26FC8F66; // BigWig Magic High to Low

    let BIGBED_MAGIC_LTH = 0x8789F2EB; // BigBed Magic Low to High

    let BIGBED_MAGIC_HTL = 0xEBF28987; // BigBed Magic High to Low

    let BBFILE_HEADER_SIZE = 64;
    let RPTREE_HEADER_SIZE = 48;
    let RPTREE_NODE_LEAF_ITEM_SIZE = 32; // leaf item size

    let RPTREE_NODE_CHILD_ITEM_SIZE = 24; // child item size

    let BUFFER_SIZE = 512000; //  buffer

    class BWReader {
      constructor(config, genome) {
        this.path = config.url;
        this.format = config.format || "bigwig";
        this.genome = genome;
        this.rpTreeCache = {};
        this.config = config;
        this.loader = isDataURL(this.path) ? new DataBuffer(this.path) : igvxhr;
      }

      async readWGFeatures(bpPerPixel, windowFunction) {
        await this.loadHeader();
        const chrIdx1 = 0;
        const chrIdx2 = this.chromTree.idToChrom.length - 1;
        const chr1 = this.chromTree.idToChrom[chrIdx1];
        const chr2 = this.chromTree.idToChrom[chrIdx2];
        return this.readFeatures(chr1, 0, chr2, Number.MAX_VALUE, bpPerPixel, windowFunction);
      }

      async readFeatures(chr1, bpStart, chr2, bpEnd, bpPerPixel, windowFunction) {
        await this.loadHeader();
        const chrIdx1 = this.chromTree.chromToID[chr1];
        const chrIdx2 = this.chromTree.chromToID[chr2];

        if (chrIdx1 === undefined || chrIdx2 === undefined) {
          return [];
        }

        let treeOffset;
        let decodeFunction;

        if (this.type === "bigwig") {
          // Select a biwig "zoom level" appropriate for the current resolution.
          const zoomLevelHeaders = await this.getZoomHeaders();
          let zoomLevelHeader = bpPerPixel ? zoomLevelForScale$1(bpPerPixel, zoomLevelHeaders) : undefined;

          if (zoomLevelHeader) {
            treeOffset = zoomLevelHeader.indexOffset;
            decodeFunction = decodeZoomData;
          } else {
            treeOffset = this.header.fullIndexOffset;
            decodeFunction = decodeWigData;
          }
        } else {
          // bigbed, zoom data is not currently used in igv for bed type features
          treeOffset = this.header.fullIndexOffset;
          decodeFunction = getBedDataDecoder.call(this);
        } // Load the R Tree and fine leaf items


        const rpTree = await this.loadRPTree(treeOffset);
        const leafItems = await rpTree.findLeafItemsOverlapping(chrIdx1, bpStart, chrIdx2, bpEnd);

        if (!leafItems || leafItems.length === 0) {
          return [];
        } else {
          // Consolidate leaf items and get all data at once
          let start = Number.MAX_VALUE;
          let end = 0;

          for (let item of leafItems) {
            start = Math.min(start, item.dataOffset);
            end = Math.max(end, item.dataOffset + item.dataSize);
          }

          const size = end - start;
          const arrayBuffer = await this.loader.loadArrayBuffer(this.config.url, buildOptions(this.config, {
            range: {
              start: start,
              size: size
            }
          })); // Parse data and return features

          const allFeatures = [];

          for (let item of leafItems) {
            const uint8Array = new Uint8Array(arrayBuffer, item.dataOffset - start, item.dataSize);
            let plain;
            const isCompressed = this.header.uncompressBuffSize > 0;

            if (isCompressed) {
              plain = inflate(uint8Array);
            } else {
              plain = uint8Array;
            }

            decodeFunction.call(this, new DataView(plain.buffer), chrIdx1, bpStart, chrIdx2, bpEnd, allFeatures, this.chromTree.idToChrom, windowFunction);
          }

          allFeatures.sort(function (a, b) {
            return a.start - b.start;
          });
          return allFeatures;
        }
      }

      async getZoomHeaders() {
        if (this.zoomLevelHeaders) {
          return this.zoomLevelHeaders;
        } else {
          await this.loadHeader();
          return this.zoomLevelHeaders;
        }
      }

      async loadHeader() {
        if (this.header) {
          return this.header;
        } else {
          let data = await this.loader.loadArrayBuffer(this.path, buildOptions(this.config, {
            range: {
              start: 0,
              size: BBFILE_HEADER_SIZE
            }
          }));
          let header; // Assume low-to-high unless proven otherwise

          this.littleEndian = true;
          let binaryParser = new BinaryParser(new DataView(data));
          let magic = binaryParser.getUInt();

          if (magic === BIGWIG_MAGIC_LTH) {
            this.type = "bigwig";
          } else if (magic === BIGBED_MAGIC_LTH) {
            this.type = "bigbed";
          } else {
            //Try big endian order
            this.littleEndian = false;
            binaryParser.littleEndian = false;
            binaryParser.position = 0;
            let magic = binaryParser.getUInt();

            if (magic === BIGWIG_MAGIC_HTL) {
              this.type = "bigwig";
            } else if (magic === BIGBED_MAGIC_HTL) {
              this.type = "bigbed";
            } else ;
          } // Table 5  "Common header for bigwig and bigbed files"


          header = {
            bwVersion: binaryParser.getUShort(),
            nZoomLevels: binaryParser.getUShort(),
            chromTreeOffset: binaryParser.getLong(),
            fullDataOffset: binaryParser.getLong(),
            fullIndexOffset: binaryParser.getLong(),
            fieldCount: binaryParser.getUShort(),
            definedFieldCount: binaryParser.getUShort(),
            autoSqlOffset: binaryParser.getLong(),
            totalSummaryOffset: binaryParser.getLong(),
            uncompressBuffSize: binaryParser.getInt(),
            extensionOffset: binaryParser.getLong()
          }; ///////////

          const startOffset = BBFILE_HEADER_SIZE;
          let range = {
            start: startOffset,
            size: header.fullDataOffset - startOffset + 5
          };
          data = await this.loader.loadArrayBuffer(this.path, buildOptions(this.config, {
            range: range
          }));
          const nZooms = header.nZoomLevels;
          binaryParser = new BinaryParser(new DataView(data));
          this.zoomLevelHeaders = [];
          this.firstZoomDataOffset = Number.MAX_SAFE_INTEGER;

          for (let i = 1; i <= nZooms; i++) {
            const zoomNumber = nZooms - i;
            const zlh = new ZoomLevelHeader(zoomNumber, binaryParser);
            this.firstZoomDataOffset = Math.min(zlh.dataOffset, this.firstZoomDataOffset);
            this.zoomLevelHeaders[zoomNumber] = zlh;
          } // Autosql


          if (header.autoSqlOffset > 0) {
            binaryParser.position = header.autoSqlOffset - startOffset;
            const autoSqlString = binaryParser.getString();

            if (autoSqlString) {
              this.autoSql = parseAutoSQL(autoSqlString);
            }
          } // Total summary


          if (header.totalSummaryOffset > 0) {
            binaryParser.position = header.totalSummaryOffset - startOffset;
            this.totalSummary = new BWTotalSummary(binaryParser);
          } // Chrom data index


          if (header.chromTreeOffset > 0) {
            binaryParser.position = header.chromTreeOffset - startOffset;
            this.chromTree = new BPTree(binaryParser, startOffset, this.genome);
          } else {
            // TODO -- this is an error, not expected
            throw "BigWig chromosome tree offset <= 0";
          } //Finally total data count


          binaryParser.position = header.fullDataOffset - startOffset;
          header.dataCount = binaryParser.getInt(); ///////////

          this.setDefaultVisibilityWindow(header);
          this.header = header;
          return this.header;
        }
      }

      async loadRPTree(offset) {
        let rpTree = this.rpTreeCache[offset];

        if (rpTree) {
          return rpTree;
        } else {
          rpTree = new RPTree(offset, this.config, this.littleEndian, this.loader);
          await rpTree.load();
          this.rpTreeCache[offset] = rpTree;
          return rpTree;
        }
      }

      async getType() {
        await this.loadHeader();
        return this.type;
      }

      async getTrackType() {
        await this.loadHeader();

        if (this.type === "bigwig") {
          return "wig";
        } else {
          return this.autoSql && this.autoSql.table === "chromatinInteract" ? "interact" : "annotation";
        }
      }

      setDefaultVisibilityWindow(header) {
        if (this.type === "bigwig") {
          this.visibilityWindow = -1;
        } else {
          // bigbed
          let genomeSize = this.genome ? this.genome.getGenomeLength() : 3088286401; // Estimate window size to return ~ 1,000 features, assuming even distribution across the genome

          this.visibilityWindow = header.dataCount < 1000 ? -1 : 1000 * (genomeSize / header.dataCount);
        }
      }

    }

    class ZoomLevelHeader {
      constructor(index, byteBuffer) {
        this.index = index;
        this.reductionLevel = byteBuffer.getInt();
        this.reserved = byteBuffer.getInt();
        this.dataOffset = byteBuffer.getLong();
        this.indexOffset = byteBuffer.getLong();
      }

    }

    class RPTree {
      constructor(fileOffset, config, littleEndian, loader) {
        this.config = config;
        this.loader = loader;
        this.fileOffset = fileOffset; // File offset to beginning of tree

        this.path = config.url;
        this.littleEndian = littleEndian;
      }

      async load() {
        const rootNodeOffset = this.fileOffset + RPTREE_HEADER_SIZE;
        const bufferedReader = isDataURL(this.path) ? this.loader : new BufferedReader(this.config, BUFFER_SIZE);
        this.rootNode = await this.readNode(rootNodeOffset, bufferedReader);
        return this;
      }

      async readNode(filePosition, bufferedReader) {
        let dataView = await bufferedReader.dataViewForRange({
          start: filePosition,
          size: 4
        }, false);
        let binaryParser = new BinaryParser(dataView, this.littleEndian);
        const type = binaryParser.getByte();
        const isLeaf = type === 1;
        binaryParser.getByte();
        const count = binaryParser.getUShort();
        filePosition += 4;
        let bytesRequired = count * (isLeaf ? RPTREE_NODE_LEAF_ITEM_SIZE : RPTREE_NODE_CHILD_ITEM_SIZE);
        let range2 = {
          start: filePosition,
          size: bytesRequired
        };
        dataView = await bufferedReader.dataViewForRange(range2, false);
        const items = new Array(count);
        binaryParser = new BinaryParser(dataView);

        if (isLeaf) {
          for (let i = 0; i < count; i++) {
            let item = {
              isLeaf: true,
              startChrom: binaryParser.getInt(),
              startBase: binaryParser.getInt(),
              endChrom: binaryParser.getInt(),
              endBase: binaryParser.getInt(),
              dataOffset: binaryParser.getLong(),
              dataSize: binaryParser.getLong()
            };
            items[i] = item;
          }

          return new RPTreeNode(items);
        } else {
          // non-leaf
          for (let i = 0; i < count; i++) {
            let item = {
              isLeaf: false,
              startChrom: binaryParser.getInt(),
              startBase: binaryParser.getInt(),
              endChrom: binaryParser.getInt(),
              endBase: binaryParser.getInt(),
              childOffset: binaryParser.getLong()
            };
            items[i] = item;
          }

          return new RPTreeNode(items);
        }
      }

      async findLeafItemsOverlapping(chrIdx1, startBase, chrIdx2, endBase) {
        let self = this;
        return new Promise(function (fulfill, reject) {
          let leafItems = [],
              processing = new Set(),
              bufferedReader = isDataURL(self.path) ? self.loader : new BufferedReader(self.config, BUFFER_SIZE);
          processing.add(0); // Zero represents the root node

          findLeafItems(self.rootNode, 0);

          function findLeafItems(node, nodeId) {
            if (overlaps(node, chrIdx1, startBase, chrIdx2, endBase)) {
              let items = node.items;
              items.forEach(function (item) {
                if (overlaps(item, chrIdx1, startBase, chrIdx2, endBase)) {
                  if (item.isLeaf) {
                    leafItems.push(item);
                  } else {
                    if (item.childNode) {
                      findLeafItems(item.childNode);
                    } else {
                      processing.add(item.childOffset); // Represent node to-be-loaded by its file position

                      self.readNode(item.childOffset, bufferedReader).then(function (node) {
                        item.childNode = node;
                        findLeafItems(node, item.childOffset);
                      }).catch(reject);
                    }
                  }
                }
              });
            }

            if (nodeId !== undefined) processing.delete(nodeId); // Wait until all nodes are processed

            if (processing.size === 0) {
              fulfill(leafItems);
            }
          }
        });
      }

    }

    class RPTreeNode {
      constructor(items) {
        this.items = items;
        let minChromId = Number.MAX_SAFE_INTEGER,
            maxChromId = 0,
            minStartBase = Number.MAX_SAFE_INTEGER,
            maxEndBase = 0,
            i,
            item;

        for (i = 0; i < items.length; i++) {
          item = items[i];
          minChromId = Math.min(minChromId, item.startChrom);
          maxChromId = Math.max(maxChromId, item.endChrom);
          minStartBase = Math.min(minStartBase, item.startBase);
          maxEndBase = Math.max(maxEndBase, item.endBase);
        }

        this.startChrom = minChromId;
        this.endChrom = maxChromId;
        this.startBase = minStartBase;
        this.endBase = maxEndBase;
      }

    }

    class BPTree {
      constructor(binaryParser, startOffset, genome) {
        let magic = binaryParser.getInt();
        let blockSize = binaryParser.getInt();
        let keySize = binaryParser.getInt();
        let valSize = binaryParser.getInt();
        let itemCount = binaryParser.getLong();
        let reserved = binaryParser.getLong();
        let chromToId = {};
        let idToChrom = [];
        this.header = {
          magic: magic,
          blockSize: blockSize,
          keySize: keySize,
          valSize: valSize,
          itemCount: itemCount,
          reserved: reserved
        };
        this.chromToID = chromToId;
        this.idToChrom = idToChrom; // Recursively walk tree to populate dictionary

        readTreeNode(binaryParser, -1);

        function readTreeNode(byteBuffer, offset) {
          if (offset >= 0) byteBuffer.position = offset;
          let type = byteBuffer.getByte();
              byteBuffer.getByte();
              let count = byteBuffer.getUShort(),
              i,
              key,
              chromId,
              childOffset,
              bufferOffset,
              currOffset;

          if (type === 1) {
            for (i = 0; i < count; i++) {
              key = byteBuffer.getFixedLengthTrimmedString(keySize);
              chromId = byteBuffer.getInt();
              byteBuffer.getInt();
              if (genome) key = genome.getChromosomeName(key); // Translate to canonical chr name

              chromToId[key] = chromId;
              idToChrom[chromId] = key;
            }
          } else {
            // non-leaf
            for (i = 0; i < count; i++) {
              key = byteBuffer.getFixedLengthTrimmedString(keySize);
              childOffset = byteBuffer.getLong();
              bufferOffset = childOffset - startOffset;
              currOffset = byteBuffer.position;
              readTreeNode(byteBuffer, bufferOffset);
              byteBuffer.position = currOffset;
            }
          }
        }
      }

    }
    /**
     * Return true if {chrIdx1:startBase-chrIdx2:endBase} overlaps item's interval
     * @returns {boolean}
     */


    function overlaps(item, chrIdx1, startBase, chrIdx2, endBase) {
      if (!item) {
        console.log("null item for " + chrIdx1 + " " + startBase + " " + endBase);
        return false;
      }

      return (chrIdx2 > item.startChrom || chrIdx2 === item.startChrom && endBase >= item.startBase) && (chrIdx1 < item.endChrom || chrIdx1 === item.endChrom && startBase <= item.endBase);
    }

    class BWTotalSummary {
      constructor(byteBuffer) {
        if (byteBuffer) {
          this.basesCovered = byteBuffer.getLong();
          this.minVal = byteBuffer.getDouble();
          this.maxVal = byteBuffer.getDouble();
          this.sumData = byteBuffer.getDouble();
          this.sumSquares = byteBuffer.getDouble();
          computeStats.call(this);
        } else {
          this.basesCovered = 0;
          this.minVal = 0;
          this.maxVal = 0;
          this.sumData = 0;
          this.sumSquares = 0;
          this.mean = 0;
          this.stddev = 0;
        }
      }

    }

    function computeStats() {
      let n = this.basesCovered;

      if (n > 0) {
        this.mean = this.sumData / n;
        this.stddev = Math.sqrt(this.sumSquares / (n - 1));
        let min = this.minVal < 0 ? this.mean - 2 * this.stddev : 0,
            max = this.maxVal > 0 ? this.mean + 2 * this.stddev : 0;
        this.defaultRange = {
          min: min,
          max: max
        };
      }
    }

    function zoomLevelForScale$1(bpPerPixel, zoomLevelHeaders) {
      let level;

      for (let i = 0; i < zoomLevelHeaders.length; i++) {
        const zl = zoomLevelHeaders[i];

        if (zl.reductionLevel < bpPerPixel) {
          level = zl;
          break;
        }
      }

      return level;
    }

    function decodeWigData(data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, chrDict) {
      const binaryParser = new BinaryParser(data);
      const chromId = binaryParser.getInt();
      let chromStart = binaryParser.getInt();
      let chromEnd = binaryParser.getInt();
      const itemStep = binaryParser.getInt();
      const itemSpan = binaryParser.getInt();
      const type = binaryParser.getByte();
      binaryParser.getByte();
      let itemCount = binaryParser.getUShort();

      if (chromId >= chrIdx1 && chromId <= chrIdx2) {
        while (itemCount-- > 0) {
          let value;

          switch (type) {
            case 1:
              chromStart = binaryParser.getInt();
              chromEnd = binaryParser.getInt();
              value = binaryParser.getFloat();
              break;

            case 2:
              chromStart = binaryParser.getInt();
              value = binaryParser.getFloat();
              chromEnd = chromStart + itemSpan;
              break;

            case 3:
              // Fixed step
              value = binaryParser.getFloat();
              chromEnd = chromStart + itemSpan;
              chromStart += itemStep;
              break;
          }

          if (chromId < chrIdx1 || chromId === chrIdx1 && chromEnd < bpStart) continue;else if (chromId > chrIdx2 || chromId === chrIdx2 && chromStart >= bpEnd) break;

          if (Number.isFinite(value)) {
            const chr = chrDict[chromId];
            featureArray.push({
              chr: chr,
              start: chromStart,
              end: chromEnd,
              value: value
            });
          }
        }
      }
    }

    function getBedDataDecoder() {
      const minSize = 3 * 4 + 1; // Minimum # of bytes required for a bed record

      const decoder = getDecoder(this.header.definedFieldCount, this.header.fieldCount, this.autoSql, this.format);
      return function (data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, chrDict) {
        const binaryParser = new BinaryParser(data);

        while (binaryParser.remLength() >= minSize) {
          const chromId = binaryParser.getInt();
          const chr = chrDict[chromId];
          const chromStart = binaryParser.getInt();
          const chromEnd = binaryParser.getInt();
          const rest = binaryParser.getString();
          if (chromId < chrIdx1 || chromId === chrIdx1 && chromEnd < bpStart) continue;else if (chromId > chrIdx2 || chromId === chrIdx2 && chromStart >= bpEnd) break;

          if (chromEnd > 0) {
            const feature = {
              chr: chr,
              start: chromStart,
              end: chromEnd
            };
            featureArray.push(feature);
            const tokens = rest.split("\t");
            decoder(feature, tokens);
          }
        }
      };
    }

    function decodeZoomData(data, chrIdx1, bpStart, chrIdx2, bpEnd, featureArray, chrDict, windowFunction) {
      const binaryParser = new BinaryParser(data);
      const minSize = 8 * 4; // Minimum # of bytes required for a zoom record

      while (binaryParser.remLength() >= minSize) {
        const chromId = binaryParser.getInt();
        const chr = chrDict[chromId];
        const chromStart = binaryParser.getInt();
        const chromEnd = binaryParser.getInt();
        const validCount = binaryParser.getInt();
        const minVal = binaryParser.getFloat();
        const maxVal = binaryParser.getFloat();
        const sumData = binaryParser.getFloat();
        binaryParser.getFloat();
        let value;

        switch (windowFunction) {
          case "min":
            value = minVal;
            break;

          case "max":
            value = maxVal;
            break;

          default:
            value = validCount === 0 ? 0 : sumData / validCount;
        }

        if (chromId < chrIdx1 || chromId === chrIdx1 && chromEnd < bpStart) continue;else if (chromId > chrIdx2 || chromId === chrIdx2 && chromStart >= bpEnd) break;

        if (Number.isFinite(value)) {
          featureArray.push({
            chr: chr,
            start: chromStart,
            end: chromEnd,
            value: value
          });
        }
      }
    }

    class DataBuffer {
      constructor(dataURI) {
        this.data = decodeDataURI$1(dataURI).buffer;
      }
      /**
       * igvxhr interface
       * @param ignore
       * @param options
       * @returns {any}
       */


      loadArrayBuffer(ignore, options) {
        const range = options.range;
        return range ? this.data.slice(range.start, range.start + range.size) : this.data;
      }
      /**
       * BufferedReader interface
       *
       * @param requestedRange - byte rangeas {start, size}
       * @param fulfill - function to receive result
       * @param asUint8 - optional flag to return result as an UInt8Array
       */


      async dataViewForRange(requestedRange, asUint8) {
        const len = Math.min(this.data.byteLength - requestedRange.start, requestedRange.size);
        return asUint8 ? new Uint8Array(this.data, requestedRange.start, len) : new DataView(this.data, requestedRange.start, len);
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class BWSource {
      constructor(config, genome) {
        this.reader = new BWReader(config, genome);
        this.genome = genome;
        this.format = config.format || "bigwig";
        this.wgValues = {};
      }

      async getFeatures(_ref) {
        let {
          chr,
          start,
          end,
          bpPerPixel,
          windowFunction
        } = _ref;
        const features = chr.toLowerCase() === "all" ? await this.getWGValues(windowFunction) : await this.reader.readFeatures(chr, start, chr, end, bpPerPixel, windowFunction);
        const isBigWig = this.reader.type === "bigwig";

        if (!isBigWig) {
          pack(features);
        }

        return features;
      }

      async getHeader() {
        return this.reader.loadHeader();
      }

      getDefaultRange() {
        if (this.reader.totalSummary !== undefined) {
          return this.reader.totalSummary.defaultRange;
        } else {
          return undefined;
        }
      }

      async defaultVisibilityWindow() {
        return this.reader.defaultVisibilityWindow;
      }

      async getWGValues(windowFunction) {
        const nominalScreenWidth = 1000; // This doesn't need to be precise

        const genome = this.genome;

        if (this.wgValues[windowFunction]) {
          return this.wgValues[windowFunction];
        } else {
          const bpPerPixel = genome.getGenomeLength() / nominalScreenWidth;
          const features = await this.reader.readWGFeatures(bpPerPixel, windowFunction);
          let wgValues = [];

          for (let f of features) {
            const chr = f.chr;
            const offset = genome.getCumulativeOffset(chr);
            const wgFeature = Object.assign({}, f);
            wgFeature.chr = "all";
            wgFeature.start = offset + f.start;
            wgFeature.end = offset + f.end;
            wgValues.push(wgFeature);
          }

          this.wgValues[windowFunction] = wgValues;
          return wgValues;
        }
      }

      supportsWholeGenome() {
        return this.reader.type === "bigwig";
      }

      async trackType() {
        return this.reader.getTrackType();
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016 University of California San Diego
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const GZIP_FLAG = 0x1;

    class TDFReader {
      constructor(config, genome) {
        this.config = config;
        this.genome = genome;
        this.path = config.url;
        this.groupCache = {};
        this.datasetCache = {};
      }

      async readHeader() {
        if (this.magic !== undefined) {
          return this; // Already read
        }

        let data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
          range: {
            start: 0,
            size: 64000
          }
        }));
        let binaryParser = new BinaryParser(new DataView(data));
        this.magic = binaryParser.getInt();
        this.version = binaryParser.getInt();
        this.indexPos = binaryParser.getLong();
        this.indexSize = binaryParser.getInt();
        binaryParser.getInt();

        if (this.version >= 2) {
          let nWindowFunctions = binaryParser.getInt();
          this.windowFunctions = [];

          while (nWindowFunctions-- > 0) {
            this.windowFunctions.push(binaryParser.getString());
          }
        }

        this.trackType = binaryParser.getString();
        this.trackLine = binaryParser.getString();
        let nTracks = binaryParser.getInt();
        this.trackNames = [];

        while (nTracks-- > 0) {
          this.trackNames.push(binaryParser.getString());
        }

        this.genomeID = binaryParser.getString();
        this.flags = binaryParser.getInt();
        this.compressed = (this.flags & GZIP_FLAG) !== 0; // Now read index

        data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
          range: {
            start: this.indexPos,
            size: this.indexSize
          }
        }));
        binaryParser = new BinaryParser(new DataView(data));
        this.datasetIndex = {};
        let nEntries = binaryParser.getInt();

        while (nEntries-- > 0) {
          const name = binaryParser.getString();
          const pos = binaryParser.getLong();
          const size = binaryParser.getInt();
          this.datasetIndex[name] = {
            position: pos,
            size: size
          };
        }

        this.groupIndex = {};
        nEntries = binaryParser.getInt();

        while (nEntries-- > 0) {
          const name = binaryParser.getString();
          const pos = binaryParser.getLong();
          const size = binaryParser.getInt();
          this.groupIndex[name] = {
            position: pos,
            size: size
          };
        }

        return this;
      }

      async readDataset(chr, windowFunction, zoom) {
        const key = chr + "_" + windowFunction + "_" + zoom;

        if (this.datasetCache[key]) {
          return this.datasetCache[key];
        } else {
          await this.readHeader();
          const wf = this.version < 2 ? "" : "/" + windowFunction;
          const zoomString = chr.toLowerCase() === "all" || zoom === undefined ? "0" : zoom.toString();
          let dsName;

          if (windowFunction === "raw") {
            dsName = "/" + chr + "/raw";
          } else {
            dsName = "/" + chr + "/z" + zoomString + wf;
          }

          const indexEntry = this.datasetIndex[dsName];

          if (indexEntry === undefined) {
            return undefined;
          }

          const data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
            range: {
              start: indexEntry.position,
              size: indexEntry.size
            }
          }));

          if (!data) {
            return undefined;
          }

          const binaryParser = new BinaryParser(new DataView(data));
          let nAttributes = binaryParser.getInt();
          const attributes = {};

          while (nAttributes-- > 0) {
            attributes[binaryParser.getString()] = binaryParser.getString();
          }

          const dataType = binaryParser.getString();
          const tileWidth = binaryParser.getFloat();
          let nTiles = binaryParser.getInt();
          const tiles = [];

          while (nTiles-- > 0) {
            tiles.push({
              position: binaryParser.getLong(),
              size: binaryParser.getInt()
            });
          }

          const dataset = {
            name: dsName,
            attributes: attributes,
            dataType: dataType,
            tileWidth: tileWidth,
            tiles: tiles
          };
          this.datasetCache[key] = dataset;
          return dataset;
        }
      }

      async readRootGroup() {
        const genome = this.genome;
        const rootGroup = this.groupCache["/"];

        if (rootGroup) {
          return rootGroup;
        } else {
          const group = await this.readGroup("/");
          const names = group["chromosomes"];
          const maxZoomString = group["maxZoom"]; // Now parse out interesting attributes.

          if (maxZoomString) {
            this.maxZoom = Number(maxZoomString);
          }

          const totalCountString = group["totalCount"];

          if (totalCountString) {
            group.totalCount = Number(totalCountString);
          } // Chromosome names


          const chrAliasTable = {};

          if (names) {
            names.split(",").forEach(function (chr) {
              const canonicalName = genome.getChromosomeName(chr);
              chrAliasTable[canonicalName] = chr;
            });
          }

          this.chrAliasTable = chrAliasTable;
          this.groupCache["/"] = group;
          return group;
        }
      }

      async readGroup(name) {
        const group = this.groupCache[name];

        if (group) {
          return group;
        } else {
          await this.readHeader();
          const indexEntry = this.groupIndex[name];

          if (indexEntry === undefined) {
            return undefined;
          }

          const data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
            range: {
              start: indexEntry.position,
              size: indexEntry.size
            }
          }));

          if (!data) {
            return undefined;
          }

          const binaryParser = new BinaryParser(new DataView(data));
          const group = {
            name: name
          };
          let nAttributes = binaryParser.getInt();

          while (nAttributes-- > 0) {
            const key = binaryParser.getString();
            const value = binaryParser.getString();
            group[key] = value;
          }

          this.groupCache[name] = group;
          return group;
        }
      }

      async readTiles(tileIndeces, nTracks) {
        tileIndeces.sort(function (a, b) {
          return a.position - b.position;
        });
        tileIndeces = tileIndeces.filter(function (idx) {
          return idx.size > 0;
        });

        if (tileIndeces.length === 0) {
          return Promise.resolve([]);
        }

        const firstEntry = tileIndeces[0];
        const lastEntry = tileIndeces[tileIndeces.length - 1];
        const position = firstEntry.position;
        const size = lastEntry.position + lastEntry.size - position;
        const data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
          range: {
            start: position,
            size: size
          }
        }));
        const tiles = []; // Loop through and decode tiles

        for (let indexEntry of tileIndeces) {
          const start = indexEntry.position - position;
          const size = indexEntry.size;

          if (size > 0) {
            let tileData;

            if (this.compressed) {
              const plain = inflate(data.slice(start, start + size));
              tileData = plain.buffer;
            } else {
              tileData = data.slice(start, start + size);
            }

            const binaryParser = new BinaryParser(new DataView(tileData));
            const type = binaryParser.getString();
            let tile;

            switch (type) {
              case "fixedStep":
                tile = createFixedStep(binaryParser, nTracks);
                break;

              case "variableStep":
                tile = createVariableStep(binaryParser, nTracks);
                break;

              case "bed":
              case "bedWithName":
                tile = createBed(binaryParser, nTracks, type);
                break;

              default:
                throw "Unknown tile type: " + type;
            }

            tiles.push(tile);
          }
        }

        return tiles;
      }

      async readTile(indexEntry, nTracks) {
        let data = await igvxhr.loadArrayBuffer(this.path, buildOptions(this.config, {
          range: {
            start: indexEntry.position,
            size: indexEntry.size
          }
        }));

        if (this.compressed) {
          const plain = inflate(data);
          data = plain.buffer;
        }

        const binaryParser = new BinaryParser(new DataView(data));
        const type = binaryParser.getString();

        switch (type) {
          case "fixedStep":
            return createFixedStep(binaryParser, nTracks);

          case "variableStep":
            return createVariableStep(binaryParser, nTracks);

          case "bed":
          case "bedWithName":
            return createBed(binaryParser, nTracks, type);

          default:
            throw "Unknown tile type: " + type;
        }
      }

    }

    function createFixedStep(binaryParser, nTracks) {
      const nPositions = binaryParser.getInt();
      const start = binaryParser.getInt();
      const span = binaryParser.getFloat();
      const data = [];
      let nt = nTracks;

      while (nt-- > 0) {
        let np = nPositions;
        const dtrack = [];

        while (np-- > 0) {
          dtrack.push(binaryParser.getFloat());
        }

        data.push(dtrack);
      }

      return {
        type: "fixedStep",
        start: start,
        span: span,
        data: data,
        nTracks: nTracks,
        nPositions: nPositions
      };
    }

    function createVariableStep(binaryParser, nTracks) {
      const tileStart = binaryParser.getInt();
      const span = binaryParser.getFloat();
      const nPositions = binaryParser.getInt();
      const start = [];
      let np = nPositions;

      while (np-- > 0) {
        start.push(binaryParser.getInt());
      }

      binaryParser.getInt(); // # of samples, ignored but should === nTracks

      const data = [];
      let nt = nTracks;

      while (nt-- > 0) {
        np = nPositions;
        const dtrack = [];

        while (np-- > 0) {
          dtrack.push(binaryParser.getFloat());
        }

        data.push(dtrack);
      }

      return {
        type: "variableStep",
        tileStart: tileStart,
        span: span,
        start: start,
        data: data,
        nTracks: nTracks,
        nPositions: nPositions
      };
    }

    function createBed(binaryParser, nTracks, type) {
      const nPositions = binaryParser.getInt();
      let n = nPositions;
      const start = [];

      while (n-- > 0) {
        start.push(binaryParser.getInt());
      }

      n = nPositions;
      const end = [];

      while (n-- > 0) {
        end.push(binaryParser.getInt());
      }

      binaryParser.getInt(); // # of samples, ignored but should === nTracks

      const data = [];
      let nt = nTracks;

      while (nt-- > 0) {
        let np = nPositions;
        const dtrack = [];

        while (np-- > 0) {
          dtrack.push(binaryParser.getFloat());
        }

        data.push(dtrack);
      }

      if (type === "bedWithName") {
        n = nPositions;
        const name = [];

        while (n-- > 0) {
          name.push(binaryParser.getString());
        }
      }

      return {
        type: type,
        start: start,
        end: end,
        data: data,
        nTracks: nTracks,
        nPositions: nPositions
      };
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016 University of California San Diego
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class TDFSource {
      constructor(config, genome) {
        this.genome = genome;
        this.windowFunction = config.windowFunction || "mean";
        this.reader = new TDFReader(config, genome);
      }

      async getFeatures(_ref) {
        let {
          chr,
          start,
          end,
          bpPerPixel
        } = _ref;

        if (chr.toLowerCase() === "all") {
          const wgFeatures = [];
          const genome = this.genome;
          const chrNames = this.genome.wgChromosomeNames;

          if (chrNames) {
            for (let c of genome.wgChromosomeNames) {
              const len = genome.getChromosome(c).bpLength;
              bpPerPixel = len / 1000;
              const chrFeatures = await this._getFeatures(c, 0, len, bpPerPixel);

              if (chrFeatures) {
                for (let f of chrFeatures) {
                  const wg = Object.assign({}, f);
                  wg.chr = "all";
                  wg.start = genome.getGenomeCoordinate(f.chr, f.start);
                  wg.end = genome.getGenomeCoordinate(f.chr, f.end);
                  wg._f = f;
                  wgFeatures.push(wg);
                }
              }
            }
          }

          return wgFeatures;
        } else {
          return this._getFeatures(chr, start, end, bpPerPixel);
        }
      }

      async _getFeatures(chr, start, end, bpPerPixel) {
        const genomicInterval = new GenomicInterval(chr, start, end);
        const genome = this.genome;

        if (!this.rootGroup) {
          this.rootGroup = await this.reader.readRootGroup();

          if (!this.normalizationFactor) {
            const totalCount = this.rootGroup.totalCount;

            if (totalCount) {
              this.normalizationFactor = 1.0e6 / totalCount;
            }
          }
        }

        genomicInterval.bpPerPixel = bpPerPixel;
        const zoom = zoomLevelForScale(chr, bpPerPixel, genome);
        let queryChr = this.reader.chrAliasTable[chr];
        let maxZoom = this.reader.maxZoom;
        if (queryChr === undefined) queryChr = chr;
        if (maxZoom === undefined) maxZoom = -1;
        const wf = zoom > maxZoom ? "raw" : this.windowFunction;
        const dataset = await this.reader.readDataset(queryChr, wf, zoom);

        if (dataset == null) {
          return [];
        }

        const tileWidth = dataset.tileWidth;
        const startTile = Math.floor(start / tileWidth);
        const endTile = Math.floor(end / tileWidth);
        const NTRACKS = 1; // TODO read this

        const tiles = await this.reader.readTiles(dataset.tiles.slice(startTile, endTile + 1), NTRACKS);
        const features = [];

        for (let tile of tiles) {
          switch (tile.type) {
            case "bed":
              decodeBedTile(tile, chr, start, end, bpPerPixel, features);
              break;

            case "variableStep":
              decodeVaryTile(tile, chr, start, end, bpPerPixel, features);
              break;

            case "fixedStep":
              decodeFixedTile(tile, chr, start, end, bpPerPixel, features);
              break;

            default:
              throw "Unknown tile type: " + tile.type;
          }
        }

        features.sort(function (a, b) {
          return a.start - b.start;
        });
        return features;
      }

      get supportsWholeGenome() {
        return true;
      }

    }

    function decodeBedTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
      const nPositions = tile.nPositions;
      const starts = tile.start;
      const ends = tile.end;
      const data = tile.data[0]; // Single track for now

      for (let i = 0; i < nPositions; i++) {
        const s = starts[i];
        const e = ends[i];
        if (e < bpStart) continue;
        if (s > bpEnd) break;
        features.push({
          chr: chr,
          start: s,
          end: e,
          value: data[i]
        });
      }
    }

    function decodeVaryTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
      const nPositions = tile.nPositions;
      const starts = tile.start;
      const span = tile.span;
      const data = tile.data[0]; // Single track for now

      for (let i = 0; i < nPositions; i++) {
        const s = starts[i];
        const e = s + span;
        if (e < bpStart) continue;
        if (s > bpEnd) break;
        features.push({
          chr: chr,
          start: s,
          end: e,
          value: data[i]
        });
      }
    }

    function decodeFixedTile(tile, chr, bpStart, bpEnd, bpPerPixel, features) {
      const nPositions = tile.nPositions;
      let s = tile.start;
      const span = tile.span;
      const data = tile.data[0]; // Single track for now

      for (let i = 0; i < nPositions; i++) {
        const e = s + span;
        if (s > bpEnd) break;

        if (e >= bpStart) {
          if (!Number.isNaN(data[i])) {
            features.push({
              chr: chr,
              start: s,
              end: e,
              value: data[i]
            });
          }
        }

        s = e;
      }
    }

    var log2 = Math.log(2);

    function zoomLevelForScale(chr, bpPerPixel, genome) {
      // Convert bpPerPixel to IGV "zoom" level.   This is a bit convoluted,  IGV computes zoom levels assuming
      // display in a 700 pixel window.  The fully zoomed out view of a chromosome is zoom level "0".
      // Zoom level 1 is magnified 2X,  and so forth
      var chrSize = genome.getChromosome(chr).bpLength;
      return Math.ceil(Math.log(Math.max(0, chrSize / (bpPerPixel * 700))) / log2);
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014-2015 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const bbFormats = new Set(['bigwig', 'bw', 'bigbed', 'bb', 'biginteract', 'biggenepred', 'bignarrowpeak']);

    function FeatureSource(config, genome) {
      const format = config.format ? config.format.toLowerCase() : undefined;

      if (bbFormats.has(format)) {
        return new BWSource(config, genome);
      } else if ("tdf" === format) {
        return new TDFSource(config, genome);
      } else {
        return new TextFeatureSource(config, genome);
      }
    }

    const GtexUtils = {
      getTissueInfo: function (datasetId, baseURL) {
        datasetId = datasetId || 'gtex_v8';
        baseURL = baseURL || 'https://gtexportal.org/rest/v1';
        let url = baseURL + '/dataset/tissueInfo?datasetId=' + datasetId;
        return igvxhr.loadJson(url, {});
      },
      //https://gtexportal.org/rest/v1/association/singleTissueEqtlByLocation?chromosome=7&start=98358766&end=101523798&tissueName=Liver&datasetId=gtex_v7
      //https://gtexportal.org/rest/v1/association/singleTissueEqtlByLocation?chromosome=7&start=98358766&end=101523798&tissueSiteDetailId=Liver&datasetId=gtex_v8
      trackConfiguration: function (tissueSummary, baseURL) {
        baseURL = baseURL || 'https://gtexportal.org/rest/v1';
        return {
          type: "eqtl",
          sourceType: "gtex-ws",
          url: baseURL + '/association/singleTissueEqtlByLocation',
          tissueSiteDetailId: tissueSummary.tissueSiteDetailId,
          name: tissueSummary.tissueSiteDetailId.split('_').join(' '),
          visibilityWindow: 250000
        };
      }
    };

    /**
     * @param feature
     * @param bpStart  genomic location of the left edge of the current canvas
     * @param xScale  scale in base-pairs per pixel
     * @returns {{px: number, px1: number, pw: number, h: number, py: number}}
     */

    function calculateFeatureCoordinates(feature, bpStart, xScale) {
      let px = (feature.start - bpStart) / xScale;
      let px1 = (feature.end - bpStart) / xScale; //px = Math.round((feature.start - bpStart) / xScale),
      //px1 = Math.round((feature.end - bpStart) / xScale),

      let pw = px1 - px;

      if (pw < 3) {
        pw = 3;
        px -= 1.5;
      }

      return {
        px: px,
        px1: px1,
        pw: pw
      };
    }
    /**
     *
     * @param feature
     * @param bpStart  genomic location of the left edge of the current canvas
     * @param xScale  scale in base-pairs per pixel
     * @param pixelHeight  pixel height of the current canvas
     * @param ctx  the canvas 2d context
     * @param options  genomic state
     */

    function renderFeature(feature, bpStart, xScale, pixelHeight, ctx, options) {
      try {
        ctx.save(); // Set ctx color to a known valid color.  If getColorForFeature returns an invalid color string it is ignored, and
        // this default will be used.

        ctx.fillStyle = this.defaultColor;
        ctx.strokeStyle = this.defaultColor;
        const color = getColorForFeature.call(this, feature);
        ctx.fillStyle = color;
        ctx.strokeStyle = color;
        let h;
        let py;

        if (this.displayMode === "SQUISHED" && feature.row !== undefined) {
          h = this.featureHeight / 2;
          py = this.margin + this.squishedRowHeight * feature.row;
        } else if (this.displayMode === "EXPANDED" && feature.row !== undefined) {
          h = this.featureHeight;
          py = this.margin + this.expandedRowHeight * feature.row;
        } else {
          // collapsed
          h = this.featureHeight;
          py = this.margin;
        }

        const pixelWidth = options.pixelWidth;
        const cy = py + h / 2;
        const h2 = h / 2;
        const py2 = cy - h2 / 2;
        const exonCount = feature.exons ? feature.exons.length : 0;
        const coord = calculateFeatureCoordinates(feature, bpStart, xScale);
        const step = this.arrowSpacing;
        const direction = feature.strand === '+' ? 1 : feature.strand === '-' ? -1 : 0;

        if (exonCount === 0) {
          // single-exon transcript
          const xLeft = Math.max(0, coord.px);
          const xRight = Math.min(pixelWidth, coord.px1);
          const width = xRight - xLeft;
          ctx.fillRect(xLeft, py, width, h); // Arrows
          // Do not draw if strand is not +/-

          if (direction !== 0) {
            ctx.fillStyle = "white";
            ctx.strokeStyle = "white";

            for (let x = xLeft + step / 2; x < xRight; x += step) {
              // draw arrowheads along central line indicating transcribed orientation
              IGVGraphics.strokeLine(ctx, x - direction * 2, cy - 2, x, cy);
              IGVGraphics.strokeLine(ctx, x - direction * 2, cy + 2, x, cy);
            }

            ctx.fillStyle = color;
            ctx.strokeStyle = color;
          }
        } else {
          // multi-exon transcript
          IGVGraphics.strokeLine(ctx, coord.px + 1, cy, coord.px1 - 1, cy); // center line for introns

          const xLeft = Math.max(0, coord.px) + step / 2;
          const xRight = Math.min(pixelWidth, coord.px1);

          for (let x = xLeft; x < xRight; x += step) {
            // draw arrowheads along central line indicating transcribed orientation
            IGVGraphics.strokeLine(ctx, x - direction * 2, cy - 2, x, cy);
            IGVGraphics.strokeLine(ctx, x - direction * 2, cy + 2, x, cy);
          }

          for (let e = 0; e < exonCount; e++) {
            // draw the exons
            const exon = feature.exons[e];
            let ePx = Math.round((exon.start - bpStart) / xScale);
            let ePx1 = Math.round((exon.end - bpStart) / xScale);
            let ePw = Math.max(1, ePx1 - ePx);
            let ePxU;

            if (ePx + ePw < 0) {
              continue; // Off the left edge
            }

            if (ePx > pixelWidth) {
              break; // Off the right edge
            }

            if (exon.utr) {
              ctx.fillRect(ePx, py2, ePw, h2); // Entire exon is UTR
            } else {
              if (exon.cdStart) {
                ePxU = Math.round((exon.cdStart - bpStart) / xScale);
                ctx.fillRect(ePx, py2, ePxU - ePx, h2); // start is UTR

                ePw -= ePxU - ePx;
                ePx = ePxU;
              }

              if (exon.cdEnd) {
                ePxU = Math.round((exon.cdEnd - bpStart) / xScale);
                ctx.fillRect(ePxU, py2, ePx1 - ePxU, h2); // start is UTR

                ePw -= ePx1 - ePxU;
                ePx1 = ePxU;
              }

              ePw = Math.max(ePw, 1);
              ctx.fillRect(ePx, py, ePw, h); // Arrows

              if (ePw > step + 5 && direction !== 0) {
                ctx.fillStyle = "white";
                ctx.strokeStyle = "white";

                for (let x = ePx + step / 2; x < ePx1; x += step) {
                  // draw arrowheads along central line indicating transcribed orientation
                  IGVGraphics.strokeLine(ctx, x - direction * 2, cy - 2, x, cy);
                  IGVGraphics.strokeLine(ctx, x - direction * 2, cy + 2, x, cy);
                }

                ctx.fillStyle = color;
                ctx.strokeStyle = color;
              }
            }
          }
        }

        if (options.drawLabel && this.displayMode !== "SQUISHED") {
          renderFeatureLabel.call(this, ctx, feature, coord.px, coord.px1, py, options.referenceFrame, options);
        }
      } finally {
        ctx.restore();
      }
    }
    /**
     * @param ctx       the canvas 2d context
     * @param feature
     * @param featureX  feature start x-coordinate
     * @param featureX1 feature end x-coordinate
     * @param featureY  feature y-coordinate
     * @param windowX   visible window start x-coordinate
     * @param windowX1  visible window end x-coordinate
     * @param referenceFrame  genomic state
     * @param options  options
     */

    function renderFeatureLabel(ctx, feature, featureX, featureX1, featureY, referenceFrame, options) {
      try {
        ctx.save();
        let name = feature.name;
        if (name === undefined && feature.gene) name = feature.gene.name;
        if (name === undefined) name = feature.id || feature.ID;
        if (!name || name === '.') return;
        let pixelXOffset = options.pixelXOffset || 0;
        const t1 = Math.max(featureX, -pixelXOffset);
        const t2 = Math.min(featureX1, -pixelXOffset + options.viewportWidth);
        const centerX = (t1 + t2) / 2;
        let transform;

        if (this.displayMode === "COLLAPSED" && this.labelDisplayMode === "SLANT") {
          transform = {
            rotate: {
              angle: 45
            }
          };
        }

        const labelY = getFeatureLabelY(featureY, transform);
        let color = getColorForFeature.call(this, feature);
        let geneColor;
        let gtexSelection = false;

        if (referenceFrame.selection && GtexUtils.gtexLoaded) {
          // TODO -- for gtex, figure out a better way to do this
          gtexSelection = true;
          geneColor = referenceFrame.selection.colorForGene(name);
        }

        const geneFontStyle = {
          textAlign: "SLANT" === this.labelDisplayMode ? undefined : 'center',
          fillStyle: geneColor || color,
          strokeStyle: geneColor || color
        };
        const textBox = ctx.measureText(name);
        const xleft = centerX - textBox.width / 2;
        const xright = centerX + textBox.width / 2;

        if (options.labelAllFeatures || xleft > options.rowLastLabelX[feature.row] || gtexSelection) {
          options.rowLastLabelX[feature.row] = xright;
          IGVGraphics.fillText(ctx, name, centerX, labelY, geneFontStyle, transform);
        }
      } finally {
        ctx.restore();
      }
    }

    function getFeatureLabelY(featureY, transform) {
      return transform ? featureY + 20 : featureY + 25;
    }
    /**
     * Return color for feature.  Called in the context of a FeatureTrack instance.
     * @param feature
     * @returns {string}
     */


    function getColorForFeature(feature) {
      let color;

      if (this.altColor && "-" === feature.strand) {
        color = typeof this.altColor === "function" ? this.altColor(feature) : this.altColor;
      } else if (this.color) {
        color = typeof this.color === "function" ? this.color(feature) : this.color; // Explicit setting via menu, or possibly track line if !config.color
      } else if (this.colorBy) {
        const value = feature.getAttributeValue ? feature.getAttributeValue(this.colorBy) : feature[this.colorBy];
        color = this.colorTable.getColor(value);
      } else if (feature.color) {
        color = feature.color; // Explicit color for feature
      } else {
        color = this.defaultColor; // Track default
      }

      if (feature.alpha && feature.alpha !== 1) {
        color = IGVColor.addAlpha(color, feature.alpha);
      } else if (this.useScore && feature.score && !Number.isNaN(feature.score)) {
        // UCSC useScore option, for scores between 0-1000.  See https://genome.ucsc.edu/goldenPath/help/customTrack.html#TRACK
        const min = this.config.min ? this.config.min : 0; //getViewLimitMin(track);

        const max = this.config.max ? this.config.max : 1000; //getViewLimitMax(track);

        const alpha = getAlpha(min, max, feature.score);
        feature.alpha = alpha; // Avoid computing again

        color = IGVColor.addAlpha(color, alpha);
      }

      function getAlpha(min, max, score) {
        const binWidth = (max - min) / 9;
        const binNumber = Math.floor((score - min) / binWidth);
        return Math.min(1.0, 0.2 + binNumber * 0.8 / 9);
      }

      return color;
    }

    // SNP constants
    const codingNonSynonSet = new Set(['nonsense', 'missense', 'stop-loss', 'frameshift', 'cds-indel']);
    const codingSynonSet = new Set(['coding-synon']);
    const spliceSiteSet = new Set(['splice-3', 'splice-5']);
    const untranslatedSet = new Set(['untranslated-5', 'untranslated-3']);
    /**
     * Renderer for a UCSC snp track
     *
     * @param snp
     * @param bpStart  genomic location of the left edge of the current canvas
     * @param xScale  scale in base-pairs per pixel
     * @param pixelHeight  pixel height of the current canvas
     * @param ctx  the canvas 2d context
     */

    function renderSnp(snp, bpStart, xScale, pixelHeight, ctx) {
      var coord = calculateFeatureCoordinates(snp, bpStart, xScale),
          py = this.margin,
          h,
          colorArrLength = this.snpColors.length,
          colorPriority;
      h = this.displayMode === "squished" ? this.squishedRowHeight : this.expandedRowHeight;

      switch (this.colorBy) {
        case 'function':
          colorPriority = colorByFunc(snp.func);
          break;

        case 'class':
          colorPriority = colorByClass(snp['class']);
      }

      ctx.fillStyle = this.snpColors[colorPriority];
      ctx.fillRect(coord.px, py, coord.pw, h); // Coloring functions, convert a value to a priority

      function colorByFunc(theFunc) {
        var priorities;
        var funcArray = theFunc.split(','); // possible func values

        priorities = funcArray.map(function (func) {
          if (codingNonSynonSet.has(func) || spliceSiteSet.has(func)) {
            return colorArrLength - 1;
          } else if (codingSynonSet.has(func)) {
            return colorArrLength - 2;
          } else if (untranslatedSet.has(func)) {
            return colorArrLength - 3;
          } else {
            // locusSet.has(func) || intronSet.has(func)
            return 0;
          }
        });
        return priorities.reduce(function (a, b) {
          return Math.max(a, b);
        });
      }

      function colorByClass(cls) {
        if (cls === 'deletion') {
          return colorArrLength - 1;
        } else if (cls === 'mnp') {
          return colorArrLength - 2;
        } else if (cls === 'microsatellite' || cls === 'named') {
          return colorArrLength - 3;
        } else {
          // cls === 'single' || cls === 'in-del' || cls === 'insertion'
          return 0;
        }
      }
    }

    /**
     *
     * @param feature
     * @param bpStart  genomic location of the left edge of the current canvas
     * @param xScale  scale in base-pairs per pixel
     * @param pixelHeight  pixel height of the current canvas
     * @param ctx  the canvas 2d context
     */
    function renderFusionJuncSpan(feature, bpStart, xScale, pixelHeight, ctx) {
      const rowHeight = this.displayMode === "EXPANDED" ? this.expandedRowHeight : this.squishedRowHeight;
      let py = this.margin;

      if (this.displayMode !== "COLLAPSED" && feature.row !== undefined) {
        py += feature.row * rowHeight;
      }

      const cy = py + 0.5 * rowHeight;
      const topY = cy - 0.5 * rowHeight;
      const bottomY = cy + 0.5 * rowHeight; // draw the junction arc

      const junctionLeftPx = Math.round((feature.junction_left - bpStart) / xScale);
      const junctionRightPx = Math.round((feature.junction_right - bpStart) / xScale);
      ctx.beginPath();
      ctx.moveTo(junctionLeftPx, cy);
      ctx.bezierCurveTo(junctionLeftPx, topY, junctionRightPx, topY, junctionRightPx, cy);
      ctx.lineWidth = 1 + Math.log(feature.num_junction_reads) / Math.log(2);
      ctx.strokeStyle = 'blue';
      ctx.stroke(); // draw the spanning arcs

      const spanningCoords = feature.spanning_frag_coords;

      for (let i = 0; i < spanningCoords.length; i++) {
        const spanningInfo = spanningCoords[i];
        const spanLeftPx = Math.round((spanningInfo.left - bpStart) / xScale);
        const spanRightPx = Math.round((spanningInfo.right - bpStart) / xScale);
        ctx.beginPath();
        ctx.moveTo(spanLeftPx, cy);
        ctx.bezierCurveTo(spanLeftPx, bottomY, spanRightPx, bottomY, spanRightPx, cy);
        ctx.lineWidth = 1;
        ctx.strokeStyle = 'purple';
        ctx.stroke();
      }
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class FeatureTrack extends TrackBase {
      constructor(config, browser) {
        super(config, browser);
      }

      init(config) {
        super.init(config);
        this.type = config.type || "annotation"; // Set maxRows -- protects against pathological feature packing cases (# of rows of overlapping feaures)

        this.maxRows = config.maxRows === undefined ? 1000 : config.maxRows;
        this.displayMode = config.displayMode || "EXPANDED"; // COLLAPSED | EXPANDED | SQUISHED

        this.labelDisplayMode = config.labelDisplayMode;

        if (config._featureSource) {
          this.featureSource = config._featureSource;
          delete config._featureSource;
        } else {
          this.featureSource = config.featureSource ? config.featureSource : FeatureSource(config, this.browser.genome);
        } // Set default heights


        this.autoHeight = config.autoHeight;
        this.margin = config.margin === undefined ? 10 : config.margin;
        this.featureHeight = config.featureHeight || 14;

        if ("FusionJuncSpan" === config.type) {
          this.render = config.render || renderFusionJuncSpan;
          this.squishedRowHeight = config.squishedRowHeight || 50;
          this.expandedRowHeight = config.expandedRowHeight || 50;
          this.height = config.height || this.margin + 2 * this.expandedRowHeight;
        } else if ('snp' === config.type) {
          this.render = config.render || renderSnp; // colors ordered based on priority least to greatest

          this.snpColors = ['rgb(0,0,0)', 'rgb(0,0,255)', 'rgb(0,255,0)', 'rgb(255,0,0)'];
          this.colorBy = 'function';
          this.expandedRowHeight = config.expandedRowHeight || 10;
          this.squishedRowHeight = config.squishedRowHeight || 5;
          this.height = config.height || 30;
        } else {
          this.render = config.render || renderFeature;
          this.arrowSpacing = 30; // adjust label positions to make sure they're always visible

          monitorTrackDrag(this);
          this.squishedRowHeight = config.squishedRowHeight || 15;
          this.expandedRowHeight = config.expandedRowHeight || 30;
          this.height = config.height || this.margin + 2 * this.expandedRowHeight; // Set colorBy fields considering legacy options for backward compatibility

          if (config.colorBy) {
            if (config.colorBy.field) {
              config.colorTable = config.colorBy.pallete || config.colorBy.palette;
              config.colorBy = config.colorBy.field;
            }

            this.colorBy = config.colorBy; // Can be undefined => default

            if (config.colorTable) {
              this.colorTable = new ColorTable(config.colorTable);
            } else {
              this.colorTable = new PaletteColorTable("Set1");
            }
          }
        } //UCSC useScore option


        this.useScore = config.useScore;
      }

      async postInit() {
        if (typeof this.featureSource.getHeader === "function") {
          this.header = await this.featureSource.getHeader();
        } // Set properties from track line


        if (this.header) {
          this.setTrackProperties(this.header);
        }

        if (this.visibilityWindow === undefined && typeof this.featureSource.defaultVisibilityWindow === 'function') {
          this.visibilityWindow = await this.featureSource.defaultVisibilityWindow();
        }

        return this;
      }

      get supportsWholeGenome() {
        if (this.config.supportsWholeGenome !== undefined) {
          return this.config.supportsWholeGenome;
        } else if (this.featureSource && typeof this.featureSource.supportsWholeGenome === 'function') {
          return this.featureSource.supportsWholeGenome();
        } else {
          if (this.visibilityWindow === undefined && (this.config.indexed === false || !this.config.indexURL)) {
            return true;
          }
        }
      }

      async getFeatures(chr, start, end, bpPerPixel) {
        const visibilityWindow = this.visibilityWindow;
        return this.featureSource.getFeatures({
          chr,
          start,
          end,
          bpPerPixel,
          visibilityWindow
        });
      }

      /**
       * The required height in pixels required for the track content.   This is not the visible track height, which
       * can be smaller (with a scrollbar) or larger.
       *
       * @param features
       * @returns {*}
       */
      computePixelHeight(features) {
        if (this.displayMode === "COLLAPSED") {
          return this.margin + this.expandedRowHeight;
        } else {
          let maxRow = 0;

          if (features && typeof features.forEach === "function") {
            for (let feature of features) {
              if (feature.row && feature.row > maxRow) {
                maxRow = feature.row;
              }
            }
          }

          const height = this.margin + (maxRow + 1) * ("SQUISHED" === this.displayMode ? this.squishedRowHeight : this.expandedRowHeight);
          return height;
        }
      }

      draw(options) {
        const featureList = options.features;
        const ctx = options.context;
        const bpPerPixel = options.bpPerPixel;
        const bpStart = options.bpStart;
        const pixelWidth = options.pixelWidth;
        const pixelHeight = options.pixelHeight;
        const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;

        if (!this.config.isMergedTrack) {
          IGVGraphics.fillRect(ctx, 0, options.pixelTop, pixelWidth, pixelHeight, {
            'fillStyle': "rgb(255, 255, 255)"
          });
        }

        if (featureList) {
          const rowFeatureCount = [];
          options.rowLastX = [];
          options.rowLastLabelX = [];

          for (let feature of featureList) {
            if (feature.start > bpStart && feature.end < bpEnd) {
              const row = this.displayMode === "COLLAPSED" ? 0 : feature.row || 0;

              if (rowFeatureCount[row] === undefined) {
                rowFeatureCount[row] = 1;
              } else {
                rowFeatureCount[row]++;
              }

              options.rowLastX[row] = -Number.MAX_SAFE_INTEGER;
              options.rowLastLabelX[row] = -Number.MAX_SAFE_INTEGER;
            }
          }

          const pixelsPerFeature = pixelWidth / Math.max(...rowFeatureCount);
          let lastPxEnd = [];

          for (let feature of featureList) {
            if (feature.end < bpStart) continue;
            if (feature.start > bpEnd) break;
            const row = this.displayMode === 'COLLAPSED' ? 0 : feature.row;
            options.drawLabel = options.labelAllFeatures || pixelsPerFeature > 10;
            const pxEnd = Math.ceil((feature.end - bpStart) / bpPerPixel);
            const last = lastPxEnd[row];

            if (!last || pxEnd > last) {
              this.render.call(this, feature, bpStart, bpPerPixel, pixelHeight, ctx, options); // Ensure a visible gap between features

              const pxStart = Math.floor((feature.start - bpStart) / bpPerPixel);

              if (last && pxStart - last <= 0) {
                ctx.globalAlpha = 0.5;
                IGVGraphics.strokeLine(ctx, pxStart, 0, pxStart, pixelHeight, {
                  'strokeStyle': "rgb(255, 255, 255)"
                });
                ctx.globalAlpha = 1.0;
              }

              lastPxEnd[row] = pxEnd;
            }
          }
        } else {
          console.log("No feature list");
        }
      }

      clickedFeatures(clickState, features) {
        const y = clickState.y - this.margin;
        const allFeatures = super.clickedFeatures(clickState, features);
        let row;

        switch (this.displayMode) {
          case 'SQUISHED':
            row = Math.floor(y / this.squishedRowHeight);
            break;

          case 'EXPANDED':
            row = Math.floor(y / this.expandedRowHeight);
            break;

          default:
            row = undefined;
        }

        return allFeatures.filter(function (feature) {
          return row === undefined || feature.row === undefined || row === feature.row;
        });
      }
      /**
       * Return "popup data" for feature @ genomic location.  Data is an array of key-value pairs
       */


      popupData(clickState, features) {
        features = this.clickedFeatures(clickState, features);
        const genomicLocation = clickState.genomicLocation;
        const data = [];

        for (let feature of features) {
          // Whole genome hack, whole-genome psuedo features store the "real" feature in an _f field
          const f = feature._f || feature;
          const featureData = typeof f.popupData === "function" ? f.popupData(genomicLocation) : this.extractPopupData(f);

          if (featureData) {
            if (data.length > 0) {
              data.push("<hr/><hr/>");
            } // If we have an infoURL, find the name property and create the link.  We do this at this level
            // to catch name properties in both custom popupData functions and the generic extractPopupData function


            const infoURL = this.infoURL || this.config.infoURL;

            for (let fd of featureData) {
              data.push(fd);

              if (infoURL && fd.name && fd.name.toLowerCase() === "name" && fd.value && isString$3(fd.value) && !fd.value.startsWith("<")) {
                const href = infoURL.replace("$$", feature.name);
                fd.value = `<a target=_blank href=${href}>${fd.value}</a>`;
              }
            } //Array.prototype.push.apply(data, featureData);
            // If we have clicked over an exon number it.
            // Disabled for GFF and GTF files if the visibility window is < the feature length since we don't know if we have all exons


            const isGFF = "gff" === this.config.format || "gff3" === this.config.format || "gtf" === this.config.format;

            if (f.exons) {
              for (let i = 0; i < f.exons.length; i++) {
                const exon = f.exons[i];

                if (genomicLocation >= exon.start && genomicLocation <= exon.end) {
                  const exonNumber = isGFF ? exon.number : f.strand === "-" ? f.exons.length - i : i + 1;

                  if (exonNumber) {
                    data.push('<hr/>');
                    data.push({
                      name: "Exon Number",
                      value: exonNumber
                    });
                  }

                  break;
                }
              }
            }
          }
        }

        return data;
      }

      menuItemList() {
        const menuItems = [];

        if (this.render === renderSnp) {
          menuItems.push('<hr/>');

          for (let colorScheme of ["function", "class"]) {
            menuItems.push({
              object: $$1(createCheckbox$1('Color by ' + colorScheme, colorScheme === this.colorBy)),
              click: () => {
                this.colorBy = colorScheme;
                this.trackView.repaintViews();
              }
            });
          }
        }

        menuItems.push('<hr/>');

        for (let displayMode of ["COLLAPSED", "SQUISHED", "EXPANDED"]) {
          const lut = {
            "COLLAPSED": "Collapse",
            "SQUISHED": "Squish",
            "EXPANDED": "Expand"
          };
          menuItems.push({
            object: $$1(createCheckbox$1(lut[displayMode], displayMode === this.displayMode)),
            click: () => {
              this.displayMode = displayMode;
              this.config.displayMode = displayMode;
              this.trackView.checkContentHeight();
              this.trackView.repaintViews();
            }
          });
        }

        return menuItems;
      }

      contextMenuItemList(clickState) {
        const features = this.clickedFeatures(clickState);

        if (features.length > 1) {
          features.sort((a, b) => b.end - b.start - (a.end - a.start));
        }

        const f = features[0]; // The shortest clicked feature

        if (f.end - f.start <= 1000000) {
          const list = [{
            label: 'View feature sequence',
            click: async () => {
              let seq = await this.browser.genome.getSequence(f.chr, f.start, f.end);

              if (f.strand === '-') {
                seq = reverseComplementSequence(seq);
              }

              if (!seq) seq = "Unknown sequence";
              Alert.presentAlert(seq);
            }
          }];

          if (isSecureContext() && navigator.clipboard !== undefined) {
            list.push({
              label: 'Copy feature sequence',
              click: async () => {
                let seq = await this.browser.genome.getSequence(f.chr, f.start, f.end);

                if (f.strand === '-') {
                  seq = reverseComplementSequence(seq);
                }

                try {
                  await navigator.clipboard.writeText(seq);
                } catch (e) {
                  console.error(e);
                  Alert.presentAlert(`error copying sequence to clipboard ${e}`);
                }
              }
            });
          }

          list.push('<hr/>');
          return list;
        } else {
          return undefined;
        }
      }

      description() {
        // if('snp' === this.type) {
        if (renderSnp === this.render) {
          let desc = "<html>" + this.name + '<hr/>';
          desc += '<em>Color By Function:</em><br>';
          desc += '<span style="color:red">Red</span>: Coding-Non-Synonymous, Splice Site<br>';
          desc += '<span style="color:green">Green</span>: Coding-Synonymous<br>';
          desc += '<span style="color:blue">Blue</span>: Untranslated<br>';
          desc += '<span style="color:black">Black</span>: Intron, Locus, Unknown<br><br>';
          desc += '<em>Color By Class:</em><br>';
          desc += '<span style="color:red">Red</span>: Deletion<br>';
          desc += '<span style="color:green">Green</span>: MNP<br>';
          desc += '<span style="color:blue">Blue</span>: Microsatellite, Named<br>';
          desc += '<span style="color:black">Black</span>: Indel, Insertion, SNP';
          desc += "</html>";
          return desc;
        } else {
          return super.description();
        }
      }

      /**
       * Called when the track is removed.  Do any needed cleanup here
       */
      dispose() {
        this.trackView = undefined;
      }

    }
    /**
     * Monitors track drag events, updates label position to ensure that they're always visible.
     * @param track
     */


    function monitorTrackDrag(track) {
      if (track.browser.on) {
        track.browser.on('trackdragend', onDragEnd);
        track.browser.on('trackremoved', unSubscribe);
      }

      function onDragEnd() {
        if (track.trackView && track.displayMode !== "SQUISHED") {
          track.trackView.repaintViews(); // TODO -- refine this to the viewport that was dragged after DOM refactor
        }
      }

      function unSubscribe(removedTrack) {
        if (track.browser.un && track === removedTrack) {
          track.browser.un('trackdragend', onDragEnd);
          track.browser.un('trackremoved', unSubscribe);
        }
      }
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const DEFAULT_COLOR = "rgb(150,150,150)";

    class WigTrack extends TrackBase {
      constructor(config, browser) {
        super(config, browser);
      }

      init(config) {
        super.init(config);
        this.type = "wig";
        this.height = config.height || 50;
        this.featureType = 'numeric';
        this.paintAxis = paintAxis;
        const format = config.format ? config.format.toLowerCase() : config.format;
        this.flipAxis = config.flipAxis ? config.flipAxis : false;
        this.logScale = config.logScale ? config.logScale : false;

        if ("bigwig" === format) {
          this.featureSource = new BWSource(config, this.browser.genome);
        } else if ("tdf" === format) {
          this.featureSource = new TDFSource(config, this.browser.genome);
        } else {
          this.featureSource = FeatureSource(config, this.browser.genome);
        }

        this.autoscale = config.autoscale || config.max === undefined;

        if (!this.autoscale) {
          this.dataRange = {
            min: config.min || 0,
            max: config.max
          };
        }

        this.windowFunction = config.windowFunction || "mean";
        this.graphType = config.graphType || "bar";
        this.normalize = config.normalize; // boolean, for use with "TDF" files

        this.scaleFactor = config.scaleFactor; // optional scale factor, ignored if normalize === true;
      }

      async postInit() {
        const header = await this.getHeader();
        if (header) this.setTrackProperties(header);
      }

      async getFeatures(chr, start, end, bpPerPixel) {
        const features = await this.featureSource.getFeatures({
          chr,
          start,
          end,
          bpPerPixel,
          windowFunction: this.windowFunction
        });

        if (this.normalize && this.featureSource.normalizationFactor) {
          const scaleFactor = this.featureSource.normalizationFactor;

          for (let f of features) {
            f.value *= scaleFactor;
          }
        }

        if (this.scaleFactor) {
          const scaleFactor = this.scaleFactor;

          for (let f of features) {
            f.value *= scaleFactor;
          }
        }

        return features;
      }

      menuItemList() {
        let items = [];

        if (this.flipAxis !== undefined) {
          items.push({
            label: "Flip y-axis",
            click: () => {
              this.flipAxis = !this.flipAxis;
              this.trackView.repaintViews();
            }
          });
        }

        items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
        return items;
      }

      async getHeader() {
        if (typeof this.featureSource.getHeader === "function") {
          this.header = await this.featureSource.getHeader();
        }

        return this.header;
      } // TODO: refactor to igvUtils.js


      getScaleFactor(min, max, height, logScale) {
        const scale = logScale ? height / (Math.log10(max + 1) - (min <= 0 ? 0 : Math.log10(min + 1))) : height / (max - min);
        return scale;
      }

      computeYPixelValue(yValue, yScaleFactor) {
        return (this.flipAxis ? yValue - this.dataRange.min : this.dataRange.max - yValue) * yScaleFactor;
      }

      computeYPixelValueInLogScale(yValue, yScaleFactor) {
        let maxValue = this.dataRange.max;
        let minValue = this.dataRange.min;
        if (maxValue <= 0) return 0; // TODO:

        if (minValue <= -1) minValue = 0;
        minValue = minValue <= 0 ? 0 : Math.log10(minValue + 1);
        maxValue = Math.log10(maxValue + 1);
        yValue = Math.log10(yValue + 1);
        return (this.flipAxis ? yValue - minValue : maxValue - yValue) * yScaleFactor;
      }

      draw(options) {
        const features = options.features;
        const ctx = options.context;
        const bpPerPixel = options.bpPerPixel;
        const bpStart = options.bpStart;
        const pixelWidth = options.pixelWidth;
        options.pixelHeight;
        const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
        let lastPixelEnd = -1;
        let lastValue = -1;
        let lastNegValue = 1;
        const posColor = this.color || DEFAULT_COLOR;
        let baselineColor;

        if (typeof posColor === "string" && posColor.startsWith("rgb(")) {
          baselineColor = IGVColor.addAlpha(posColor, 0.1);
        }

        const scaleFactor = this.getScaleFactor(this.dataRange.min, this.dataRange.max, options.pixelHeight, this.logScale);

        const yScale = yValue => this.logScale ? this.computeYPixelValueInLogScale(yValue, scaleFactor) : this.computeYPixelValue(yValue, scaleFactor);

        if (features && features.length > 0) {
          if (this.dataRange.min === undefined) this.dataRange.min = 0; // Max can be less than min if config.min is set but max left to autoscale.   If that's the case there is
          // nothing to paint.

          if (this.dataRange.max > this.dataRange.min) {
            const y0 = yScale(0);

            for (let f of features) {
              if (f.end < bpStart) continue;
              if (f.start > bpEnd) break;
              const x = Math.floor((f.start - bpStart) / bpPerPixel);
              if (isNaN(x)) continue;
              let y = yScale(f.value);
              const rectEnd = Math.ceil((f.end - bpStart) / bpPerPixel);
              const width = Math.max(1, rectEnd - x);
              let c = f.value < 0 && this.altColor ? this.altColor : posColor;
              const color = typeof c === "function" ? c(f.value) : c;

              if (this.graphType === "points") {
                const pointSize = this.config.pointSize || 3;
                const px = x + width / 2;
                IGVGraphics.fillCircle(ctx, px, y, pointSize / 2, {
                  "fillStyle": color,
                  "strokeStyle": color
                });
              } else {
                let height = y - y0;
                const pixelEnd = x + width;

                if (pixelEnd > lastPixelEnd || f.value >= 0 && f.value > lastValue || f.value < 0 && f.value < lastNegValue) {
                  IGVGraphics.fillRect(ctx, x, y0, width, height, {
                    fillStyle: color
                  });
                }

                lastValue = f.value;
                lastPixelEnd = pixelEnd;
              }
            } // If the track includes negative values draw a baseline


            if (this.dataRange.min < 0) {
              const basepx = this.dataRange.max / (this.dataRange.max - this.dataRange.min) * options.pixelHeight;
              IGVGraphics.strokeLine(ctx, 0, basepx, options.pixelWidth, basepx, {
                strokeStyle: baselineColor
              });
            }
          }
        } // Draw guidelines


        if (this.config.hasOwnProperty('guideLines')) {
          for (let line of this.config.guideLines) {
            if (line.hasOwnProperty('color') && line.hasOwnProperty('y') && line.hasOwnProperty('dotted')) {
              let y = yScale(line.y);
              let props = {
                'strokeStyle': line['color'],
                'strokeWidth': 2
              };
              if (line['dotted']) IGVGraphics.dashedLine(options.context, 0, y, options.pixelWidth, y, 5, props);else IGVGraphics.strokeLine(options.context, 0, y, options.pixelWidth, y, props);
            }
          }
        }
      }

      popupData(clickState, features) {
        // We use the featureCache property rather than method to avoid async load.  If the
        // feature is not already loaded this won't work,  but the user wouldn't be mousing over it either.
        features = this.clickedFeatures(clickState, features);

        if (features && features.length > 0) {
          const genomicLocation = clickState.genomicLocation;
          const popupData = []; // Sort features based on distance from click

          features.sort(function (a, b) {
            const distA = Math.abs((a.start + a.end) / 2 - genomicLocation);
            const distB = Math.abs((b.start + b.end) / 2 - genomicLocation);
            return distA - distB;
          }); // Display closest 10

          const displayFeatures = features.length > 10 ? features.slice(0, 10) : features; // Resort in ascending order

          displayFeatures.sort(function (a, b) {
            return a.start - b.start;
          });

          for (let selectedFeature of displayFeatures) {
            if (selectedFeature) {
              if (popupData.length > 0) {
                popupData.push('<hr/>');
              }

              let posString = selectedFeature.end - selectedFeature.start === 1 ? numberFormatter$1(selectedFeature.start + 1) : numberFormatter$1(selectedFeature.start + 1) + "-" + numberFormatter$1(selectedFeature.end);
              popupData.push({
                name: "Position:",
                value: posString
              });
              popupData.push({
                name: "Value:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;",
                value: numberFormatter$1(selectedFeature.value)
              });
            }
          }

          if (displayFeatures.length < features.length) {
            popupData.push("<hr/>...");
          }

          return popupData;
        } else {
          return [];
        }
      }

      get supportsWholeGenome() {
        return !this.config.indexURL && this.config.supportsWholeGenome !== false;
      }
      /**
       * Called when the track is removed.  Do any needed cleanup here
       */


      dispose() {
        this.trackView = undefined;
      }
      /**
       * Return the current state of the track.  Used to create sessions and bookmarks.
       *
       * @returns {*|{}}
       */


      getState() {
        const config = super.getState();
        if (this.flipAxis !== undefined) config.flipAxis = this.flipAxis;
        if (this.logScale !== undefined) config.logScale = this.logScale;
        return config;
      }

    }

    /**
     *
     * @param cs - object containing
     * 1) array of threshold values defining bin boundaries in ascending order
     * 2) array of colors for bins  (length == thresholds.length + 1)
     * @constructor
     */
    function BinnedColorScale(cs) {
      this.thresholds = cs.thresholds;
      this.colors = cs.colors;
    }

    BinnedColorScale.prototype.getColor = function (value) {
      for (let threshold of this.thresholds) {
        if (value < threshold) {
          return this.colors[this.thresholds.indexOf(threshold)];
        }
      }

      return this.colors[this.colors.length - 1];
    };
    /**
     *
     * @param scale - object with the following properties
     *           low
     *           lowR
     *           lowG
     *           lowB
     *           high
     *           highR
     *           highG
     *           highB
     *
     * @constructor
     */


    function GradientColorScale(scale) {
      this.scale = scale;
      this.lowColor = "rgb(" + scale.lowR + "," + scale.lowG + "," + scale.lowB + ")";
      this.highColor = "rgb(" + scale.highR + "," + scale.highG + "," + scale.highB + ")";
      this.diff = scale.high - scale.low;
    }

    GradientColorScale.prototype.getColor = function (value) {
      var scale = this.scale,
          r,
          g,
          b,
          frac;
      if (value <= scale.low) return this.lowColor;else if (value >= scale.high) return this.highColor;
      frac = (value - scale.low) / this.diff;
      r = Math.floor(scale.lowR + frac * (scale.highR - scale.lowR));
      g = Math.floor(scale.lowG + frac * (scale.highG - scale.lowG));
      b = Math.floor(scale.lowB + frac * (scale.highB - scale.lowB));
      return "rgb(" + r + "," + g + "," + b + ")";
    };

    class ConstantColorScale {
      constructor(color) {
        this.color = color;
      }

      getColor() {
        return this.color;
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class SegTrack extends TrackBase {
      constructor(config, browser) {
        super(config, browser);
      }

      init(config) {
        super.init(config);
        this.type = config.type || "seg";
        if (this.type === 'maf') this.type = 'mut';
        this.isLog = config.isLog;
        this.displayMode = config.displayMode || "EXPANDED"; // EXPANDED | SQUISHED

        this.height = config.height || 300;
        this.maxHeight = config.maxHeight || 500;
        this.squishedRowHeight = config.sampleSquishHeight || config.squishedRowHeight || 2;
        this.expandedRowHeight = config.sampleExpandHeight || config.expandedRowHeight || 13;
        this.sampleHeight = this.squishedRowHeight; // Initial value, will get overwritten when rendered

        if (config.color) {
          this.color = config.color;
        } else {
          // Color scales for "seg" (copy number) tracks.
          this.posColorScale = config.posColorScale || new GradientColorScale({
            low: 0.1,
            lowR: 255,
            lowG: 255,
            lowB: 255,
            high: 1.5,
            highR: 255,
            highG: 0,
            highB: 0
          });
          this.negColorScale = config.negColorScale || new GradientColorScale({
            low: -1.5,
            lowR: 0,
            lowG: 0,
            lowB: 255,
            high: -0.1,
            highR: 255,
            highG: 255,
            highB: 255
          }); // Default color table for mutation (mut and maf) tracks

          if (this.type === "mut") {
            this.colorTable = new ColorTable(config.colorTable || MUT_COLORS);
          }
        }

        this.sampleKeys = [];
        this.sampleNames = new Map();

        if (config.samples) {
          // Explicit setting, keys == names
          for (let s of config.samples) {
            this.sampleKeys.push(s);
            this.sampleNames.set(s, s);
          }

          this.explicitSamples = true;
        } //   this.featureSource = config.sourceType === "bigquery" ?
        //       new igv.BigQueryFeatureSource(this.config) :
        // Disable whole genome downsampling unless explicitly.


        const configCopy = Object.assign({}, this.config);
        configCopy.maxWGCount = configCopy.maxWGCount || Number.MAX_SAFE_INTEGER;
        this.featureSource = FeatureSource(configCopy, this.browser.genome);
        this.initialSort = config.sort;
      }

      async postInit() {
        if (typeof this.featureSource.getHeader === "function") {
          this.header = await this.featureSource.getHeader();
        } // Set properties from track line


        if (this.header) {
          this.setTrackProperties(this.header);
        }
      }

      menuItemList() {
        const menuItems = [];
        const lut = {
          "SQUISHED": "Squish",
          "EXPANDED": "Expand",
          "FILL": "Fill"
        };
        menuItems.push('<hr/>');
        menuItems.push("DisplayMode:");
        const displayOptions = this.type === 'seg' ? ["SQUISHED", "EXPANDED", "FILL"] : ["SQUISHED", "EXPANDED"];

        for (let displayMode of displayOptions) {
          const checkBox = createCheckbox$1(lut[displayMode], displayMode === this.displayMode);
          menuItems.push({
            object: $$1(checkBox),
            click: () => {
              this.displayMode = displayMode;
              this.config.displayMode = displayMode;
              this.trackView.checkContentHeight();
              this.trackView.repaintViews();
              this.trackView.moveScroller(this.trackView.sampleNameViewport.trackScrollDelta);
            }
          });
        }

        return menuItems;
      }

      hasSamples() {
        return true; // SEG, MUT, and MAF tracks have samples by definition
      }

      getSamples() {
        return {
          names: this.sampleKeys.map(key => this.sampleNames.get(key)),
          height: this.sampleHeight,
          yOffset: 0
        };
      }

      async getFeatures(chr, start, end) {
        const features = await this.featureSource.getFeatures({
          chr,
          start,
          end
        });

        if (this.initialSort) {
          const sort = this.initialSort;
          this.sortSamples(sort.chr, sort.start, sort.end, sort.direction, features);
          this.initialSort = undefined; // Sample order is sorted,
        }

        return features;
      }

      draw(_ref) {
        let {
          context,
          renderSVG,
          pixelTop,
          pixelWidth,
          pixelHeight,
          features,
          bpPerPixel,
          bpStart
        } = _ref;
        IGVGraphics.fillRect(context, 0, 0, pixelWidth, pixelHeight, {
          'fillStyle': "rgb(255, 255, 255)"
        });

        if (features && features.length > 0) {
          this.checkForLog(features); // New segments could conceivably add new samples

          this.updateSampleKeys(features); // Create a map for fast id -> row lookup

          const samples = {};
          this.sampleKeys.forEach(function (id, index) {
            samples[id] = index;
          });
          let border;

          switch (this.displayMode) {
            case "FILL":
              this.sampleHeight = pixelHeight / this.sampleKeys.length;
              border = 0;
              break;

            case "SQUISHED":
              this.sampleHeight = this.squishedRowHeight;
              border = 0;
              break;

            default:
              // EXPANDED
              this.sampleHeight = this.expandedRowHeight;
              border = 1;
          }

          const rowHeight = this.sampleHeight; // this.featureMap = new Map()

          for (let segment of features) {
            segment.pixelRect = undefined; // !important, reset this in case segment is not drawn
          }

          const pixelBottom = pixelTop + pixelHeight;
          const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;
          const xScale = bpPerPixel;
          this.sampleYStart = undefined;

          for (let f of features) {
            if (f.end < bpStart) continue;
            if (f.start > bpEnd) break;
            const sampleKey = f.sampleKey || f.sample;
            f.row = samples[sampleKey];
            const y = f.row * rowHeight + border;

            if (undefined === this.sampleYStart) {
              this.sampleYStart = y;
            }

            const bottom = y + rowHeight;

            if (bottom < pixelTop || y > pixelBottom) {
              continue;
            }

            const segmentStart = Math.max(f.start, bpStart); // const segmentStart = segment.start;

            let x = Math.round((segmentStart - bpStart) / xScale);
            const segmentEnd = Math.min(f.end, bpEnd); // const segmentEnd = segment.end;

            const x1 = Math.round((segmentEnd - bpStart) / xScale);
            let w = Math.max(1, x1 - x);
            let color;

            if (this.color) {
              if (typeof this.color === "function") {
                color = this.color(f);
              } else {
                color = this.color;
              }
            } else if (this.colorTable) {
              color = this.colorTable.getColor(f.value.toLowerCase());
            }

            let h;

            if ("mut" === this.type) {
              h = rowHeight - 2 * border;

              if (w < 3) {
                w = 3;
                x -= 1;
              }
            } else {
              // Assume seg track
              let value = f.value;

              if (!this.isLog) {
                value = IGVMath.log2(value / 2);
              }

              if (value < -0.1) {
                color = this.negColorScale.getColor(value);
              } else if (value > 0.1) {
                color = this.posColorScale.getColor(value);
              } else {
                color = "white";
              }

              let sh = rowHeight;

              if (rowHeight < 0.25) {
                const f = 0.1 + 2 * Math.abs(value);
                sh = Math.min(1, f * rowHeight);
              }

              h = sh - 2 * border;
            }

            f.pixelRect = {
              x,
              y,
              w,
              h
            }; // Use for diagnostic rendering
            // context.fillStyle = randomRGB(180, 240)
            // context.fillStyle = randomGrey(200, 255)

            context.fillStyle = color;
            context.fillRect(x, y, w, h);
          }
        }
      }

      checkForLog(features) {
        if (this.isLog === undefined) {
          this.isLog = false;

          for (let feature of features) {
            if (feature.value < 0) {
              this.isLog = true;
              return;
            }
          }
        }
      }
      /**
       * Optional method to compute pixel height to accomodate the list of features.  The implementation below
       * has side effects (modifiying the samples hash).  This is unfortunate, but harmless.
       *
       * Note displayMode "FILL" is handled by the viewport
       *
       * @param features
       * @returns {number}
       */


      computePixelHeight(features) {
        if (!features) return 0;
        const sampleHeight = "SQUISHED" === this.displayMode ? this.squishedRowHeight : this.expandedRowHeight;
        this.updateSampleKeys(features);
        return this.sampleKeys.length * sampleHeight;
      }
      /**
       * Sort samples by the average value over the genomic range in the direction indicated (1 = ascending, -1 descending)
       */


      async sortSamples(chr, start, end, direction, featureList) {
        if (!featureList) {
          featureList = await this.featureSource.getFeatures({
            chr,
            start,
            end
          });
        }

        if (!featureList) return;
        this.updateSampleKeys(featureList);
        const scores = {};
        const d2 = direction === "ASC" ? 1 : -1;

        const sortSeg = () => {
          // Compute weighted average score for each sample
          const bpLength = end - start + 1;

          for (let segment of featureList) {
            if (segment.end < start) continue;
            if (segment.start > end) break;
            const min = Math.max(start, segment.start);
            const max = Math.min(end, segment.end);
            const f = (max - min) / bpLength;
            const sampleKey = segment.sampleKey || segment.sample;
            const s = scores[sampleKey] || 0;
            scores[sampleKey] = s + f * segment.value;
          } // Now sort sample names by score


          this.sampleKeys.sort(function (a, b) {
            let s1 = scores[a];
            let s2 = scores[b];
            if (!s1) s1 = d2 * Number.MAX_VALUE;
            if (!s2) s2 = d2 * Number.MAX_VALUE;
            if (s1 === s2) return 0;else if (s1 > s2) return d2;else return d2 * -1;
          });
        };

        const sortMut = () => {
          // Compute weighted average score for each sample
          for (let segment of featureList) {
            if (segment.end < start) continue;
            if (segment.start > end) break;
            const sampleKey = segment.sampleKey || segment.sample;

            if (!scores.hasOwnProperty(sampleKey) || segment.value.localeCompare(scores[sampleKey]) > 0) {
              scores[sampleKey] = segment.value;
            }
          } // Now sort sample names by score


          this.sampleKeys.sort(function (a, b) {
            let sa = scores[a] || "";
            let sb = scores[b] || "";
            return d2 * sa.localeCompare(sb);
          });
        };

        if ("mut" === this.type) {
          sortMut();
        } else {
          sortSeg();
        }

        this.trackView.repaintViews();
      }

      clickedFeatures(clickState, features) {
        const allFeatures = super.clickedFeatures(clickState, features);
        const y = clickState.y;
        return allFeatures.filter(function (feature) {
          const rect = feature.pixelRect;
          return rect && y >= rect.y && y <= rect.y + rect.h;
        });
      }

      popupData(clickState, featureList) {
        featureList = this.clickedFeatures(clickState);
        const items = [];

        for (let feature of featureList) {
          // Double line divider between features
          if (items.length > 0) {
            items.push('<hr/>');
            items.push('<hr/>');
          } // hack for whole genome features, which save the original feature as "_f"


          const f = feature._f || feature;
          const data = typeof f.popupData === 'function' ? f.popupData(this.type, this.browser.genome.id) : this.extractPopupData(f);
          Array.prototype.push.apply(items, data);
        }

        return items;
      }

      contextMenuItemList(clickState) {
        const referenceFrame = clickState.viewport.referenceFrame;
        const genomicLocation = clickState.genomicLocation; // Define a region 5 "pixels" wide in genomic coordinates

        const sortDirection = this.config.sort ? this.config.sort.direction === "ASC" ? "DESC" : "ASC" : // Toggle from previous sort
        "DESC";
        const bpWidth = referenceFrame.toBP(2.5);

        const sortHandler = sort => {
          const viewport = clickState.viewport;
          const features = viewport.cachedFeatures;
          this.sortSamples(sort.chr, sort.start, sort.end, sort.direction, features);
        };

        const sortLabel = this.type === 'seg' ? 'Sort by value' : 'Sort by type';
        return [{
          label: sortLabel,
          click: e => {
            const sort = {
              direction: sortDirection,
              chr: clickState.viewport.referenceFrame.chr,
              start: genomicLocation - bpWidth,
              end: genomicLocation + bpWidth
            };
            sortHandler(sort);
            this.config.sort = sort;
          }
        }];
      }

      get supportsWholeGenome() {
        return (this.config.indexed === false || !this.config.indexURL) && this.config.supportsWholeGenome !== false;
      }

      updateSampleKeys(featureList) {
        if (this.explicitSamples) return;

        for (let feature of featureList) {
          const sampleKey = feature.sampleKey || feature.sample;

          if (!this.sampleNames.has(sampleKey)) {
            this.sampleNames.set(sampleKey, feature.sample);
            this.sampleKeys.push(sampleKey);
          }
        }
      }

    } // Mut and MAF file default color table


    const MUT_COLORS = {
      "indel": "rgb(0,200,0)",
      "targeted region": "rgb(236,155,43)",
      "truncating": "rgb(	150,0,0)",
      "non-coding transcript": "rgb(0,0,150)",
      // Colors from https://www.nature.com/articles/nature11404
      "synonymous": "rgb(109,165,95)",
      "silent": "rgb(109,135,80)",
      "missense_mutation": "rgb(72,130,187)",
      "missense": "rgb(72,130,187)",
      "splice site": "rgb(143,83,155)",
      "splice_region": "rgb(143,83,155)",
      "nonsense": "rgb(216, 57,81)",
      "nonsense_mutation": "rgb(216, 57,81)",
      "frame_shift_del": "rgb(226,135,65)",
      "frame_shift_ins": "rgb(226,135,65)",
      "in_frame_del": "rgb(247,235,94)",
      "in_frame_ins": "rgb(247,235,94)",
      "*other*": "rgb(159,91,50)" //
      // 3'Flank
      // 3'UTR
      // 5'Flank
      // 5'UTR
      // Frame_Shift_Del
      // Frame_Shift_Ins
      // IGR
      // In_Frame_Del
      // In_Frame_Ins
      // Intron
      // Missense_Mutation
      // Nonsense_Mutation
      // Nonstop_Mutation
      // RNA
      // Silent
      // Splice_Region
      // Splice_Site
      // Translation_Start_Site
      // Variant_Classification

    };

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    /**
     * Represents 2 or more wig tracks overlaid on a common viewport.
     */

    class MergedTrack extends TrackBase {
      constructor(config, browser) {
        super(config, browser);
        this.type = "merged";
        this.featureType = 'numeric';
        this.paintAxis = paintAxis;
      }

      init(config) {
        if (!config.tracks) {
          throw Error("Error: no tracks defined for merged track" + config);
        }

        super.init(config);
      }

      async postInit() {
        this.tracks = [];
        const p = [];

        for (let tconf of this.config.tracks) {
          tconf.isMergedTrack = true;
          const t = await this.browser.createTrack(tconf);

          if (t) {
            t.autoscale = false; // Scaling done from merged track

            this.tracks.push(t);
          } else {
            console.warn("Could not create track " + tconf);
          }

          if (typeof t.postInit === 'function') {
            p.push(t.postInit());
          }
        }

        this.flipAxis = this.config.flipAxis ? this.config.flipAxis : false;
        this.logScale = this.config.logScale ? this.config.logScale : false;
        this.autoscale = this.config.autoscale || this.config.max === undefined;

        if (!this.autoscale) {
          this.dataRange = {
            min: this.config.min || 0,
            max: this.config.max
          };
        }

        for (let t of this.tracks) {
          t.autoscale = false;
          t.dataRange = this.dataRange;
        }

        this.height = this.config.height || 50;
        return Promise.all(p);
      }

      get height() {
        return this._height;
      }

      set height(h) {
        this._height = h;

        if (this.tracks) {
          for (let t of this.tracks) {
            t.height = h;
            t.config.height = h;
          }
        }
      }

      menuItemList() {
        let items = [];

        if (this.flipAxis !== undefined) {
          items.push({
            label: "Flip y-axis",
            click: () => {
              this.flipAxis = !this.flipAxis;
              this.trackView.repaintViews();
            }
          });
        }

        items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
        return items;
      }

      async getFeatures(chr, bpStart, bpEnd, bpPerPixel) {
        const promises = this.tracks.map(t => t.getFeatures(chr, bpStart, bpEnd, bpPerPixel));
        return Promise.all(promises);
      }

      draw(options) {
        const mergedFeatures = options.features; // Array of feature arrays, 1 for each track

        if (this.autoscale) {
          this.dataRange = autoscale(options.referenceFrame.chr, mergedFeatures);
        }

        for (let i = 0, len = this.tracks.length; i < len; i++) {
          const trackOptions = Object.assign({}, options);
          trackOptions.features = mergedFeatures[i];
          this.tracks[i].dataRange = this.dataRange;
          this.tracks[i].flipAxis = this.flipAxis;
          this.tracks[i].logScale = this.logScale;
          this.tracks[i].graphType = this.graphType;
          this.tracks[i].draw(trackOptions);
        }
      }

      popupData(clickState, features) {
        const featuresArray = features || clickState.viewport.cachedFeatures;

        if (featuresArray && featuresArray.length === this.tracks.length) {
          // Array of feature arrays, 1 for each track
          const popupData = [];

          for (let i = 0; i < this.tracks.length; i++) {
            if (i > 0) popupData.push('<hr/>');
            popupData.push(`<div style=background-color:rgb(245,245,245);border-bottom-style:dashed;border-bottom-width:1px;padding-bottom:5px;padding-top:10px;font-weight:bold;font-size:larger >${this.tracks[i].name}</div>`);
            const trackPopupData = this.tracks[i].popupData(clickState, featuresArray[i]);
            popupData.push(...trackPopupData);
          }

          return popupData;
        }
      }

      get supportsWholeGenome() {
        return this.tracks.every(track => track.supportsWholeGenome());
      }

    }

    function autoscale(chr, featureArrays) {
      let min = 0;
      let max = -Number.MAX_VALUE;

      for (let features of featureArrays) {
        for (let f of features) {
          if (typeof f.value !== 'undefined' && !Number.isNaN(f.value)) {
            min = Math.min(min, f.value);
            max = Math.max(max, f.value);
          }
        }
      }

      return {
        min: min,
        max: max
      };
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2018 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    function getArcType(config) {
      if (!config.arcType) {
        return "nested";
      }

      switch (config.arcType) {
        case "chiapet":
          return "inView";

        case "chiapetoutbound":
          return "partialInView";

        default:
          return config.arcType;
      }
    }

    class InteractionTrack extends TrackBase {
      constructor(config, browser) {
        super(config, browser);
      }

      init(config) {
        super.init(config);
        this.theta = config.theta || Math.PI / 4;
        this.sinTheta = Math.sin(this.theta);
        this.cosTheta = Math.cos(this.theta);
        this.height = config.height || 250;
        this.arcType = getArcType(config); // nested | proportional | inView | partialInView

        this.arcOrientation = config.arcOrientation === undefined ? true : config.arcOrientation; // true for up, false for down

        this.showBlocks = config.showBlocks === undefined ? true : config.showBlocks;
        this.blockHeight = config.blockHeight || 3;
        this.thickness = config.thickness || 1;
        this.color = config.color || "rgb(180,25,137)";
        this.alpha = config.alpha || 0.02; // was: 0.15

        this.painter = {
          flipAxis: !this.arcOrientation,
          dataRange: this.dataRange,
          paintAxis: paintAxis
        };

        if (config.valueColumn) {
          this.valueColumn = config.valueColumn;
          this.hasValue = true;
        } else if (config.useScore) {
          this.hasValue = true;
          this.valueColumn = "score";
        }

        this.logScale = config.logScale !== false; // i.e. defaul to true (undefined => true)

        if (config.max) {
          this.dataRange = {
            min: config.min || 0,
            max: config.max
          };
          this.autoscale = false;
        } else {
          this.autoscale = true;
        } // Create the FeatureSource and override the default whole genome method


        this.featureSource = FeatureSource(config, this.browser.genome);
        this.featureSource.getWGFeatures = getWGFeatures;
      }

      async postInit() {
        if (typeof this.featureSource.getHeader === "function") {
          this.header = await this.featureSource.getHeader();
        } // Set properties from track line


        if (this.header) {
          this.setTrackProperties(this.header);
        }

        if (this.visibilityWindow === undefined && typeof this.featureSource.defaultVisibilityWindow === 'function') {
          this.visibilityWindow = await this.featureSource.defaultVisibilityWindow();
          this.featureSource.visibilityWindow = this.visibilityWindow; // <- this looks odd
        }

        return this;
      }

      get supportsWholeGenome() {
        return true;
      }

      async getFeatures(chr, start, end) {
        const visibilityWindow = this.visibilityWindow;
        const features = await this.featureSource.getFeatures({
          chr,
          start,
          end,
          visibilityWindow
        }); // Check for score or value

        if (this.hasValue === undefined && features && features.length > 0) {
          this.hasValue = features[0].score !== undefined;
        }

        return features;
      }

      draw(options) {
        if (this.arcType === "proportional") {
          this.drawProportional(options);
        } else if (this.arcType === "inView" || this.arcType === "partialInView") {
          this.drawProportional(options);
        } else {
          this.drawNested(options);
        }
      }

      drawNested(options) {
        const ctx = options.context;
        const pixelWidth = options.pixelWidth;
        const pixelHeight = options.pixelHeight;
        const viewportWidth = options.viewportWidth;
        const bpPerPixel = options.bpPerPixel;
        const bpStart = options.bpStart;
        const xScale = bpPerPixel;
        IGVGraphics.fillRect(ctx, 0, options.pixelTop, pixelWidth, pixelHeight, {
          'fillStyle': "rgb(255, 255, 255)"
        });
        const featureList = options.features;

        if (featureList) {
          // Autoscale theta
          autoscaleNested.call(this);
          const y = this.arcOrientation ? options.pixelHeight : 0;
          const direction = this.arcOrientation;
          ctx.font = "8px sans-serif";
          ctx.textAlign = "center";

          for (let feature of featureList) {
            // Reset transient property drawState.  An undefined value => feature has not been drawn.
            feature.drawState = undefined;
            let color = feature.color || this.color;

            if (color && this.config.useScore) {
              color = getAlphaColor(color, scoreShade(feature.score));
            }

            ctx.lineWidth = feature.thickness || this.thickness || 1;

            if (feature.chr1 === feature.chr2 || feature.chr === 'all') {
              const {
                m1,
                m2
              } = getMidpoints(feature, this.browser.genome);
              let pixelStart = Math.round((m1 - bpStart) / xScale);
              let pixelEnd = Math.round((m2 - bpStart) / xScale);
              if (pixelEnd < 0 || pixelStart > pixelWidth) continue;
              let w = pixelEnd - pixelStart;

              if (w < 3) {
                w = 3;
                pixelStart--;
              }

              const a = w / 2;
              const r = a / this.sinTheta;
              const b = this.cosTheta * r;
              const xc = pixelStart + a;
              let yc, startAngle, endAngle;

              if (direction) {
                // UP
                yc = this.height + b;
                startAngle = Math.PI + Math.PI / 2 - this.theta;
                endAngle = Math.PI + Math.PI / 2 + this.theta;
              } else {
                // DOWN
                yc = -b;
                startAngle = Math.PI / 2 - this.theta;
                endAngle = Math.PI / 2 + this.theta;
              }

              if (this.showBlocks && feature.chr !== 'all') {
                const s1 = (feature.start1 - bpStart) / xScale;
                const e1 = (feature.end1 - bpStart) / xScale;
                const s2 = (feature.start2 - bpStart) / xScale;
                const e2 = (feature.end2 - bpStart) / xScale;
                const hb = this.arcOrientation ? -this.blockHeight : this.blockHeight;
                ctx.fillRect(s1, y, e1 - s1, hb);
                ctx.fillRect(s2, y, e2 - s2, hb);
              } // Alpha shade (de-emphasize) arcs that extend beyond viewport, unless alpha shading is used for score.


              if (color && !this.config.useScore && w > viewportWidth) {
                color = getAlphaColor(color, this.alpha);
              }

              ctx.strokeStyle = color;
              ctx.fillStyle = color;
              ctx.beginPath();
              ctx.arc(xc, yc, r, startAngle, endAngle, false);
              ctx.stroke();
              feature.drawState = {
                xc,
                yc,
                r
              };
            } else {
              let pixelStart = Math.round((feature.start - bpStart) / xScale);
              let pixelEnd = Math.round((feature.end - bpStart) / xScale);
              if (pixelEnd < 0 || pixelStart > pixelWidth) continue;
              let w = pixelEnd - pixelStart;

              if (w < 3) {
                w = 3;
                pixelStart--;
              }

              const otherChr = feature.chr === feature.chr1 ? feature.chr2 : feature.chr1;
              ctx.strokeStyle = color; // get a sense of trans "spread"

              ctx.fillStyle = getAlphaColor(getChrColor(otherChr), 0.5); // ctx.fillStyle = color

              if (direction) {
                // UP
                ctx.fillRect(pixelStart, this.height / 2, w, this.height / 2);
                ctx.fillText(otherChr, pixelStart + w / 2, this.height / 2 - 5);
                feature.drawState = {
                  x: pixelStart,
                  y: this.height / 2,
                  w: w,
                  h: this.height / 2
                };
              } else {
                ctx.fillRect(pixelStart, 0, w, this.height / 2);
                ctx.fillText(otherChr, pixelStart + w / 2, this.height / 2 + 13);
                feature.drawState = {
                  x: pixelStart,
                  y: 0,
                  w: w,
                  h: this.height / 2
                };
              }
            }
          }
        }

        function autoscaleNested() {
          let max = 0;

          for (let feature of featureList) {
            let pixelStart = (feature.start - bpStart) / xScale;
            let pixelEnd = (feature.end - bpStart) / xScale;

            if (pixelEnd >= 0 && pixelStart <= pixelWidth) {
              max = Math.max(max, pixelEnd - pixelStart);
            }
          }

          let a = Math.min(viewportWidth, max) / 2;

          if (max > 0) {
            let coa = (pixelHeight - 10) / a;
            this.theta = estimateTheta(coa);
            this.sinTheta = Math.sin(this.theta);
            this.cosTheta = Math.cos(this.theta);
          }
        }
      }

      getScaleFactor(min, max, height, logScale) {
        const scale = logScale ? height / (Math.log10(max + 1) - (min <= 0 ? 0 : Math.log10(min + 1))) : height / (max - min);
        return scale;
      }

      drawProportional(options) {
        const ctx = options.context;
        const pixelWidth = options.pixelWidth;
        const pixelHeight = options.pixelHeight;
        const bpPerPixel = options.bpPerPixel;
        const bpStart = options.bpStart;
        const xScale = bpPerPixel;
        const refStart = options.referenceFrame.start;
        const refEnd = options.referenceFrame.end;
        IGVGraphics.fillRect(ctx, 0, options.pixelTop, pixelWidth, pixelHeight, {
          'fillStyle': "rgb(255, 255, 255)"
        });
        const featureList = options.features;

        if (featureList && featureList.length > 0) {
          // we use the min as a filter but not moving the axis

          const effectiveMin = 0;
          const yScale = this.getScaleFactor(effectiveMin, this.dataRange.max, options.pixelHeight - 1, this.logScale);
          const y = this.arcOrientation ? options.pixelHeight : 0;

          for (let feature of featureList) {
            // Reset transient property drawState.  An undefined value => feature has not been drawn.
            feature.drawState = undefined;
            const value = this.valueColumn ? feature[this.valueColumn] : feature.score;
            if (value === undefined || Number.isNaN(value)) continue;
            const radiusY = Math.round((this.logScale ? Math.log10(value + 1) : value) * yScale);

            if (feature.chr1 === feature.chr2 || feature.chr === 'all') {
              const {
                m1,
                m2
              } = getMidpoints(feature, this.browser.genome);
              let pixelStart = Math.round((m1 - bpStart) / xScale);
              let pixelEnd = Math.round((m2 - bpStart) / xScale);
              let w = pixelEnd - pixelStart;

              if (w < 3) {
                w = 3;
                pixelStart--;
              } // Various filters


              if (value < this.dataRange.min || value > this.dataRange.max) continue;

              if ("proportional" !== this.arcType) {
                const showOutbound = this.arcType === "partialInView";
                const within = m1 >= refStart && m2 <= refEnd;
                let outBound = false;
                let inBound = false;

                if (!within && showOutbound) {
                  outBound = refStart <= m1 && m1 <= refEnd;
                  if (!outBound) inBound = refStart <= m2 && m2 <= refEnd;
                }

                if (!(within || outBound || inBound)) continue;
              }

              const radiusX = w / 2;
              const xc = pixelStart + w / 2;
              feature.drawState = {
                xc,
                yc: y,
                radiusX,
                radiusY
              }; // const arcKey = ((pixelStart << 16) | pixelEnd)
              // let arc = arcCaches.get(arcKey)
              // if (arc !== undefined) {
              //     if (arc.has(radiusY)) {
              //         continue
              //     }
              //     arc.add(radiusY)
              // } else {
              //     let arcHeights = new Set()
              //     arcHeights.add(radiusY)
              //     arcCaches.set(arcKey, arcHeights)
              // }

              const counterClockwise = this.arcOrientation ? true : false;
              const color = feature.color || this.color;
              ctx.strokeStyle = color;
              ctx.lineWidth = feature.thickness || this.thickness || 1;

              if (true === ctx.isSVG) {
                ctx.strokeEllipse(xc, y, radiusX, radiusY, 0, 0, Math.PI, counterClockwise);
              } else {
                ctx.beginPath();
                ctx.ellipse(xc, y, radiusX, radiusY, 0, 0, Math.PI, counterClockwise);
                ctx.stroke();
              }

              if (this.alpha) {
                ctx.fillStyle = getAlphaColor(color, this.alpha);

                if (true === ctx.isSVG) {
                  ctx.fillEllipse(xc, y, radiusX, radiusY, 0, 0, Math.PI, counterClockwise);
                } else {
                  ctx.fill();
                }
              }

              if (this.showBlocks && feature.chr !== 'all') {
                ctx.fillStyle = color;
                const s1 = (feature.start1 - bpStart) / xScale;
                const e1 = (feature.end1 - bpStart) / xScale;
                const s2 = (feature.start2 - bpStart) / xScale;
                const e2 = (feature.end2 - bpStart) / xScale;
                const hb = this.arcOrientation ? -this.blockHeight : this.blockHeight;
                ctx.fillRect(s1, y, e1 - s1, hb);
                ctx.fillRect(s2, y, e2 - s2, hb);
              }
            } else {
              // Inter chromosome
              let pixelStart = Math.round((feature.start - bpStart) / xScale);
              let pixelEnd = Math.round((feature.end - bpStart) / xScale);
              if (pixelEnd < 0 || pixelStart > pixelWidth || value < this.dataRange.min || value > this.dataRange.max) continue;
              const h = Math.min(radiusY, this.height - 13); // Leave room for text

              let w = pixelEnd - pixelStart;

              if (w < 3) {
                w = 3;
                pixelStart--;
              }

              const otherChr = feature.chr === feature.chr1 ? feature.chr2 : feature.chr1;
              ctx.font = "8px sans-serif";
              ctx.textAlign = "center"; // get a sense of trans "spread"

              ctx.fillStyle = getAlphaColor(getChrColor(otherChr), 0.5);

              if (this.arcOrientation) {
                // UP
                const y = this.height - h;
                ctx.fillRect(pixelStart, y, w, h);
                ctx.fillText(otherChr, pixelStart + w / 2, y - 5);
                feature.drawState = {
                  x: pixelStart,
                  y,
                  w,
                  h
                };
              } else {
                ctx.fillRect(pixelStart, 0, w, h);
                ctx.fillText(otherChr, pixelStart + w / 2, h + 13);
                feature.drawState = {
                  x: pixelStart,
                  y: 0,
                  w,
                  h
                };
              }
            }
          }
        }
      }

      clearAxis(ctx, pixelWidth, pixelHeight) {
        IGVGraphics.fillRect(ctx, 0, 0, pixelWidth, pixelHeight, {
          'fillStyle': "rgb(255, 255, 255)"
        });
      }

      paintAxis(ctx, pixelWidth, pixelHeight) {
        // dataRane is interpreted differently for interactino tracks -- all arcs are drawn from "zero", irrespective of dataRange.min
        const axisRange = {
          min: 0,
          max: this.dataRange.max
        };

        if (this.arcType === "proportional") {
          this.painter.flipAxis = !this.arcOrientation;
          this.painter.dataRange = axisRange;
          this.painter.paintAxis(ctx, pixelWidth, pixelHeight);
        } else if (this.arcType === "inView" || this.arcType === "partialInView") {
          this.painter.flipAxis = !this.arcOrientation;
          this.painter.dataRange = axisRange;
          this.painter.paintAxis(ctx, pixelWidth, pixelHeight);
        } else {
          this.clearAxis(ctx, pixelWidth, pixelHeight);
        }
      }

      menuItemList() {
        let items = [{
          name: "Set track color",
          click: () => {
            this.trackView.presentColorPicker();
          }
        }, '<hr/>'];

        if (this.hasValue) {
          const lut = {
            "nested": "Nested",
            "proportional": "Proportional - All",
            "inView": "Proportional - Both Ends in View",
            "partialInView": "Proportional - One End in View"
          };
          items.push("<b>Arc Type</b>");

          for (let arcType of ["nested", "proportional", "inView", "partialInView"]) {
            items.push({
              object: $$1(createCheckbox$1(lut[arcType], arcType === this.arcType)),
              click: () => {
                this.arcType = arcType;
                this.trackView.repaintViews();
              }
            });
          }
        }

        items.push("<hr/>");
        items.push({
          name: "Toggle arc direction",
          click: () => {
            this.arcOrientation = !this.arcOrientation;
            this.trackView.repaintViews();
          }
        });
        items.push({
          name: this.showBlocks ? "Hide Blocks" : "Show Blocks",
          click: () => {
            this.showBlocks = !this.showBlocks;
            this.trackView.repaintViews();
          }
        });

        if (this.arcType === "proportional" || this.arcType === "inView" || this.arcType === "partialInView") {
          // MenuUtils.numericDataMenuItems(this.trackView).forEach(item => items.push(item))
          items = items.concat(MenuUtils.numericDataMenuItems(this.trackView));
        }

        if (this.browser.circularView) {
          items.push('<hr/>');
          items.push({
            label: 'Add interactions to circular view',
            click: () => {
              for (let viewport of this.trackView.viewports) {
                this.addChordsForViewport(viewport.referenceFrame);
              }
            }
          });
        }

        return items;
      }

      contextMenuItemList(clickState) {
        // Experimental JBrowse feature
        if (this.browser.circularView) {
          const viewport = clickState.viewport;
          const list = [];
          list.push({
            label: 'Add interactions to circular view',
            click: () => {
              const refFrame = viewport.referenceFrame; // first pass: to get all the relevant features

              this.addChordsForViewport(refFrame);
            }
          });
          list.push('<hr/>');
          return list;
        }
      }
      /**
       * Add chords to the circular view for the given viewport, represented by its reference frame
       * @param refFrame
       */


      addChordsForViewport(refFrame) {
        const cachedFeatures = "all" === refFrame.chr ? this.featureSource.getAllFeatures() : this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end); // inView features are simply features that have been drawn, i.e. have a drawState

        const inView = cachedFeatures.filter(f => f.drawState);
        if (inView.length === 0) return;
        const chords = makeBedPEChords(inView);
        sendChords(chords, this, refFrame, 0.5); //
        //
        // // for filtered set, distinguishing the chromosomes is more critical than tracks
        // const chordSetColor = IGVColor.addAlpha("all" === refFrame.chr ? this.color : getChrColor(refFrame.chr), 0.5)
        // const trackColor = IGVColor.addAlpha(this.color, 0.5)
        //
        // // name the chord set to include locus and filtering information
        // const encodedName = this.name.replaceAll(' ', '%20')
        // const chordSetName = "all" === refFrame.chr ?
        //     encodedName :
        //     `${encodedName} (${refFrame.chr}:${refFrame.start}-${refFrame.end} ; range:${this.dataRange.min}-${this.dataRange.max})`
        // this.browser.circularView.addChords(chords, {track: chordSetName, color: chordSetColor, trackColor: trackColor})
      }

      doAutoscale(features) {
        // if ("proportional" === this.arcType) {
        let max = 0;

        if (features) {
          for (let f of features) {
            const v = this.valueColumn ? f[this.valueColumn] : f.score;

            if (!Number.isNaN(v)) {
              max = Math.max(max, v);
            }
          }
        }

        return {
          min: 0,
          max: max
        }; // }
      }

      popupData(clickState, features) {
        features = this.clickedFeatures(clickState);
        const data = [];

        for (let feature of features) {
          const f = feature._ || feature; // For "whole genome" features, which keeps a pointer to the original

          data.push({
            name: "Region 1",
            value: positionString(f.chr1, f.start1, f.end1, f.strand1)
          });
          data.push({
            name: "Region 2",
            value: positionString(f.chr2, f.start2, f.end2, f.strand2)
          });

          if (f.name) {
            data.push({
              name: "Name",
              value: f.name
            });
          }

          if (f.value !== undefined) {
            data.push({
              name: "Value",
              value: f.value
            });
          }

          if (f.score !== undefined) {
            data.push({
              name: "Score",
              value: f.score
            });
          }

          if (f.extras && this.header && this.header.columnNames) {
            const columnNames = this.header.columnNames;

            for (let i = 10; i < columnNames.length; i++) {
              if (columnNames[i] === 'info') {
                extractInfoColumn(data, f.extras[i - 10]);
              } else {
                data.push({
                  name: columnNames[i],
                  value: f.extras[i - 10]
                });
              }
            }
          } // For now just return the top hit


          break; //if (data.length > 0) {
          //     data.push("<HR>");
          // }
        }

        return data;
      }

      clickedFeatures(clickState, features) {
        // We use the cached features rather than method to avoid async load.  If the
        // feature is not already loaded this won't work,  but the user wouldn't be mousing over it either.
        const featureList = features || clickState.viewport.cachedFeatures;
        const candidates = [];

        if (featureList) {
          const proportional = this.arcType === "proportional" || this.arcType === "inView" || this.arcType === "partialInView";

          for (let feature of featureList) {
            if (!feature.drawState) continue;

            if (feature.chr1 === feature.chr2 || feature.chr === 'all') {
              if (proportional) {
                //(x-xc)^2/radiusX^2 + (y-yc)^2/radiusY^2 <= 1
                const {
                  xc,
                  yc,
                  radiusX,
                  radiusY
                } = feature.drawState;
                const dx = clickState.canvasX - xc;
                const dy = clickState.canvasY - yc;
                const score = dx / radiusX * (dx / radiusX) + dy / radiusY * (dy / radiusY);

                if (score <= 1) {
                  candidates.push({
                    score: 1 / score,
                    feature
                  });
                }
              } else {
                const {
                  xc,
                  yc,
                  r
                } = feature.drawState;
                const dx = clickState.canvasX - xc;
                const dy = clickState.canvasY - yc;
                const score = Math.abs(Math.sqrt(dx * dx + dy * dy) - r);

                if (score < 5) {
                  candidates.push({
                    score,
                    feature
                  });
                }
              }
            } else {
              const {
                x,
                y,
                w,
                h
              } = feature.drawState;
              const tolerance = 5;

              if (clickState.canvasX >= x - tolerance && clickState.canvasX <= x + w + tolerance && clickState.canvasY >= y && clickState.canvasY <= y + h) {
                const score = -Math.abs(clickState.canvasX - (x + w / 2));
                candidates.push({
                  score,
                  feature
                });
                break;
              }
            }
          }
        }

        if (candidates.length > 1) {
          candidates.sort((a, b) => a.score - b.score);
        }

        return candidates.map(c => c.feature);
      }
      /**
       * Return the current state of the track.  Used to create sessions and bookmarks.
       *
       * @returns {*|{}}
       */


      getState() {
        const config = super.getState(); // if (this.height !== undefined) config.height = this.height;

        if (this.arcType !== undefined) config.arcType = this.arcType;
        if (this.arcOrientation !== undefined) config.arcOrientation = this.arcOrientation;
        if (this.showBlocks !== undefined) config.showBlocks = this.showBlocks;
        if (this.blockHeight !== undefined) config.blockHeight = this.blockHeight;
        if (this.thickness !== undefined) config.thickness = this.thickness;
        if (this.alpha !== undefined) config.alpha = this.alpha;
        return config;
      }

    }

    function getMidpoints(feature, genome) {
      let m1 = (feature.start1 + feature.end1) / 2;
      let m2 = (feature.start2 + feature.end2) / 2;

      if (feature.chr === 'all') {
        m1 = genome.getGenomeCoordinate(feature.chr1, m1);
        m2 = genome.getGenomeCoordinate(feature.chr2, m2);
      }

      if (m1 > m2) {
        const tmp = m1;
        m1 = m2;
        m2 = tmp;
      }

      return {
        m1,
        m2
      };
    }

    function positionString(chr, start, end, strand) {
      return strand && strand !== '.' ? `${chr}:${numberFormatter$1(start + 1)}-${numberFormatter$1(end)} (${strand})` : `${chr}:${numberFormatter$1(start + 1)}-${numberFormatter$1(end)}`;
    }
    /**
     * Estimate theta given the ratio of track height to 1/2 the feature width (coa).  This relationship is approximately linear.
     */


    function estimateTheta(x) {
      let coa = [0.01570925532366355, 0.15838444032453644, 0.3249196962329063, 0.5095254494944288, 0.7265425280053609, 0.9999999999999999];
      let theta = [0.031415926535897934, 0.3141592653589793, 0.6283185307179586, 0.9424777960769379, 1.2566370614359172, 1.5707963267948966];
      let idx;

      for (idx = 0; idx < coa.length; idx++) {
        if (coa[idx] > x) {
          break;
        }
      }

      let left = idx === 0 ? 0 : coa[idx - 1];
      let right = idx < coa.length ? coa[idx] : 1;
      let r = (x - left) / (right - left);
      let thetaLeft = idx === 0 ? 0 : theta[idx - 1];
      let thetaRight = idx < theta.length ? theta[idx] : Math.PI / 2;
      return thetaLeft + r * (thetaRight - thetaLeft);
    }

    const colorAlphaCache = new Map();

    function getAlphaColor(color, alpha) {
      const key = `${color}_${alpha}`;
      let c = colorAlphaCache.get(key);

      if (!c) {
        c = IGVColor.addAlpha(color, alpha);
        colorAlphaCache.set(key, c);
      }

      return c;
    }
    /**
     * Called in the context of FeatureSource  (i.e. this == the feature source (a TextFeatureSource) for the track
     *
     * @param allFeatures
     * @returns {[]}
     */


    function getWGFeatures(allFeatures) {
      const makeWGFeature = f => {
        const wg = Object.assign({}, f);
        wg.chr = "all";
        wg.start = genome.getGenomeCoordinate(f.chr1, f.start1);
        wg.end = genome.getGenomeCoordinate(f.chr2, f.end2);
        return wg;
      };

      const genome = this.genome; // First pass -- find the max score feature

      let maxScoreFeature;
      let totalFeatureCount = 0;

      for (let c of genome.wgChromosomeNames) {
        let chrFeatures = allFeatures[c];

        if (chrFeatures) {
          for (let f of chrFeatures) {
            if (!f.dup) {
              totalFeatureCount++;

              if (f.score && (!maxScoreFeature || f.score > maxScoreFeature.score)) {
                maxScoreFeature = f;
              }
            }
          }
        }
      }

      const maxCount = this.maxWGCount;
      const nBins = maxScoreFeature && maxScoreFeature.score > 0 && totalFeatureCount > maxCount ? 5 : 1; // TODO make a function of total # of features & maxCount?

      const featuresPerBin = Math.floor(maxCount / nBins);
      const binSize = maxScoreFeature && maxScoreFeature.score > 0 ? Math.log(maxScoreFeature.score) / nBins : Number.MAX_SAFE_INTEGER;
      let binnedFeatures = [];
      let counts = [];

      for (let i = 0; i < nBins; i++) {
        counts.push([0]);
        binnedFeatures.push([]);
      }

      for (let c of genome.wgChromosomeNames) {
        let chrFeatures = allFeatures[c];

        if (chrFeatures) {
          for (let f of chrFeatures) {
            if (!f.dup) {
              const bin = f.score ? Math.min(nBins - 1, Math.floor(Math.log(f.score) / binSize)) : 0;

              if (binnedFeatures[bin].length < featuresPerBin) {
                binnedFeatures[bin].push(makeWGFeature(f));
              } else {
                //Reservoir sampling
                const samplingProb = featuresPerBin / (counts[bin] + 1);

                if (Math.random() < samplingProb) {
                  const idx = Math.floor(Math.random() * (featuresPerBin - 1));
                  binnedFeatures[bin][idx] = makeWGFeature(f);
                }
              }

              counts[bin]++;
            }
          }
        }
      }

      let wgFeatures;

      if (nBins === 1) {
        wgFeatures = binnedFeatures[0];
      } else {
        wgFeatures = [];

        for (let bf of binnedFeatures) {
          for (let f of bf) wgFeatures.push(f);
        } // Keep the feature with max score


        if (maxScoreFeature) {
          wgFeatures.push(makeWGFeature(maxScoreFeature));
        }

        wgFeatures.sort(function (a, b) {
          return a.start - b.start;
        });
        console.log(wgFeatures.length);
      }

      return wgFeatures;
    }
    /**
     * Extract a gff style info column for popup text.  This convention used by 10X for bedpe files
     *     ALLELIC_FRAC=0.0375670840787;BLACK1=.;BLACK2=.;...
     * @param data
     * @param str
     */


    function extractInfoColumn(data, str) {
      const kvs = str.split(';');

      for (let t of kvs) {
        const kv = t.split('=');

        if (kv.length === 2) {
          data.push({
            name: kv[0],
            value: kv[1]
          });
        }
      }
    }

    const isString = isString$3;
    const DEFAULT_VISIBILITY_WINDOW = 1000000;
    const TOP_MARGIN = 10;
    const STANDARD_FIELDS = new Map([["REF", "referenceBases"], ["ALT", "alternateBases"], ["QUAL", "quality"], ["FILTER", "filter"]]);

    class VariantTrack extends TrackBase {
      constructor(config, browser) {
        super(config, browser);
      }

      init(config) {
        super.init(config);
        this.visibilityWindow = config.visibilityWindow;
        this.displayMode = config.displayMode || "EXPANDED"; // COLLAPSED | EXPANDED | SQUISHED

        this.labelDisplayMode = config.labelDisplayMode;
        this.expandedVariantHeight = config.expandedVariantHeight || config.variantHeight || 10;
        this.squishedVariantHeight = config.squishedVariantHeight || 2;
        this.squishedCallHeight = config.squishedCallHeight || 1;
        this.expandedCallHeight = config.expandedCallHeight || 10;
        this.expandedVGap = config.expandedVGap !== undefined ? config.expandedVGap : 2;
        this.squishedVGap = config.squishedVGap !== undefined ? config.squishedVGap : 1;
        this.expandedGroupGap = config.expandedGroupGap || 10;
        this.squishedGroupGap = config.squishedGroupGap || 5;
        this.featureHeight = config.featureHeight || 14;
        this.visibilityWindow = config.visibilityWindow;
        this.featureSource = FeatureSource(config, this.browser.genome);
        this.noGenotypeColor = config.noGenotypeColor || "rgb(200,180,180)";
        this.noCallColor = config.noCallColor || "rgb(225, 225, 225)";
        this.nonRefColor = config.nonRefColor || "rgb(200, 200, 215)";
        this.mixedColor = config.mixedColor || "rgb(200, 220, 200)";
        this.homrefColor = config.homrefColor || "rgb(200, 200, 200)";
        this.homvarColor = config.homvarColor || "rgb(17,248,254)";
        this.hetvarColor = config.hetvarColor || "rgb(34,12,253)";
        this.sortDirection = "ASC";
        this.type = config.type || "variant";
        this.colorBy = config.colorBy; // Can be undefined => default

        this._initColorBy = config.colorBy;

        if (config.colorTable) {
          this.colorTables = new Map();
          this.colorTables.set(config.colorBy, new ColorTable(config.colorTable));
        }

        this.showGenotypes = config.showGenotypes === undefined ? true : config.showGenotypes; // The number of variant rows are computed dynamically, but start with "1" by default

        this.variantRowCount(1);
      }

      async postInit() {
        this.header = await this.getHeader(); // cricital, don't remove'

        if (undefined === this.visibilityWindow && this.config.indexed !== false) {
          const fn = isFile(this.config.url) ? this.config.url.name : this.config.url;

          if (isString(fn) && fn.toLowerCase().includes("gnomad")) {
            this.visibilityWindow = 1000; // these are known to be very dense
          } else if (typeof this.featureSource.defaultVisibilityWindow === 'function') {
            this.visibilityWindow = await this.featureSource.defaultVisibilityWindow();
          } else {
            this.visibilityWindow = DEFAULT_VISIBILITY_WINDOW;
          }
        }

        return this;
      }

      get supportsWholeGenome() {
        return this.config.indexed === false || this.config.supportsWholeGenome === true;
      }

      get color() {
        return this._color;
      }

      set color(c) {
        this._color = c;
        this.colorBy = undefined;
      }

      async getHeader() {
        if (!this.header) {
          if (typeof this.featureSource.getHeader === "function") {
            const header = await this.featureSource.getHeader();

            if (header) {
              this.callSets = header.callSets || [];
            }

            this.header = header;
          }

          this.sampleNames = this.callSets ? this.callSets.map(cs => cs.name) : [];
        }

        return this.header;
      }

      getCallsetsLength() {
        return this.callSets ? this.callSets.length : 0;
      }

      async getFeatures(chr, start, end, bpPerPixel) {
        if (this.header === undefined) {
          this.header = await this.getHeader();
        }

        return this.featureSource.getFeatures({
          chr,
          start,
          end,
          bpPerPixel,
          visibilityWindow: this.visibilityWindow
        });
      }

      hasSamples() {
        return this.getCallsetsLength() > 0;
      }

      getSamples() {
        return {
          yOffset: this.sampleYOffset,
          names: this.sampleNames,
          height: this.sampleHeight
        };
      }
      /**
       * The required height in pixels required for the track content.   This is not the visible track height, which
       * can be smaller (with a scrollbar) or larger.
       *
       * @param features
       * @returns {*}
       */


      computePixelHeight(features) {
        if (!features || features.length == 0) return TOP_MARGIN;
        const nVariantRows = this.displayMode === "COLLAPSED" ? 1 : this.nVariantRows;
        const vGap = this.displayMode === "SQUISHED" ? this.squishedVGap : this.expandedVGap;
        const variantHeight = this.displayMode === "SQUISHED" ? this.squishedVariantHeight : this.expandedVariantHeight;
        const callHeight = this.displayMode === "SQUISHED" ? this.squishedCallHeight : this.expandedCallHeight;
        const nCalls = this.showGenotypes === false ? 0 : this.getCallsetsLength() * nVariantRows;
        const h = TOP_MARGIN + nVariantRows * (variantHeight + vGap);
        return h + vGap + (nCalls + 1) * (callHeight + vGap);
      }

      variantRowCount(count) {
        this.nVariantRows = count;
      }

      draw(_ref) {
        let {
          context,
          pixelWidth,
          pixelHeight,
          bpPerPixel,
          bpStart,
          pixelTop,
          features
        } = _ref;
        IGVGraphics.fillRect(context, 0, pixelTop, pixelWidth, pixelHeight, {
          'fillStyle': "rgb(255, 255, 255)"
        });
        const vGap = "SQUISHED" === this.displayMode ? this.squishedVGap : this.expandedVGap;
        const rowCount = "COLLAPSED" === this.displayMode ? 1 : this.nVariantRows;
        const variantHeight = "SQUISHED" === this.displayMode ? this.squishedVariantHeight : this.expandedVariantHeight;
        this.variantBandHeight = TOP_MARGIN + rowCount * (variantHeight + vGap);
        const callSets = this.callSets;
        const nCalls = this.getCallsetsLength();

        if (callSets && nCalls > 0 && this.showGenotypes !== false) {
          IGVGraphics.strokeLine(context, 0, this.variantBandHeight, pixelWidth, this.variantBandHeight, {
            strokeStyle: 'rgb(224,224,224) '
          });
        }

        if (features) {
          const callHeight = "SQUISHED" === this.displayMode ? this.squishedCallHeight : this.expandedCallHeight;
          const vGap = "SQUISHED" === this.displayMode ? this.squishedVGap : this.expandedVGap;
          const bpEnd = bpStart + pixelWidth * bpPerPixel + 1; // Loop through variants.  A variant == a row in a VCF file

          for (let variant of features) {
            if (variant.end < bpStart) continue;
            if (variant.start > bpEnd) break;
            const variantHeight = "SQUISHED" === this.displayMode ? this.squishedVariantHeight : this.expandedVariantHeight;
            const y = TOP_MARGIN + ("COLLAPSED" === this.displayMode ? 0 : variant.row * (variantHeight + vGap));
            const h = variantHeight; // Compute pixel width.   Minimum width is 3 pixels,  if > 5 pixels create gap between variants

            let x = Math.round((variant.start - bpStart) / bpPerPixel);
            let x1 = Math.round((variant.end - bpStart) / bpPerPixel);
            let w = Math.max(1, x1 - x);

            if (w < 3) {
              w = 3;
              x -= 1;
            } else if (w > 5) {
              x += 1;
              w -= 2;
            }

            context.fillStyle = this.getVariantColor(variant);
            context.fillRect(x, y, w, h);
            variant.pixelRect = {
              x,
              y,
              w,
              h
            }; // Loop though the calls for this variant.  There will potentially be a call for each sample.

            if (nCalls > 0 && this.showGenotypes !== false) {
              const nVariantRows = "COLLAPSED" === this.displayMode ? 1 : this.nVariantRows;
              this.sampleYOffset = this.variantBandHeight + vGap;
              this.sampleHeight = nVariantRows * (callHeight + vGap); // For each sample, there is a call for each variant at this position

              let sampleNumber = 0;

              for (let callSet of callSets) {
                const call = variant.calls[callSet.id];

                if (call) {
                  const row = "COLLAPSED" === this.displayMode ? 0 : variant.row;
                  const py = this.sampleYOffset + sampleNumber * this.sampleHeight + row * (callHeight + vGap);
                  let allVar = true; // until proven otherwise

                  let allRef = true;
                  let noCall = false;

                  if (call.genotype) {
                    for (let g of call.genotype) {
                      if ('.' === g) {
                        noCall = true;
                        break;
                      } else {
                        if (g !== 0) allRef = false;
                        if (g === 0) allVar = false;
                      }
                    }
                  }

                  if (!call.genotype) {
                    context.fillStyle = this.noGenotypeColor;
                  } else if (noCall) {
                    context.fillStyle = this.noCallColor;
                  } else if (allRef) {
                    context.fillStyle = this.homrefColor;
                  } else if (allVar) {
                    context.fillStyle = this.homvarColor;
                  } else {
                    context.fillStyle = this.hetvarColor;
                  }

                  context.fillRect(x, py, w, callHeight);
                  callSet.pixelRect = {
                    x,
                    y: py,
                    w,
                    h: callHeight
                  };
                }

                sampleNumber++;
              }
            }
          }
        } else {
          console.log("No feature list");
        }
      }

      getVariantColor(variant) {
        const v = variant._f || variant;
        let variantColor;

        if (this.colorBy) {
          const colorBy = this.colorBy;
          let value;

          if (v.info.hasOwnProperty(colorBy)) {
            value = v.info[colorBy];
          } else if (STANDARD_FIELDS.has(colorBy)) {
            const key = STANDARD_FIELDS.get(colorBy);
            value = v[key];
          }

          variantColor = this.getVariantColorTable(colorBy).getColor(value);

          if (!variantColor) {
            variantColor = "gray";
          }
        } else if (this._color) {
          variantColor = typeof this._color === "function" ? this._color(variant) : this._color;
        } else if ("NONVARIANT" === v.type) {
          variantColor = this.nonRefColor;
        } else if ("MIXED" === v.type) {
          variantColor = this.mixedColor;
        } else {
          variantColor = this.defaultColor;
        }

        return variantColor;
      }

      clickedFeatures(clickState, features) {
        let featureList = super.clickedFeatures(clickState, features);
        const vGap = this.displayMode === 'EXPANDED' ? this.expandedVGap : this.squishedVGap;
        const callHeight = vGap + ("SQUISHED" === this.displayMode ? this.squishedCallHeight : this.expandedCallHeight); // Find the variant row (i.e. row assigned during feature packing)

        const yOffset = clickState.y;

        if (yOffset <= this.variantBandHeight) {
          // Variant
          const variantHeight = "SQUISHED" === this.displayMode ? this.squishedVariantHeight : this.expandedVariantHeight;
          const variantRow = Math.floor((yOffset - TOP_MARGIN) / (variantHeight + vGap));

          if ("COLLAPSED" !== this.displayMode) {
            featureList = featureList.filter(f => f.row === variantRow);
          }
        } else if (this.callSets) {
          const callSets = this.callSets;
          const sampleY = yOffset - this.variantBandHeight;
          const sampleRow = Math.floor(sampleY / this.sampleHeight);

          if (sampleRow >= 0 && sampleRow < callSets.length) {
            const variantRow = Math.floor((sampleY - sampleRow * this.sampleHeight) / callHeight);
            const variants = "COLLAPSED" === this.displayMode ? featureList : featureList.filter(f => f.row === variantRow);
            const cs = callSets[sampleRow];
            featureList = variants.map(v => {
              const call = v.calls[cs.id];
              expandGenotype(call, v);
              return call;
            });
          }
        }

        return featureList;
      }
      /**
       * Return "popup data" for feature @ genomic location.  Data is an array of key-value pairs
       */


      popupData(clickState, features) {
        const featureList = this.clickedFeatures(clickState, features);
        const genomicLocation = clickState.genomicLocation;
        const genomeID = this.browser.genome.id;
        const sampleInformation = this.browser.sampleInformation;
        let popupData = [];

        for (let v of featureList) {
          const f = v._f || v; // Get real variant from psuedo-variant, e.g. whole genome or SV mate

          if (popupData.length > 0) {
            popupData.push({
              html: '<hr style="border-top-width:2px ;border-color: #c9c3ba" />'
            });
          }

          if (typeof f.popupData === 'function') {
            const v = f.popupData(genomicLocation, genomeID);
            Array.prototype.push.apply(popupData, v);
          } else {
            // Assume this is a call (genotype)
            const call = f;

            if (call.callSetName !== undefined) {
              popupData.push({
                name: 'Name',
                value: call.callSetName
              });
            }

            if (call.genotypeName) {
              popupData.push({
                name: 'Genotype',
                value: call.genotypeName
              });
            }

            if (call.phaseset !== undefined) {
              popupData.push({
                name: 'Phase set',
                value: call.phaseset
              });
            }

            if (call.genotypeLikelihood !== undefined) {
              popupData.push({
                name: 'genotypeLikelihood',
                value: call.genotypeLikelihood.toString()
              });
            }

            if (sampleInformation) {
              var attr = sampleInformation.getAttributes(call.callSetName);

              if (attr) {
                Object.keys(attr).forEach(function (attrName) {
                  var displayText = attrName.replace(/([A-Z])/g, " $1");
                  displayText = displayText.charAt(0).toUpperCase() + displayText.slice(1);
                  popupData.push({
                    name: displayText,
                    value: attr[attrName]
                  });
                });
              }
            }

            var infoKeys = Object.keys(call.info);

            if (infoKeys.length) {
              popupData.push('<hr/>');
            }

            infoKeys.forEach(function (key) {
              popupData.push({
                name: key,
                value: decodeURIComponent(call.info[key])
              });
            });
          }
        }

        return popupData;
      } // VariantTrack.prototype.contextMenuItemList = function (clickState) {
      //
      //     const self = this;
      //     const menuItems = [];
      //
      //     const featureList = this.clickedFeatures(clickState);
      //
      //     if (this.callSets && featureList && featureList.length > 0) {
      //
      //         featureList.forEach(function (variant) {
      //
      //             if ('str' === variant.type) {
      //
      //                 menuItems.push({
      //                     label: 'Sort by allele length',
      //                     click: function () {
      //                         sortCallSetsByAlleleLength(self.callSets, variant, self.sortDirection);
      //                         self.sortDirection = (self.sortDirection === "ASC") ? "DESC" : "ASC";
      //                         self.trackView.repaintViews();
      //                     }
      //                 });
      //
      //             }
      //
      //         });
      //     }
      //
      //
      //     function sortCallSetsByAlleleLength(callSets, variant, direction) {
      //         var d = (direction === "DESC") ? 1 : -1;
      //         Object.keys(callSets).forEach(function (property) {
      //             callSets[property].sort(function (a, b) {
      //                 var aNan = isNaN(variant.calls[a.id].genotype[0]);
      //                 var bNan = isNaN(variant.calls[b.id].genotype[0]);
      //                 if (aNan && bNan) {
      //                     return 0;
      //                 } else if (aNan) {
      //                     return 1;
      //                 } else if (bNan) {
      //                     return -1;
      //                 } else {
      //                     var a0 = getAlleleString(variant.calls[a.id], variant, 0);
      //                     var a1 = getAlleleString(variant.calls[a.id], variant, 1);
      //                     var b0 = getAlleleString(variant.calls[b.id], variant, 0);
      //                     var b1 = getAlleleString(variant.calls[b.id], variant, 1);
      //                     var result = Math.max(b0.length, b1.length) - Math.max(a0.length, a1.length);
      //                     if (result === 0) {
      //                         result = Math.min(b0.length, b1.length) - Math.min(a0.length, a1.length);
      //                     }
      //                     return d * result;
      //                 }
      //             });
      //         });
      //     }
      //
      //
      //     return menuItems;
      //
      // };


      menuItemList() {
        const menuItems = []; // color-by INFO attribute

        if (this.header.INFO) {
          //Code below will present checkboxes for all info fields of type "String".   Wait until this is requested
          //const stringInfoKeys = Object.keys(this.header.INFO).filter(key => "String" === this.header.INFO[key].Type);
          // For now stick to explicit info fields (well, exactly 1 for starters)
          if (this.header.INFO) {
            //const stringInfoKeys = Object.keys(this.header.INFO).filter(key => this.header.INFO[key].Type === "String")
            const stringInfoKeys = this.header.INFO.SVTYPE ? ['SVTYPE'] : [];

            if (this._initColorBy && this._initColorBy !== 'SVTYPE') {
              stringInfoKeys.push(this._initColorBy);
            }

            if (stringInfoKeys.length > 0) {
              menuItems.push('<hr/>');
              const $e = $$1('<div class="igv-track-menu-category igv-track-menu-border-top">');
              $e.text('Color by:');
              menuItems.push({
                name: undefined,
                object: $e,
                click: undefined,
                init: undefined
              });
              stringInfoKeys.sort();

              for (let item of stringInfoKeys) {
                const selected = this.colorBy === item;
                const label = item ? item : 'None';
                menuItems.push(this.colorByCB({
                  key: item,
                  label: label
                }, selected));
              }

              menuItems.push(this.colorByCB({
                key: undefined,
                label: 'None'
              }, this.colorBy === undefined));
              menuItems.push('<hr/>');
            }
          }
        }

        if (this.getCallsetsLength() > 0) {
          menuItems.push({
            object: $$1('<div class="igv-track-menu-border-top">')
          });
          menuItems.push({
            object: $$1(createCheckbox$1("Show Genotypes", this.showGenotypes)),
            click: () => {
              this.showGenotypes = !this.showGenotypes; //adjustTrackHeight();

              this.trackView.checkContentHeight();
              this.trackView.repaintViews();
            }
          });
        }

        menuItems.push({
          object: $$1('<div class="igv-track-menu-border-top">')
        });

        for (let displayMode of ["COLLAPSED", "SQUISHED", "EXPANDED"]) {
          var lut = {
            "COLLAPSED": "Collapse",
            "SQUISHED": "Squish",
            "EXPANDED": "Expand"
          };
          menuItems.push({
            object: $$1(createCheckbox$1(lut[displayMode], displayMode === this.displayMode)),
            click: () => {
              this.displayMode = displayMode;
              this.trackView.checkContentHeight();
              this.trackView.repaintViews();
            }
          });
        } // Experimental JBrowse circular view integration


        if (this.browser.circularView) {
          menuItems.push('<hr>');
          menuItems.push({
            label: 'Add SVs to circular view',
            click: () => {

              for (let viewport of this.trackView.viewports) {
                this.sendChordsForViewport(viewport);
              }
            }
          });
        }

        return menuItems;
      }

      contextMenuItemList(clickState) {
        // Experimental JBrowse circular view integration
        if (this.browser.circularView) {
          const viewport = clickState.viewport;
          const list = [];
          list.push({
            label: 'Add SVs to Circular View',
            click: () => {
              this.sendChordsForViewport(viewport);
            }
          });
          list.push('<hr/>');
          return list;
        }
      }

      sendChordsForViewport(viewport) {
        const refFrame = viewport.referenceFrame;
        const inView = "all" === refFrame.chr ? this.featureSource.getAllFeatures() : this.featureSource.featureCache.queryFeatures(refFrame.chr, refFrame.start, refFrame.end);
        const chords = makeVCFChords(inView);
        sendChords(chords, this, refFrame, 0.5);
      }
      /**
       * Create a "color by" checkbox menu item, optionally initially checked
       * @param menuItem
       * @param showCheck
       * @returns {{init: undefined, name: undefined, click: clickHandler, object: (jQuery|HTMLElement|jQuery.fn.init)}}
       */


      colorByCB(menuItem, showCheck) {
        const $e = $$1(createCheckbox$1(menuItem.label, showCheck));

        const clickHandler = () => {
          if (menuItem.key === this.colorBy) {
            this.colorBy = undefined;
            delete this.config.colorBy;
            this.trackView.repaintViews();
          } else {
            this.colorBy = menuItem.key;
            this.config.colorBy = menuItem.key;
            this.trackView.repaintViews();
          }
        };

        return {
          name: undefined,
          object: $e,
          click: clickHandler,
          init: undefined
        };
      }

      getState() {
        const config = super.getState();

        if (this._color && typeof this._color !== "function") {
          config.color = this._color;
        }

        return config;
      }

      getVariantColorTable(key) {
        if (!this.colorTables) {
          this.colorTables = new Map();
        }

        if (!this.colorTables.has(key)) {
          let tbl;

          switch (key) {
            case "SVTYPE":
              tbl = SV_COLOR_TABLE;
              break;

            default:
              tbl = new PaletteColorTable("Set1");
          }

          this.colorTables.set(key, tbl);
        }

        return this.colorTables.get(key);
      }

    }

    function expandGenotype(call, variant) {
      if (call.genotype) {
        let gt = '';

        if (variant.alternateBases === ".") {
          gt = "No Call";
        } else {
          const altArray = variant.alternateBases.split(",");

          for (let allele of call.genotype) {
            if (gt.length > 0) {
              gt += "|";
            }

            if ('.' === allele) {
              gt += '.';
            } else if (allele === 0) {
              gt += variant.referenceBases;
            } else {
              let alt = altArray[allele - 1].replace("<", "&lt;");
              gt += alt;
            }
          }
        }

        call.genotypeName = gt;
      }
    }

    const SV_COLOR_TABLE = new ColorTable({
      'DEL': '#ff2101',
      'INS': '#001888',
      'DUP': '#028401',
      'INV': '#008688',
      'CNV': '#8931ff',
      'BND': '#891100',
      '*': '#002eff'
    });

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class EqtlTrack extends TrackBase {
      constructor(config, browser) {
        super(config, browser);
      }

      init(config) {
        super.init(config);
        this.name = config.name;
        this.pValueField = config.pValueField || "pValue";
        this.geneField = config.geneField || "geneSymbol";
        this.snpField = config.snpField || "snp";
        const min = config.minLogP || config.min;
        const max = config.maxLogP || config.max;
        this.dataRange = {
          min: min || 3.5,
          max: max || 25
        };

        if (!max) {
          this.autoscale = true;
        } else {
          this.autoscale = config.autoscale;
        }

        this.autoscalePercentile = config.autoscalePercentile === undefined ? 98 : config.autoscalePercentile;
        this.background = config.background; // No default

        this.divider = config.divider || "rgb(225,225,225)";
        this.dotSize = config.dotSize || 2;
        this.height = config.height || 100;
        this.autoHeight = false;
        this.disableButtons = config.disableButtons; // Limit visibility window to 2 mb,  gtex server gets flaky beyond that

        this.visibilityWindow = config.visibilityWindow === undefined ? 2000000 : config.visibilityWindow >= 0 ? Math.min(2000000, config.visibilityWindow) : 2000000;
        this.featureSource = FeatureSource(config, this.browser.genome);
        GtexUtils.gtexLoaded = true;
      }

      paintAxis(ctx, pixelWidth, pixelHeight) {
        const yScale = (this.dataRange.max - this.dataRange.min) / pixelHeight;
        const font = {
          'font': 'normal 10px Arial',
          'textAlign': 'right',
          'strokeStyle': "black"
        };
        IGVGraphics.fillRect(ctx, 0, 0, pixelWidth, pixelHeight, {
          'fillStyle': "rgb(255, 255, 255)"
        }); // Determine a tick spacing such that there is at least 10 pixels between ticks

        const n = Math.ceil((this.dataRange.max - this.dataRange.min) * 10 / pixelHeight);

        for (let p = 4; p <= this.dataRange.max; p += n) {
          // TODO: Dashes may not actually line up with correct scale. Ask Jim about this
          const ref = 0.85 * pixelWidth;
          const x1 = ref - 5;
          const x2 = ref;
          const y = pixelHeight - (p - this.dataRange.min) / yScale;
          IGVGraphics.strokeLine(ctx, x1, y, x2, y, font); // Offset dashes up by 2 pixel

          if (y > 8) {
            IGVGraphics.fillText(ctx, p, x1 - 1, y + 2, font);
          } // Offset numbers down by 2 pixels;

        }

        font['textAlign'] = 'center';
        IGVGraphics.fillText(ctx, "-log10(pvalue)", pixelWidth / 4, pixelHeight / 2, font, {
          rotate: {
            angle: -90
          }
        });
      }

      async getFeatures(chr, start, end) {
        const pValueField = this.pValueField;
        const visibilityWindow = this.visibilityWindow;
        const features = await this.featureSource.getFeatures({
          chr,
          start,
          end,
          visibilityWindow
        });
        features.forEach(function (f) {
          f.value = f[pValueField];
        });
        return features;
      }

      draw(options) {
        const ctx = options.context;
        const pixelWidth = options.pixelWidth;
        const pixelHeight = options.pixelHeight;

        if (this.background) {
          IGVGraphics.fillRect(ctx, 0, 0, pixelWidth, pixelHeight, {
            'fillStyle': this.background
          });
        }

        IGVGraphics.strokeLine(ctx, 0, pixelHeight - 1, pixelWidth, pixelHeight - 1, {
          'strokeStyle': this.divider
        });

        const drawEqtls = drawSelected => {
          const radius = drawSelected ? 2 * this.dotSize : this.dotSize;
          const bpStart = options.bpStart;
          const yScale = (this.dataRange.max - this.dataRange.min) / pixelHeight;
          const selection = options.referenceFrame.selection;

          for (let eqtl of options.features) {
            const px = (eqtl.start - bpStart + 0.5) / options.bpPerPixel;
            if (px < 0) continue;else if (px > pixelWidth) break;
            const snp = eqtl.snp.toUpperCase();
            const geneName = eqtl[this.geneField].toUpperCase();
            const isSelected = selection && (selection.snp === snp || selection.gene === geneName);

            if (!drawSelected || isSelected) {
              // Add eqtl's gene to the selection if this is the selected snp.
              // TODO -- this should not be done here in the rendering code.
              if (selection && selection.snp === snp) {
                selection.addGene(geneName);
              }

              var mLogP = -Math.log(eqtl[this.pValueField]) / Math.LN10;

              if (mLogP >= this.dataRange.min) {
                let capped;

                if (mLogP > this.dataRange.max) {
                  mLogP = this.dataRange.max;
                  capped = true;
                } else {
                  capped = false;
                }

                const py = Math.max(0 + radius, pixelHeight - Math.round((mLogP - this.dataRange.min) / yScale));
                eqtl.px = px;
                eqtl.py = py;
                eqtl.radius = radius;
                let color;

                if (drawSelected && selection) {
                  color = selection.colorForGene(geneName);
                  IGVGraphics.setProperties(ctx, {
                    fillStyle: color,
                    strokeStyle: "black"
                  });
                } else {
                  color = capped ? "rgb(150, 150, 150)" : "rgb(180, 180, 180)";
                  IGVGraphics.setProperties(ctx, {
                    fillStyle: color,
                    strokeStyle: color
                  });
                }

                IGVGraphics.fillCircle(ctx, px, py, radius);
                IGVGraphics.strokeCircle(ctx, px, py, radius);
              }
            }
          }
        }; // Draw in two passes, with "selected" eqtls drawn last


        drawEqtls(false);
        drawEqtls(true);
      }
      /**
       * Return "popup data" for feature @ genomic location.  Data is an array of key-value pairs
       */


      popupData(clickState) {
        let features = clickState.viewport.cachedFeatures;
        if (!features || features.length === 0) return [];
        const tolerance = 3;
        const tissue = this.name;
        const popupData = [];

        for (let feature of features) {
          // Hit test --use square vs circle for efficiency (no sqrt)
          if (Math.abs(feature.px - clickState.canvasX) < feature.radius + tolerance && Math.abs(feature.py - clickState.canvasY) < feature.radius + tolerance) {
            if (popupData.length > 0) {
              popupData.push('<hr/>');
            }

            popupData.push({
              name: "snp id",
              value: feature.snp
            }, {
              name: "gene id",
              value: feature.geneId
            }, {
              name: "gene name",
              value: feature.geneName
            }, {
              name: "p value",
              value: feature.pValue
            }, {
              name: "tissue",
              value: tissue
            });
          }
        }

        return popupData;
      }

      menuItemList() {
        return MenuUtils.numericDataMenuItems(this.trackView);
      }

      doAutoscale(featureList) {
        if (featureList.length > 0) {
          var values = featureList.map(function (eqtl) {
            return -Math.log(eqtl.value) / Math.LN10;
          });
          this.dataRange.max = IGVMath.percentile(values, this.autoscalePercentile);
        } else {
          // No features -- default
          const max = this.config.maxLogP || this.config.max;
          this.dataRange.max = max || 25;
        }

        return this.dataRange;
      }

    }

    /**
     * Colors used for coding omosomes
     */
    const Colors = {
      "X": "rgb(204, 153, 0)",
      "Y": "rgb(153, 204, 0)",
      "Un": "darkGray)",
      "1": "rgb(80, 80, 255)",
      //"1": Color.red);
      "I": "rgb(139, 155, 187)",
      "2": "rgb(206, 61, 50)",
      "II": "rgb(206, 61, 50)",
      "2a": "rgb(210, 65, 55)",
      "2b": "rgb(215, 70, 60)",
      "3": "rgb(116, 155, 88)",
      "III": "rgb(116, 155, 88)",
      "4": "rgb(240, 230, 133)",
      "IV": "rgb(240, 230, 133)",
      "5": "rgb(70, 105, 131)",
      "6": "rgb(186, 99, 56)",
      "7": "rgb(93, 177, 221)",
      "8": "rgb(128, 34, 104)",
      "9": "rgb(107, 215, 107)",
      "10": "rgb(213, 149, 167)",
      "11": "rgb(146, 72, 34)",
      "12": "rgb(131, 123, 141)",
      "13": "rgb(199, 81, 39)",
      "14": "rgb(213, 143, 92)",
      "15": "rgb(122, 101, 165)",
      "16": "rgb(228, 175, 105)",
      "17": "rgb(59, 27, 83)",
      "18": "rgb(205, 222, 183)",
      "19": "rgb(97, 42, 121)",
      "20": "rgb(174, 31, 99)",
      "21": "rgb(231, 199, 111)",
      "22": "rgb(90, 101, 94)",
      "23": "rgb(204, 153, 0)",
      "24": "rgb(153, 204, 0)",
      "25": "rgb(51, 204, 0)",
      "26": "rgb(0, 204, 51)",
      "27": "rgb(0, 204, 153)",
      "28": "rgb(0, 153, 204)",
      "29": "rgb(10, 71, 255)",
      "30": "rgb(71, 117, 255)",
      "31": "rgb(255, 194, 10)",
      "32": "rgb(255, 209, 71)",
      "33": "rgb(153, 0, 51)",
      "34": "rgb(153, 26, 0)",
      "35": "rgb(153, 102, 0)",
      "36": "rgb(128, 153, 0)",
      "37": "rgb(51, 153, 0)",
      "38": "rgb(0, 153, 26)",
      "39": "rgb(0, 153, 102)",
      "40": "rgb(0, 128, 153)",
      "41": "rgb(0, 51, 153)",
      "42": "rgb(26, 0, 153)",
      "43": "rgb(102, 0, 153)",
      "44": "rgb(153, 0, 128)",
      "45": "rgb(214, 0, 71)",
      "46": "rgb(255, 20, 99)",
      "47": "rgb(0, 214, 143)",
      "48": "rgb(20, 255, 177)"
    }; //  aliasing

    for (let key of Object.keys(Colors)) {
      const altName = "chr" + key;
      Colors[altName] = Colors[key];
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const DEFAULT_POPOVER_WINDOW = 100000000; //const type = "gwas";

    class GWASTrack extends TrackBase {
      constructor(config, browser) {
        super(config, browser);
      }

      init(config) {
        super.init(config);
        this.useChrColors = config.useChrColors === undefined ? true : config.useChrColors;
        this.trait = config.trait;
        this.posteriorProbability = config.posteriorProbability;
        this.valueProperty = "bed" === config.format ? "score" : "value";
        this.height = config.height || 100; // The preferred height

        this.autoscale = config.autoscale;
        this.autoscalePercentile = config.autoscalePercentile === undefined ? 98 : config.autoscalePercentile;
        this.background = config.background; // No default

        this.divider = config.divider || "rgb(225,225,225)";
        this.dotSize = config.dotSize || 3;
        this.popoverWindow = config.popoverWindow === undefined ? DEFAULT_POPOVER_WINDOW : config.popoverWindow;
        this.colorScales = config.color ? new ConstantColorScale(config.color) : {
          "*": new BinnedColorScale(config.colorScale || {
            thresholds: [5e-8, 5e-4, 0.5],
            colors: ["rgb(255,50,50)", "rgb(251,100,100)", "rgb(251,170,170)", "rgb(227,238,249)"]
          })
        };
        this.featureSource = FeatureSource(config, this.browser.genome);
      }

      async postInit() {
        if (typeof this.featureSource.getHeader === "function") {
          this.header = await this.featureSource.getHeader();
        } // Set properties from track line


        if (this.header) {
          this.setTrackProperties(this.header); // setTrackProperties defined in TrackBase
        } // Set initial range if specfied, unless autoscale == true


        if (!this.autoscale) {
          if (this.posteriorProbability) {
            this.dataRange = {
              min: this.config.min === undefined ? 0 : this.config.min,
              max: this.config.max === undefined ? 1 : this.config.max
            };
          } else {
            this.dataRange = {
              min: this.config.min === undefined ? 0 : this.config.min,
              max: this.config.max === undefined ? 25 : this.config.max
            };
          }
        }

        return this;
      }

      get supportsWholeGenome() {
        return true;
      }

      async getFeatures(chr, start, end) {
        const visibilityWindow = this.visibilityWindow;
        return this.featureSource.getFeatures({
          chr,
          start,
          end,
          visibilityWindow
        });
      }

      draw(options) {
        const featureList = options.features;
        const ctx = options.context;
        const pixelWidth = options.pixelWidth;
        const pixelHeight = options.pixelHeight;

        if (this.background) {
          IGVGraphics.fillRect(ctx, 0, 0, pixelWidth, pixelHeight, {
            'fillStyle': this.background
          });
        }

        IGVGraphics.strokeLine(ctx, 0, pixelHeight - 1, pixelWidth, pixelHeight - 1, {
          'strokeStyle': this.divider
        });

        if (featureList) {
          const bpPerPixel = options.bpPerPixel;
          const bpStart = options.bpStart;
          const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;

          for (let variant of featureList) {
            const pos = variant.start;
            if (pos < bpStart) continue;
            if (pos > bpEnd) break;
            const colorScale = this.getColorScale(variant._f ? variant._f.chr : variant.chr);
            let color;
            let val;

            if (this.posteriorProbability) {
              val = variant[this.valueProperty];
              color = colorScale.getColor(val);
            } else {
              const pvalue = variant[this.valueProperty];
              if (!pvalue) continue;
              val = -Math.log10(pvalue);
              color = colorScale.getColor(val);
            }

            const yScale = (this.dataRange.max - this.dataRange.min) / pixelHeight;
            const px = Math.round((pos - bpStart) / bpPerPixel);
            const py = Math.max(this.dotSize, pixelHeight - Math.round((val - this.dataRange.min) / yScale));

            if (color) {
              IGVGraphics.setProperties(ctx, {
                fillStyle: color,
                strokeStyle: "black"
              });
            }

            IGVGraphics.fillCircle(ctx, px, py, this.dotSize);
            variant.px = px;
            variant.py = py;
          }
        }
      }

      getColorScale(chr) {
        if (this.useChrColors) {
          let cs = this.colorScales[chr];

          if (!cs) {
            const color = Colors[chr] || randomColor();
            cs = new ConstantColorScale(color);
            this.colorScales[chr] = cs;
          }

          return cs;
        } else {
          return this.colorScales("*");
        }
      }

      paintAxis(ctx, pixelWidth, pixelHeight) {
        IGVGraphics.fillRect(ctx, 0, 0, pixelWidth, pixelHeight, {
          'fillStyle': "rgb(255, 255, 255)"
        });
        var font = {
          'font': 'normal 10px Arial',
          'textAlign': 'right',
          'strokeStyle': "black"
        };
        const yScale = (this.dataRange.max - this.dataRange.min) / pixelHeight;

        if (this.posteriorProbability) {
          const n = 0.1;

          for (let p = this.dataRange.min; p < this.dataRange.max; p += n) {
            const yp = pixelHeight - Math.round((p - this.dataRange.min) / yScale);
            IGVGraphics.strokeLine(ctx, 45, yp - 2, 50, yp - 2, font); // Offset dashes up by 2 pixel

            IGVGraphics.fillText(ctx, p.toFixed(1), 44, yp + 2, font); // Offset numbers down by 2 pixels;
          }
        } else {
          const n = Math.ceil((this.dataRange.max - this.dataRange.min) * 10 / pixelHeight);

          for (let p = this.dataRange.min; p < this.dataRange.max; p += n) {
            const yp = pixelHeight - Math.round((p - this.dataRange.min) / yScale);
            IGVGraphics.strokeLine(ctx, 45, yp, 50, yp, font); // Offset dashes up by 2 pixel

            IGVGraphics.fillText(ctx, Math.floor(p), 44, yp + 4, font); // Offset numbers down by 2 pixels;
          }
        }

        font['textAlign'] = 'center';

        if (this.posteriorProbability) {
          IGVGraphics.fillText(ctx, "PPA", pixelWidth / 2, pixelHeight / 2, font, {
            rotate: {
              angle: -90
            }
          });
        } else {
          IGVGraphics.fillText(ctx, "-log10(pvalue)", pixelWidth / 2, pixelHeight / 2, font, {
            rotate: {
              angle: -90
            }
          });
        }
      }

      popupData(clickState) {
        let data = [];
        const track = clickState.viewport.trackView.track;
        const features = clickState.viewport.cachedFeatures;

        if (features) {
          let count = 0;

          for (let f of features) {
            const xDelta = Math.abs(clickState.canvasX - f.px);
            const yDelta = Math.abs(clickState.canvasY - f.py);
            const value = f[this.valueProperty];

            if (xDelta < this.dotSize && yDelta < this.dotSize) {
              if (count > 0) {
                data.push("<HR/>");
              }

              if (count == 5) {
                data.push("...");
                break;
              }

              if (typeof f.popupData === 'function') {
                data = data.concat(f.popupData());
              } else {
                const chr = f.realChr || f.chr;
                const pos = (f.realStart || f.start) + 1;
                data.push({
                  name: 'chromosome',
                  value: chr
                });
                data.push({
                  name: 'position',
                  value: pos
                });
                data.push({
                  name: 'name',
                  value: f.name
                });

                if (track.posteriorProbability) {
                  data.push({
                    name: 'posterior probability',
                    value: value
                  });
                } else {
                  data.push({
                    name: 'pValue',
                    value: value
                  });
                }
              }

              count++;
            }
          }
        }

        return data;
      }

      menuItemList() {
        return MenuUtils.numericDataMenuItems(this.trackView);
      }

      doAutoscale(featureList) {
        if (featureList.length > 0) {
          // posterior probabilities are treated without modification, but we need to take a negative logarithm of P values
          const valueProperty = this.valueProperty;
          const posterior = this.posteriorProbability;
          const features = featureList.map(function (feature) {
            const v = feature[valueProperty];
            return {
              value: posterior ? v : -Math.log(v) / Math.LN10
            };
          });
          this.dataRange = doAutoscale(features);
        } else {
          // No features -- pick something reasonable for PPAs and p-values
          if (this.posteriorProbability) {
            this.dataRange.min = this.config.min || 0;
            this.dataRange.max = this.config.max || 1;
          } else {
            this.dataRange.max = this.config.max || 25;
            this.dataRange.min = this.config.min || 0;
          }
        }

        return this.dataRange;
      }

    }

    const X_PIXEL_DIFF_THRESHOLD = 1;

    class GCNVTrack extends TrackBase {
      constructor(config, browser) {
        super(config, browser);
      }

      init(config) {
        super.init(config);
        this.autoscale = config.autoscale || config.max === undefined;
        this.dataRange = {
          min: config.min || 0,
          max: config.max
        };
        this.windowFunction = config.windowFunction || "mean";
        this.paintAxis = paintAxis;
        this.graphType = config.graphType || "bar"; //hack to avoid creating feature source multiple times.  If config.type is not set the file must be read
        //to determine type, which results in creation of a feature source.

        if (config._featureSource) {
          this.featureSource = config._featureSource;
          delete config._featureSource;
        } else {
          this.featureSource = FeatureSource(this.config, this.browser.genome);
        } // Visibility window hardcoded to -1  (== whole chromosome).  Draw method needs feature beyond current view,
        // when/if this is resolved visibilityWindow can be used.


        this.visibilityWindow = -1;
        this.featureSource.visibilityWindow = this.visibilityWindow;
      }

      async postInit() {
        if (typeof this.featureSource.getHeader === "function") {
          this.header = await this.featureSource.getHeader();
          this.sampleNames = this.header.columnNames.slice(3); // Set generic properties from track line

          this.setTrackProperties(this.header); // setTrackProperties defined in TrackBase
          // set option to highlight sample track line on click

          if (this.header.hasOwnProperty("clickToHighlight")) {
            let colour = this.header["clickToHighlight"];
            this.config.clickToHighlight = colour;
            this.config.samplesClickedToHighlight = {};
          } // Special track line properties


          if (this.header.hasOwnProperty("highlight")) {
            this.config.highlightSamples = {};
            let v = this.header["highlight"];
            if (!Array.isArray(v)) v = [v];

            for (let h of v) {
              const tokens = h.split(";");

              if (tokens.length === 2) {
                this.config.highlightSamples[tokens[0]] = tokens[1];
              }
            }
          }
        }
      }

      menuItemList() {
        return MenuUtils.numericDataMenuItems(this.trackView);
      }

      async getFeatures(chr, start, end) {
        const chrFeatures = await this.featureSource.getFeatures({
          chr,
          start: 0,
          end: Number.MAX_SAFE_INTEGER,
          visibilityWindow: this.visibilityWindow
        });
        let prevIndex = undefined;
        let nextIndex = undefined;

        for (let i = 1; i < chrFeatures.length - 1; i++) {
          if (prevIndex === undefined && chrFeatures[i].end > start) {
            prevIndex = i - 1;
          }

          if (nextIndex === undefined && chrFeatures[i].start > end) {
            nextIndex = i + 1;
            break;
          }
        }

        if (prevIndex === undefined) prevIndex = 0;
        if (nextIndex === undefined) nextIndex = chrFeatures.length;
        return chrFeatures.slice(prevIndex, nextIndex);
      }

      draw(options) {
        const {
          features,
          context,
          bpPerPixel,
          bpStart,
          pixelWidth,
          pixelHeight
        } = options; ///let baselineColor;
        //if (typeof self.color === "string" && self.color.startsWith("rgb(")) {
        //    baselineColor = IGVColor.addAlpha(self.color, 0.1);
        //}

        const yScale = yValue => {
          return (this.dataRange.max - yValue) / (this.dataRange.max - this.dataRange.min) * pixelHeight;
        };

        const getX = function (bpPosition) {
          let x = Math.floor((bpPosition - bpStart) / bpPerPixel);
          if (isNaN(x)) console.warn('isNaN(x). feature start ' + numberFormatter$1(bpPosition) + ' bp start ' + numberFormatter$1(bpStart));
          return x;
        };

        const drawGuideLines = options => {
          if (this.config.hasOwnProperty('guideLines')) {
            for (let line of this.config.guideLines) {
              if (line.hasOwnProperty('color') && line.hasOwnProperty('y') && line.hasOwnProperty('dotted')) {
                let y = yScale(line.y);
                let props = {
                  'strokeStyle': line['color'],
                  'strokeWidth': 2
                };
                if (line['dotted']) IGVGraphics.dashedLine(options.context, 0, y, options.pixelWidth, y, 5, props);else IGVGraphics.strokeLine(options.context, 0, y, options.pixelWidth, y, props);
              }
            }
          }
        };

        if (features && features.length > 0) {
          if (this.dataRange.min === undefined) this.dataRange.min = 0; // Max can be less than min if config.min is set but max left to autoscale. If that's the case there is
          // nothing to paint.

          if (this.dataRange.max > this.dataRange.min) {
            const highlightSamples = this.config.highlightSamples;
            const onlyHandleClicksForHighlightedSamples = this.config.onlyHandleClicksForHighlightedSamples;
            const clickToHighlight = this.config.clickToHighlight;
            let previousEnd = -1;
            let previousValues = {};
            let highlightConnectorLines = [];
            let highlightFeatureLines = []; // clickDetectorCache allows fast retrieval of whether a mouse click hits a rendered line segment
            // by storing lists of rendered line segments, keyed by their right x coordinate in canvas pixel space.
            // this cache is regenerated on every draw.

            this.clickDetectorCache = {};

            for (let feature of features) {
              const x1 = getX(feature.start);
              const x2 = getX(feature.end);
              const previousX = previousEnd >= 0 ? getX(previousEnd) : x1;
              if (isNaN(x1) || isNaN(x2)) continue; // if ((x1 - previousX < X_PIXEL_DIFF_THRESHOLD) && (x2 - x1 < X_PIXEL_DIFF_THRESHOLD)) continue;

              this.clickDetectorCache[x1] = [];
              this.clickDetectorCache[x2] = [];

              for (let i = 0; i < feature.values.length; i++) {
                const sampleName = this.sampleNames[i];
                const value = feature.values[i];
                const y = yScale(value);

                if (x1 - previousX >= X_PIXEL_DIFF_THRESHOLD) {
                  const previousValue = previousValues[sampleName];
                  const previousY = yScale(previousValue);
                  const highlightColor = highlightSamples && highlightSamples[sampleName];

                  if (highlightColor) {
                    highlightConnectorLines.push([previousX, previousY, x1, y, highlightColor]);
                  } else if (clickToHighlight && sampleName in this.config.samplesClickedToHighlight) {
                    highlightConnectorLines.push([previousX, previousY, x1, y, this.config.samplesClickedToHighlight[sampleName]]);
                  } else {
                    IGVGraphics.strokeLine(context, previousX, previousY, x1, y, {
                      strokeStyle: '#D9D9D9'
                    });
                  }

                  if (!onlyHandleClicksForHighlightedSamples || sampleName in highlightSamples) {
                    this.clickDetectorCache[x1].push([previousX, previousY, x1, y, sampleName, highlightColor || 'gray']);
                  }
                }

                if (x2 - x1 >= X_PIXEL_DIFF_THRESHOLD) {
                  const highlightColor = highlightSamples && highlightSamples[sampleName];

                  if (highlightColor) {
                    highlightFeatureLines.push([x1, y, x2, y, highlightColor]);
                  } else if (clickToHighlight && sampleName in this.config.samplesClickedToHighlight) {
                    highlightFeatureLines.push([x1, y, x2, y, this.config.samplesClickedToHighlight[sampleName]]);
                  } else {
                    IGVGraphics.strokeLine(context, x1, y, x2, y, {
                      strokeStyle: 'gray'
                    });
                  }

                  if (!onlyHandleClicksForHighlightedSamples || sampleName in highlightSamples) {
                    this.clickDetectorCache[x2].push([x1, y, x2, y, sampleName, highlightColor || 'gray']);
                  }
                }

                previousValues[sampleName] = value; //IGVGraphics.fillCircle(ctx, px, y, pointSize / 2, {"fillStyle": color, "strokeStyle": color});
                //IGVGraphics.fillRect(ctx, x, y, width, height, {fillStyle: color});
              }

              previousEnd = feature.end;
            }

            for (let f of highlightConnectorLines) {
              IGVGraphics.strokeLine(context, f[0], f[1], f[2], f[3], {
                strokeStyle: f[4],
                lineWidth: 1.3
              });
            }

            for (let f of highlightFeatureLines) {
              IGVGraphics.strokeLine(context, f[0], f[1], f[2], f[3], {
                strokeStyle: f[4],
                lineWidth: 2
              });
            }
            /*
            // If the track includes negative values draw a baseline
            if (this.dataRange.min < 0) {
                const basepx = (self.dataRange.max / (this.dataRange.max - this.dataRange.min)) * options.pixelHeight;
                IGVGraphics.strokeLine(context, 0, basepx, options.pixelWidth, basepx, {strokeStyle: baselineColor});
            }
            */

          }
        }

        drawGuideLines(options);
      }

      doAutoscale(features) {
        let min, max;

        if (features.length > 0) {
          min = Number.MAX_VALUE;
          max = -Number.MAX_VALUE;
          features.forEach(function (feature) {
            min = Math.min(min, ...feature.values);
            max = Math.max(max, ...feature.values);
          });
          min -= 0.01;
          max += 0.01;
        } else {
          // No features -- default
          min = 0;
          max = 100;
        }

        return {
          min: min,
          max: max
        };
      }

      clickedFeatures(clickState) {
        //console.warn('click', clickState.canvasX, clickState.canvasY, clickState)
        const BOUNDING_BOX_PADDING = 10;
        const MIN_DISTANCE_TO_SEGMENT = 5;
        const clickX = clickState.canvasX;
        const clickY = clickState.canvasY;
        let key = null;

        for (key of Object.keys(this.clickDetectorCache)) {
          key = parseInt(key);

          if (key >= clickX) {
            break;
          }
        }

        if (key) {
          let closestDistanceSoFar = Number.MAX_VALUE;
          let closestResult = [];
          const segments = this.clickDetectorCache[key];

          for (let segment of segments) {
            const x1 = segment[0];
            const x2 = segment[2];
            if (clickX < x1 || clickX > x2) return [];
            const y1 = segment[1];
            const y2 = segment[3];
            if (clickY < Math.min(y1, y2) - BOUNDING_BOX_PADDING || clickY > Math.max(y1, y2) + BOUNDING_BOX_PADDING) continue;
            const distance = distanceToLine(clickX, clickY, x1, y1, x2, y2);

            if (distance < closestDistanceSoFar) {
              closestResult = [{
                'name': segment[4],
                'color': segment[5]
              }];
              closestDistanceSoFar = distance; //console.warn('closest:', 'name', segment[4], 'color', segment[5], distance);
            }
          }

          if (closestDistanceSoFar < MIN_DISTANCE_TO_SEGMENT) {
            // clickToHighlight set, add sample to dict of clicked lines
            if (this.config.clickToHighlight) {
              if (closestResult[0]['name'] in this.config.samplesClickedToHighlight) {
                // clicked sample already highlighted => remove if clicked again
                delete this.config.samplesClickedToHighlight[closestResult[0]['name']];
              } else if (this.config.clickToHighlight === 'any') {
                var colourList = ['red', 'darkblue', 'green', 'teal', 'olivedrab', 'orange', 'maroon', 'purple', 'blue', 'gold'];
                var colour = colourList[Math.floor(Math.random() * (colourList.length + 1))];
                this.config.samplesClickedToHighlight[closestResult[0]['name']] = colour;
              } else {
                this.config.samplesClickedToHighlight[closestResult[0]['name']] = this.config.clickToHighlight;
              }

              this.trackView.repaintViews(); // prompt redraw to change colour of clicked sample
            }

            return closestResult;
          }
        }

        return [];
      }

      popupData(clickState, featureList) {
        featureList = this.clickedFeatures(clickState, featureList);
        const items = [];
        featureList.forEach(function (f) {
          for (let property of Object.keys(f)) {
            if (isSimpleType(f[property])) {
              items.push({
                name: property,
                value: f[property]
              });
            }
          }
        });
        return items;
      }

      get supportsWholeGenome() {
        return false;
      }

    }

    function distanceToLine(x, y, ax, ay, bx, by) {
      /*
          Finds distance between point (x, y) and line defined by points (ax, ay) (bx, by)
          based on http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
      */
      const bx_minus_ax = bx - ax;
      const by_minus_ay = by - ay;
      const v = Math.abs(bx_minus_ax * (ay - y) - (ax - x) * by_minus_ay);
      const r = Math.sqrt(bx_minus_ax * bx_minus_ax + by_minus_ay * by_minus_ay);
      const distance = r > 0 ? v / r : 0; //console.warn('Check if', x, y, 'is within', ax, ay, bx, by, '. Distance from line: ', distance);

      return distance;
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class RnaStructTrack extends TrackBase {
      constructor(config, browser) {
        super(config, browser); // Set defaults

        if (!config.height) {
          this.height = 300;
        }

        this.arcOrientation = false;
        this.theta = Math.PI / 2;

        if ("bp" === config.format) {
          this.featureSource = new RNAFeatureSource(config, browser.genome);
        } else {
          this.featureSource = new TextFeatureSource(config, browser.genome);
        }
      }

      async getFeatures(chr, start, end) {
        const visibilityWindow = this.visibilityWindow;
        return this.featureSource.getFeatures({
          chr,
          start,
          end,
          visibilityWindow
        });
      }

      draw(options) {
        const ctx = options.context;
        const theta = Math.PI / 2;
        const pixelWidth = options.pixelWidth;
        const pixelHeight = options.pixelHeight;
        options.viewportWidth;
        const bpPerPixel = options.bpPerPixel;
        const bpStart = options.bpStart;
        const xScale = bpPerPixel;
        const orienation = this.arcOrientation;
        IGVGraphics.fillRect(ctx, 0, options.pixelTop, pixelWidth, pixelHeight, {
          'fillStyle': "rgb(255, 255, 255)"
        });
        const featureList = options.features;

        if (featureList) {
          // Sort by score -- draw lowest scored features first
          sortByScore(featureList, 1);

          for (let feature of featureList) {
            if (feature.startLeft) {
              let sl = Math.round((feature.startLeft - bpStart) / xScale);
              let sr = Math.round((feature.startRight - bpStart) / xScale);
              let el = Math.round((feature.endLeft - bpStart) / xScale);
              let er = Math.round((feature.endRight - bpStart) / xScale);
              ctx.fillStyle = feature.color;
              ctx.strokeStyle = feature.color;
              ctx.beginPath(); // First arc

              let x1 = (sl + er) / 2;
              let r1 = (er - sl) / 2;
              let y1 = this.height;
              let sa = Math.PI + (Math.PI / 2 - theta);
              let ea = 2 * Math.PI - (Math.PI / 2 - theta);

              if (orienation) {
                y1 = 0;
                ctx.arc(x1, y1, r1, ea, sa);
                ctx.lineTo(er, y1);
              } else {
                ctx.arc(x1, y1, r1, sa, ea);
                ctx.lineTo(el, y1);
              } // Second arc


              const x2 = (sr + el) / 2;
              const r2 = (el - sr) / 2;
              const y2 = y1; // Only for theta == pi/2

              if (orienation) {
                ctx.arc(x2, y2, r2, sa, ea, true);
                ctx.lineTo(el, y2);
              } else {
                ctx.arc(x2, y2, r2, ea, sa, true);
                ctx.lineTo(sl, y2);
              }

              ctx.stroke();
              ctx.fill();
              feature.drawState = {
                x1: x1,
                y1: y1,
                r1: r1,
                x2: x2,
                y2: y2,
                r2: r2,
                sa: sa,
                ea: ea
              };
            } else {
              let s = Math.round((feature.start - bpStart) / xScale);
              let e = Math.round((feature.end - bpStart) / xScale);
              ctx.strokeStyle = feature.color;
              ctx.beginPath(); // First arc

              let x = (s + e) / 2;
              let r = (e - s) / 2;
              let y = this.height;
              let sa = Math.PI + (Math.PI / 2 - theta);
              let ea = 2 * Math.PI - (Math.PI / 2 - theta);

              if (orienation) {
                y = 0;
                ctx.arc(x, y, r, ea, sa);
              } else {
                ctx.arc(x, y, r, sa, ea);
              }

              ctx.stroke();
              feature.drawState = {
                x1: x,
                y1: y,
                r1: r,
                sa: sa,
                ea: ea
              };
            }
          }
        }
      }

      clickedFeatures(clickState, features) {
        features = super.clickedFeatures(clickState, features);
        const clicked = []; // Sort by score in descending order   (opposite order than drawn)

        sortByScore(features, -1);

        for (let f of features) {
          const ds = f.drawState; // Distance from arc radius, or outer arc for type ".bp"

          const dx1 = clickState.canvasX - ds.x1;
          const dy1 = clickState.canvasY - ds.y1;
          const d1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
          const outerLim = ds.r1 + 3;
          let d2;
          let innerLim;

          if (ds.x2 === undefined) {
            d2 = d1;
            innerLim = ds.r1 - 3;
          } else {
            const dx2 = clickState.canvasX - ds.x2;
            const dy2 = clickState.canvasY - ds.y2;
            d2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
            innerLim = ds.r2 - 3;
          } // Between outer and inner arcs, with some tolerance


          if (d1 < outerLim && d2 > innerLim) {
            clicked.push(f);
            break;
          }
        }

        return clicked;
      }

      popupData(clickState, features) {
        // We use the featureCache property rather than method to avoid async load.  If the
        // feature is not already loaded this won't work,  but the user wouldn't be mousing over it either.
        features = this.clickedFeatures(clickState, features);

        if (features && features.length > 0) {
          return TrackBase.extractPopupData(features[0], this.getGenomeId());
        }
      }

      menuItemList() {
        var self = this;
        return [{
          name: "Toggle arc direction",
          click: function () {
            self.arcOrientation = !self.arcOrientation;
            self.trackView.repaintViews();
          }
        }];
      }

    }

    function sortByScore(featureList, direction) {
      featureList.sort(function (a, b) {
        const s1 = a.score === undefined ? -Number.MAX_VALUE : a.score;
        const s2 = b.score === undefined ? -Number.MAX_VALUE : b.score;
        const d = direction === undefined ? 1 : direction;
        return d * (s1 - s2);
      });
    }

    class RNAFeatureSource {
      constructor(config, genome) {
        this.config = config;
        this.genome = genome;
      }

      async getFeatures(_ref) {
        let {
          chr,
          start,
          end,
          bpPerPixel,
          visibilityWindow
        } = _ref;
        const genome = this.genome;

        if (!this.featureCache) {
          const options = buildOptions(this.config);
          const data = await igvxhr.loadString(this.config.url, options);
          this.featureCache = new FeatureCache$1(parseBP(data), genome);
          return this.featureCache.queryFeatures(chr, start, end);
        } else {
          return this.featureCache.queryFeatures(chr, start, end);
        }

        function parseBP(data) {
          if (!data) return null;
          const dataWrapper = getDataWrapper(data);
          let header = true;
          let line;
          const colors = [];
          const descriptors = [];
          const features = [];

          while ((line = dataWrapper.nextLine()) !== undefined) {
            const tokens = line.split('\t');

            if (header && line.startsWith("color:")) {
              const color = "rgb(" + tokens[1] + "," + tokens[2] + "," + tokens[3] + ")";
              colors.push(color);

              if (tokens.length > 4) {
                descriptors.push(tokens[4]);
              } // TODO - use label

            } else {
              header = false;
              const chr = tokens[0];
              const startLeftNuc = Number.parseInt(tokens[1]) - 1;
              const startRightNuc = Number.parseInt(tokens[2]) - 1;
              const endLeftNuc = Number.parseInt(tokens[3]);
              const endRightNuc = Number.parseInt(tokens[4]);
              var colorIdx = Number.parseInt(tokens[5]);
              const color = colors[colorIdx];
              let feature;

              if (startLeftNuc <= endRightNuc) {
                feature = {
                  chr: chr,
                  startLeft: Math.min(startLeftNuc, startRightNuc),
                  startRight: Math.max(startLeftNuc, startRightNuc),
                  endLeft: Math.min(endLeftNuc, endRightNuc),
                  endRight: Math.max(endLeftNuc, endRightNuc),
                  color: color,
                  score: colorIdx
                };
              } else {
                feature = {
                  chr: chr,
                  startLeft: Math.min(endLeftNuc, endRightNuc),
                  startRight: Math.max(endLeftNuc, endRightNuc),
                  endLeft: Math.min(startLeftNuc, startRightNuc),
                  endRight: Math.max(startLeftNuc, startRightNuc),
                  color: color,
                  score: colorIdx
                };
              }

              feature.start = feature.startLeft;
              feature.end = feature.endRight;

              if (descriptors.length > colorIdx) {
                feature.description = descriptors[colorIdx];
              }

              features.push(feature);
            }
          }

          return features;
        }
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    /**
     * Class represents an ideogram of a chromsome cytobands.  It is used for the header of a track panel.
     *
     */

    class IdeogramTrack {
      constructor(browser) {
        this.browser = browser;
        this.type = 'ideogram';
        this.height = 16;
        this.order = Number.MIN_SAFE_INTEGER;
        this.disableButtons = true;
        this.ignoreTrackMenu = true;
      }

      async getFeatures(chr, start, end) {
        return [];
      }

      computePixelHeight(ignore) {
        return this.height;
      }

      draw(_ref) {
        let {
          context,
          referenceFrame,
          pixelWidth,
          pixelHeight
        } = _ref;
        const chr = referenceFrame.chr;
        const chromosome = referenceFrame.genome.getChromosome(chr);

        if (undefined === chromosome || pixelWidth <= 0 || pixelHeight <= 0 || 'all' === chr.toLowerCase()) {
          return;
        }

        const stainColors = [];
        drawIdeogram({
          ctx: context,
          chr,
          referenceFrame,
          genome: referenceFrame.genome,
          width: pixelWidth,
          height: pixelHeight,
          stainColors
        });
        const widthBP = Math.round(referenceFrame.bpPerPixel * pixelWidth);
        const xBP = referenceFrame.start; // Total chromosome length can be > chromosome.bpLength for partial fastas.

        let chrLength = chromosome.bpLength;
        const cytobands = referenceFrame.genome.getCytobands(chr);

        if (cytobands && cytobands.length > 0 && cytobands[cytobands.length - 1].end) {
          chrLength = Math.max(chrLength, cytobands[cytobands.length - 1].end);
          chromosome.bpLength = chrLength; // Correct bp length, bit of a hack
        }

        if (widthBP < chrLength) {
          const percentWidth = widthBP / chrLength;
          const percentX = xBP / chrLength;
          let x = Math.floor(percentX * pixelWidth);
          let ww = Math.floor(percentWidth * pixelWidth);
          x = Math.max(0, x);
          x = Math.min(pixelWidth - ww, x); // Push current context

          context.save(); // Draw red box

          context.strokeStyle = "red";
          context.lineWidth = ww < 2 ? 1 : 2;
          const xx = x + context.lineWidth / 2;
          ww = ww < 2 ? 1 : ww - context.lineWidth;
          const yy = context.lineWidth / 2;
          const hh = pixelHeight - context.lineWidth;
          context.strokeRect(xx, yy, ww, hh); // Pop current context

          context.restore();
        }
      }

      dispose() {
        this.trackView = undefined;
      }

    }

    function drawIdeogram(_ref2) {
      let {
        ctx,
        chr,
        referenceFrame,
        genome,
        width,
        height,
        stainColors
      } = _ref2;
      const shim = 1;
      const shim2 = 0.5 * shim;
      const ideogramTop = 0;

      if (undefined === genome) {
        return;
      }

      IGVGraphics.fillRect(ctx, 0, 0, width, height, {
        fillStyle: IGVColor.greyScale(255)
      });
      const cytobands = genome.getCytobands(chr);

      if (cytobands) {
        const center = ideogramTop + height / 2;
        const xC = [];
        const yC = [];

        if (0 === cytobands.length) {
          return;
        } // Get chrLength from the cytobands -- chromsome.bpLength might not work for igv-reports fasta files, which
        // contain only a portion of the chromosome sequence
        // *DOESNT WORK* const chrLength = referenceFrame.genome.getChromosome(chr).bpLength;


        const chrLength = cytobands[cytobands.length - 1].end;
        const scale = width / chrLength; // round rect clipping path

        ctx.beginPath();
        IGVGraphics.roundRect(ctx, shim2, shim2 + ideogramTop, width - 2 * shim2, height - 2 * shim2, (height - 2 * shim2) / 2, 0, 1);
        ctx.clip();

        for (let i = 0; i < cytobands.length; i++) {
          const cytoband = cytobands[i];
          const start = scale * cytoband.start;
          const end = scale * cytoband.end;

          if (cytoband.type === 'c') {
            if (cytoband.name.charAt(0) === 'p') {
              xC[0] = start;
              yC[0] = height + ideogramTop;
              xC[1] = start;
              yC[1] = ideogramTop;
              xC[2] = end;
              yC[2] = center;
            } else {
              xC[0] = end;
              yC[0] = height + ideogramTop;
              xC[1] = end;
              yC[1] = ideogramTop;
              xC[2] = start;
              yC[2] = center;
            }

            ctx.fillStyle = "rgb(150, 0, 0)";
            ctx.strokeStyle = "rgb(150, 0, 0)";
            IGVGraphics.polygon(ctx, xC, yC, 1, 0);
          } else {
            ctx.fillStyle = getCytobandColor(stainColors, cytoband);
            IGVGraphics.fillRect(ctx, start, shim + ideogramTop, end - start, height - 2 * shim);
          }
        }
      } // round rect border


      ctx.strokeStyle = IGVColor.greyScale(41);
      IGVGraphics.roundRect(ctx, shim2, shim2 + ideogramTop, width - 2 * shim2, height - 2 * shim2, (height - 2 * shim2) / 2, 0, 1);
    }

    function getCytobandColor(colors, data) {
      if (data.type === 'c') {
        // centermere: "acen"
        return "rgb(150, 10, 10)";
      } else {
        var stain = data.stain; // + 4;

        var shade = 230;

        if (data.type === 'p') {
          shade = Math.floor(230 - stain / 100.0 * 230);
        }

        var c = colors[shade];

        if (!c) {
          c = "rgb(" + shade + "," + shade + "," + shade + ")";
          colors[shade] = c;
        }

        return c;
      }
    }

    let JUNCTION_MOTIF_PALETTE = new PaletteColorTable("Dark2"); // Lock in color-to-motif mapping so it's independent of data loading order. This list may not include all possible
    // motif values as this varies depending on the RNA-seq pipeline. The current list is based on STAR v2.4 docs.

    const someMotifValues = ['GT/AG', 'CT/AC', 'GC/AG', 'CT/GC', 'AT/AC', 'GT/AT', 'non-canonical'];
    someMotifValues.forEach(motif => {
      JUNCTION_MOTIF_PALETTE.getColor(motif);
    }); // rendering context with values that only need to be computed once per render, rather than for each splice junction

    const junctionRenderingContext = {};

    class SpliceJunctionTrack extends TrackBase {
      constructor(config, browser) {
        super(config, browser);
      }

      init(config) {
        super.init(config);
        this.type = config.type || 'junctions';

        if (config._featureSource) {
          this.featureSource = config._featureSource;
          delete config._featureSource;
        } else {
          this.featureSource = config.featureSource ? config.featureSource : FeatureSource(config, this.browser.genome);
        }

        this.margin = config.margin === undefined ? 10 : config.margin;

        if (!this.height) {
          this.height = 100;
        } //set defaults


        if (config.colorByNumReadsThreshold === undefined) {
          config.colorByNumReadsThreshold = 5;
        }
      }

      async postInit() {
        if (typeof this.featureSource.getHeader === "function") {
          this.header = await this.featureSource.getHeader();
        } // Set properties from track line


        if (this.header) {
          this.setTrackProperties(this.header);
        }

        if (this.visibilityWindow === undefined && typeof this.featureSource.defaultVisibilityWindow === 'function') {
          this.visibilityWindow = await this.featureSource.defaultVisibilityWindow();
        }

        return this;
      }

      get supportsWholeGenome() {
        return false;
      }

      async getFeatures(chr, start, end, bpPerPixel) {
        const visibilityWindow = this.visibilityWindow;
        return this.featureSource.getFeatures({
          chr,
          start,
          end,
          bpPerPixel,
          visibilityWindow
        });
      }

      /**
       * The required height in pixels required for the track content.   This is not the visible track height, which
       * can be smaller (with a scrollbar) or larger.
       *
       * @param features
       * @returns {*}
       */
      computePixelHeight(features) {
        return this.height;
      }

      draw(options) {
        const featureList = options.features;
        const ctx = options.context;
        const bpPerPixel = options.bpPerPixel;
        const bpStart = options.bpStart;
        const pixelWidth = options.pixelWidth;
        const pixelHeight = options.pixelHeight;
        const bpEnd = bpStart + pixelWidth * bpPerPixel + 1;

        if (!this.config.isMergedTrack) {
          IGVGraphics.fillRect(ctx, 0, options.pixelTop, pixelWidth, pixelHeight, {
            'fillStyle': "rgb(255, 255, 255)"
          });
        }

        if (featureList) {
          junctionRenderingContext.referenceFrame = options.viewport.referenceFrame;
          junctionRenderingContext.referenceFrameStart = junctionRenderingContext.referenceFrame.start;
          junctionRenderingContext.referenceFrameEnd = junctionRenderingContext.referenceFrameStart + junctionRenderingContext.referenceFrame.toBP($$1(options.viewport.contentDiv).width()); // For a given viewport, records where features that are < 2px in width have been rendered already.
          // This prevents wasteful rendering of multiple such features onto the same pixels.

          junctionRenderingContext.featureZoomOutTracker = {};

          for (let feature of featureList) {
            if (feature.end < bpStart) continue;
            if (feature.start > bpEnd) break;
            this.renderJunction(feature, bpStart, bpPerPixel, pixelHeight, ctx);
          }
        } else {
          console.log("No feature list");
        }
      }

      /**
       *
       * @param feature
       * @param bpStart  genomic location of the left edge of the current canvas
       * @param xScale  scale in base-pairs per pixel
       * @param pixelHeight  pixel height of the current canvas
       * @param ctx  the canvas 2d context
       */
      renderJunction(feature, bpStart, xScale, pixelHeight, ctx) {
        // cache whether this junction is rendered or filtered out. Use later to exclude non-rendered junctions from click detection.
        feature.isVisible = false;
        const junctionLeftPx = Math.round((feature.start - bpStart) / xScale);
        const junctionRightPx = Math.round((feature.end - bpStart) / xScale);
        const junctionMiddlePx = (junctionLeftPx + junctionRightPx) / 2;

        if (junctionRightPx - junctionLeftPx <= 3) {
          if (junctionMiddlePx in junctionRenderingContext.featureZoomOutTracker) {
            return;
          }

          junctionRenderingContext.featureZoomOutTracker[junctionMiddlePx] = true;
        } // TODO: cache filter and pixel calculations by doing them earlier when features are initially parsed?


        if (this.config.hideAnnotatedJunctions && feature.attributes.annotated_junction === "true") {
          return;
        }

        if (this.config.hideUnannotatedJunctions && feature.attributes.annotated_junction === "false") {
          return;
        }

        if (this.config.hideMotifs && this.config.hideMotifs.includes(feature.attributes.motif)) {
          return;
        }

        if (this.config.hideStrand === feature.strand) {
          return;
        } // check if splice junction is inside viewport


        if (this.config.minJunctionEndsVisible) {
          let numJunctionEndsVisible = 0;

          if (feature.start >= junctionRenderingContext.referenceFrameStart && feature.start <= junctionRenderingContext.referenceFrameEnd) {
            numJunctionEndsVisible += 1;
          }

          if (feature.end >= junctionRenderingContext.referenceFrameStart && feature.end <= junctionRenderingContext.referenceFrameEnd) {
            numJunctionEndsVisible += 1;
          }

          if (numJunctionEndsVisible < this.config.minJunctionEndsVisible) {
            return;
          }
        }

        let uniquelyMappedReadCount;
        let multiMappedReadCount;
        let totalReadCount;

        if (feature.attributes.uniquely_mapped) {
          uniquelyMappedReadCount = parseInt(feature.attributes.uniquely_mapped);

          if (uniquelyMappedReadCount < this.config.minUniquelyMappedReads) {
            return;
          }

          multiMappedReadCount = parseInt(feature.attributes.multi_mapped);
          totalReadCount = uniquelyMappedReadCount + multiMappedReadCount;

          if (totalReadCount < this.config.minTotalReads) {
            return;
          }

          if (totalReadCount > 0 && multiMappedReadCount / totalReadCount > this.config.maxFractionMultiMappedReads) {
            return;
          }

          if (feature.attributes.maximum_spliced_alignment_overhang && parseInt(feature.attributes.maximum_spliced_alignment_overhang) < this.config.minSplicedAlignmentOverhang) {
            return;
          }
        }

        let numSamplesWithThisJunction;

        if (feature.attributes.num_samples_with_this_junction) {
          numSamplesWithThisJunction = parseInt(feature.attributes.num_samples_with_this_junction);

          if (this.config.minSamplesWithThisJunction && numSamplesWithThisJunction < this.config.minSamplesWithThisJunction) {
            return;
          }

          if (this.config.maxSamplesWithThisJunction && numSamplesWithThisJunction > this.config.maxSamplesWithThisJunction) {
            return;
          }

          if (feature.attributes.num_samples_total) {
            feature.attributes.percent_samples_with_this_junction = 100 * numSamplesWithThisJunction / Number(feature.attributes.num_samples_total);

            if (this.config.minPercentSamplesWithThisJunction) {
              if (feature.attributes.percent_samples_with_this_junction < this.config.minPercentSamplesWithThisJunction || feature.attributes.percent_samples_with_this_junction > this.config.maxPercentSamplesWithThisJunction) {
                return;
              }
            }
          }
        }

        const py = this.margin;
        const rowHeight = this.height;
        const cy = py + 0.5 * rowHeight;
        let topY = py;
        const bottomY = py + rowHeight;
        const bezierBottomY = bottomY - 10; // draw the junction arc

        const bezierControlLeftPx = (junctionLeftPx + junctionMiddlePx) / 2;
        const bezierControlRightPx = (junctionMiddlePx + junctionRightPx) / 2;
        let lineWidth = 1;

        if (feature.attributes.line_width) {
          lineWidth = Number(feature.attributes.line_width);
        } else {
          if (this.config.thicknessBasedOn === undefined || this.config.thicknessBasedOn === 'numUniqueReads') {
            lineWidth = uniquelyMappedReadCount;
          } else if (this.config.thicknessBasedOn === 'numReads') {
            lineWidth = totalReadCount;
          } else if (this.config.thicknessBasedOn === 'numSamplesWithThisJunction') {
            if (numSamplesWithThisJunction !== undefined) {
              lineWidth = numSamplesWithThisJunction;
            }
          }

          lineWidth = 1 + Math.log(lineWidth + 1) / Math.log(12);
        }

        let bounceHeight;

        if (this.config.bounceHeightBasedOn === undefined || this.config.bounceHeightBasedOn === 'random') {
          // randomly but deterministically stagger topY coordinates to reduce overlap
          bounceHeight = (feature.start + feature.end) % 7;
        } else if (this.config.bounceHeightBasedOn === 'distance') {
          bounceHeight = 6 * (feature.end - feature.start) / (junctionRenderingContext.referenceFrameEnd - junctionRenderingContext.referenceFrameStart);
        } else if (this.config.bounceHeightBasedOn === 'thickness') {
          bounceHeight = 2 * lineWidth;
        }

        topY += rowHeight * Math.max(7 - bounceHeight, 0) / 10;
        let color;

        if (feature.attributes.color) {
          color = feature.attributes.color; // Explicit setting
        } else if (this.config.colorBy === undefined || this.config.colorBy === 'numUniqueReads') {
          color = uniquelyMappedReadCount > this.config.colorByNumReadsThreshold ? 'blue' : '#AAAAAA'; // color gradient?
        } else if (this.config.colorBy === 'numReads') {
          color = totalReadCount > this.config.colorByNumReadsThreshold ? 'blue' : '#AAAAAA';
        } else if (this.config.colorBy === 'isAnnotatedJunction') {
          color = feature.attributes.annotated_junction === "true" ? '#b0b0ec' : 'orange';
        } else if (this.config.colorBy === 'strand') {
          color = feature.strand === "+" ? '#b0b0ec' : '#ecb0b0';
        } else if (this.config.colorBy === 'motif') {
          color = JUNCTION_MOTIF_PALETTE.getColor(feature.attributes.motif);
        } else {
          color = '#AAAAAA';
        }

        let label = "";

        if (feature.attributes.label) {
          label = feature.attributes.label.replace(/_/g, " ");
        } else if (this.config.labelWith === undefined || this.config.labelWith === 'uniqueReadCount') {
          //default label
          label = uniquelyMappedReadCount;
        } else if (this.config.labelWith === 'totalReadCount') {
          label = totalReadCount;
        } else if (this.config.labelWith === 'numSamplesWithThisJunction') {
          if (numSamplesWithThisJunction !== undefined) {
            label = numSamplesWithThisJunction;
          }
        } else if (this.config.labelWith === 'percentSamplesWithThisJunction') {
          if (feature.attributes.percent_samples_with_this_junction !== undefined) {
            label = feature.attributes.percent_samples_with_this_junction.toFixed(0) + '%';
          }
        } else if (this.config.labelWith === 'motif') {
          if (feature.attributes.motif !== undefined) {
            label += feature.attributes.motif;
          }
        }

        if (this.config.labelWithInParen === 'uniqueReadCount') {
          label += ' (' + uniquelyMappedReadCount + ')';
        } else if (this.config.labelWithInParen === 'totalReadCount') {
          label += ' (' + totalReadCount + ')';
        } else if (this.config.labelWithInParen === 'multiMappedReadCount') {
          if (multiMappedReadCount > 0) {
            label += ' (+' + multiMappedReadCount + ')';
          }
        } else if (this.config.labelWithInParen === 'numSamplesWithThisJunction') {
          if (numSamplesWithThisJunction !== undefined) {
            label += ' (' + numSamplesWithThisJunction + ')';
          }
        } else if (this.config.labelWithInParen === 'percentSamplesWithThisJunction') {
          if (feature.attributes.percent_samples_with_this_junction !== undefined) {
            label += ' (' + feature.attributes.percent_samples_with_this_junction.toFixed(0) + '%)';
          }
        } else if (this.config.labelWithInParen === 'motif') {
          if (feature.attributes.motif !== undefined) {
            label += ` ${feature.attributes.motif}`;
          }
        } // data source: STAR splice junctions (eg. SJ.out.tab file converted to bed).
        // .bed "name" field used to store unique + multi-mapped read counts, so:
        // feature.score:  unique spanning read counts
        // feature.name:   unique + multi-mapped spanning read counts
        //example feature:  { chr: "chr17", start: 39662344, end: 39662803, name: "59", row: 0, score: 38, strand: "+"}


        feature.isVisible = true;
        ctx.beginPath();
        ctx.moveTo(junctionLeftPx, bezierBottomY);
        ctx.bezierCurveTo(bezierControlLeftPx, topY, bezierControlRightPx, topY, junctionRightPx, bezierBottomY);
        ctx.lineWidth = lineWidth;
        ctx.strokeStyle = color;
        ctx.stroke();

        const drawArrowhead = (ctx, x, y, size) => {
          //TODO draw better arrow heads: https://stackoverflow.com/questions/21052972/curved-thick-arrows-on-canvas
          ctx.beginPath();
          ctx.moveTo(x, y);
          ctx.lineTo(x - size / 2, y - size);
          ctx.lineTo(x + size / 2, y - size);
          ctx.lineTo(x, y);
          ctx.closePath();
          ctx.fill();
        };

        if (feature.attributes.left_shape || feature.attributes.right_shape) {
          ctx.fillStyle = color;
          const arrowSize = ctx.lineWidth > 2 ? 10 : 7;

          if (feature.attributes.left_shape) {
            drawArrowhead(ctx, junctionLeftPx, bezierBottomY, arrowSize);
          }

          if (feature.attributes.right_shape) {
            drawArrowhead(ctx, junctionRightPx, bezierBottomY, arrowSize);
          }
        }

        ctx.fillText(label, junctionMiddlePx - ctx.measureText(label).width / 2, (7 * topY + cy) / 8);
      }

      clickedFeatures(clickState, features) {
        const allFeatures = super.clickedFeatures(clickState, features);
        return allFeatures.filter(function (feature) {
          return feature.isVisible && feature.attributes;
        });
      }
      /**
       * Return "popup data" for feature @ genomic location.  Data is an array of key-value pairs
       */


      popupData(clickState, features) {
        features = this.clickedFeatures(clickState, features);
        const genomicLocation = clickState.genomicLocation;
        const data = [];

        for (let feature of features) {
          const featureData = typeof feature.popupData === "function" ? feature.popupData(genomicLocation) : this.extractPopupData(feature._f || feature, this.getGenomeId());

          if (featureData) {
            if (data.length > 0) {
              data.push("<hr/><hr/>");
            }

            Array.prototype.push.apply(data, featureData);
          }
        }

        return data;
      }
      /**
       * Called when the track is removed.  Do any needed cleanup here
       */


      dispose() {
        this.trackView = undefined;
      }

    }

    const trackFunctions = new Map([['ideogram', (config, browser) => new IdeogramTrack(config, browser)], ['sequence', (config, browser) => new SequenceTrack(config, browser)], ['feature', (config, browser) => new FeatureTrack(config, browser)], ['seg', (config, browser) => new SegTrack(config, browser)], ['mut', (config, browser) => new SegTrack(config, browser)], ['maf', (config, browser) => new SegTrack(config, browser)], ['wig', (config, browser) => new WigTrack(config, browser)], ['merged', (config, browser) => new MergedTrack(config, browser)], ['alignment', (config, browser) => new BAMTrack(config, browser)], ['interaction', (config, browser) => new InteractionTrack(config, browser)], ['interact', (config, browser) => new InteractionTrack(config, browser)], ['variant', (config, browser) => new VariantTrack(config, browser)], ['eqtl', (config, browser) => new EqtlTrack(config, browser)], ['gwas', (config, browser) => new GWASTrack(config, browser)], ['arc', (config, browser) => new RnaStructTrack(config, browser)], ['gcnv', (config, browser) => new GCNVTrack(config, browser)], ['junction', (config, browser) => new SpliceJunctionTrack(config, browser)]]);
    /**
     * Add a track constructor  the the factory lookup table.
     *
     * @param type
     * @param track
     */

    const addTrackCreatorFunction = function (type, track) {
      trackFunctions.set(type, track);
    };

    const getTrack = function (type, config, browser) {
      let trackKey;

      switch (type) {
        case "annotation":
        case "genes":
        case "fusionjuncspan":
        case "snp":
          trackKey = "feature";
          break;

        case 'seg':
        case 'maf':
        case 'mut':
          trackKey = 'seg';
          break;

        case 'junctions':
        case 'splicejunctions':
          trackKey = 'junction';
          break;

        default:
          trackKey = type;
      }

      return trackFunctions.has(trackKey) ? trackFunctions.get(trackKey)(config, browser) : undefined;
    };

    var TrackFactory = {
      tracks: trackFunctions,
      addTrack: addTrackCreatorFunction,
      getTrack
    };

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    var defaultHighlightColor = "rgba(68, 134, 247, 0.25)";

    class ROI {
      constructor(config, genome) {
        this.config = config;
        this.name = config.name;
        this.roiSource = FeatureSource(config, genome);
        this.color = config.color || defaultHighlightColor;
      }

      async getFeatures(chr, start, end) {
        return this.roiSource.getFeatures({
          chr,
          start,
          end
        });
      }

      draw(drawConfiguration) {
        var endBP, region, coord, regions;
        regions = drawConfiguration.features;

        if (!regions) {
          return;
        }

        endBP = drawConfiguration.bpStart + (drawConfiguration.pixelWidth * drawConfiguration.bpPerPixel + 1);

        for (var i = 0, len = regions.length; i < len; i++) {
          region = regions[i];

          if (region.end < drawConfiguration.bpStart) {
            continue;
          }

          if (region.start > endBP) {
            break;
          }

          coord = coordinates(region, drawConfiguration.bpStart, drawConfiguration.bpPerPixel);
          IGVGraphics.fillRect(drawConfiguration.context, coord.x, drawConfiguration.pixelTop, coord.width, drawConfiguration.pixelHeight, {
            fillStyle: this.color
          });
        }
      }

    }

    function coordinates(region, startBP, bpp) {
      var ss, ee, width;
      ss = Math.round((region.start - startBP) / bpp);
      ee = Math.round((region.end - startBP) / bpp);
      width = ee - ss;

      if (width < 3) {
        width = 3;
        ss -= 1;
      }

      return {
        x: ss,
        width: width
      };
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    /**
     * Minimal support for the legacy IGV desktop session format.
     */
    class XMLSession {
      constructor(xmlString, knownGenomes) {
        const parser = new DOMParser();
        const xmlDoc = parser.parseFromString(xmlString, "text/xml");
        this.processRootNode(xmlDoc, knownGenomes);
        const resourceElements = xmlDoc.getElementsByTagName("Resource");
        const trackElements = xmlDoc.getElementsByTagName("Track");
        const hasTrackElements = trackElements && trackElements.length > 0;
        const tracks = [];
        this.tracks = tracks;
        const resourceMap = new Map();
        Array.from(resourceElements).forEach(function (r, idx) {
          var config = {
            url: r.getAttribute("path"),
            indexURL: r.getAttribute("index"),
            order: idx
          };
          resourceMap.set(config.url, config);

          if (!hasTrackElements) {
            tracks.push(config);
          }
        }); // Check for optional Track section

        if (hasTrackElements) {
          Array.from(trackElements).forEach(function (track) {
            const subtracks = track.getElementsByTagName("Track");

            if (subtracks && subtracks.length > 0) {
              const mergedTrack = {
                type: 'merged',
                tracks: []
              };
              extractTrackAttributes(track, mergedTrack);
              tracks.push(mergedTrack);
              Array.from(subtracks).forEach(function (t) {
                t.processed = true;
                const id = t.getAttribute("id");
                const config = resourceMap.get(id);

                if (config) {
                  mergedTrack.tracks.push(config);
                  extractTrackAttributes(t, config);
                  config.autoscale = false;
                  mergedTrack.height = config.height; // Add alpha for merged track colors.  Alpha is not recorded by IGV desktop in XML session
                  //const color = t.getAttribute("color");
                  //if (color) {
                  //    config.color = "rgba(" + color + ",0.5)";
                  //}
                }
              });
            } else if (!track.processed) {
              const id = track.getAttribute("id");
              const res = resourceMap.get(id);

              if (res) {
                tracks.push(res);
                extractTrackAttributes(track, res);
              }
            }
          });
        }
      }

      processRootNode(xmlDoc, knownGenomes) {
        const elements = xmlDoc.getElementsByTagName("Session");

        if (!elements || elements.length === 0) ;

        const session = elements.item(0);
        const genome = session.getAttribute("genome");
        const locus = session.getAttribute("locus");
        const ucscID = session.getAttribute("ucscID");

        if (knownGenomes && knownGenomes.hasOwnProperty(genome)) {
          this.genome = genome;
        } else {
          this.reference = {
            fastaURL: genome
          };

          if (ucscID) {
            this.reference.id = ucscID;
          }
        }

        if (locus) {
          this.locus = locus;
        }
      }

    }

    function extractTrackAttributes(track, config) {
      config.name = track.getAttribute("name");
      const color = track.getAttribute("color");

      if (color) {
        config.color = "rgb(" + color + ")";
      }

      const altColor = track.getAttribute("altColor");

      if (color) {
        config.altColor = "rgb(" + altColor + ")";
      }

      const height = track.getAttribute("height");

      if (height) {
        config.height = parseInt(height);
      }

      const autoScale = track.getAttribute("autoScale");

      if (autoScale) {
        config.autoscale = autoScale === "true";
      }

      const autoscaleGroup = track.getAttribute("autoscaleGroup");

      if (autoscaleGroup) {
        config.autoscaleGroup = autoscaleGroup;
      }

      const windowFunction = track.getAttribute("windowFunction");

      if (windowFunction) {
        config.windowFunction = windowFunction;
      }

      const visWindow = track.getAttribute("visibilityWindow") || track.getAttribute("featureVisibilityWindow");

      if (visWindow) {
        config.visibilityWindow = visWindow;
      }

      const indexed = track.getAttribute("indexed");

      if (indexed) {
        config.indexed = indexed === "true";
      }

      const normalize = track.getAttribute("normalize");

      if (normalize) {
        config.normalize = normalize === "true";
      }

      const dataRangeCltn = track.getElementsByTagName("DataRange");

      if (dataRangeCltn.length > 0) {
        const dataRange = dataRangeCltn.item(0);
        config.min = Number(dataRange.getAttribute("minimum"));
        config.max = Number(dataRange.getAttribute("maximum"));
        config.logScale = dataRange.getAttribute("type") === "LOG";
      }
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014-2015 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const splitLines = splitLines$5;

    class SampleInformation {
      constructor() {
        this.attributes = {};
        this.plinkLoaded = false;
      }

      async loadPlinkFile(url, config) {
        if (!config) config = {};
        var options = buildOptions(config); // Add oauth token, if any

        const data = await igvxhr.loadString(url, options);
        var lines = splitLines(data);

        for (let line of lines) {
          var line_arr = line.split(' ');
          this.attributes[line_arr[1]] = {
            familyId: line_arr[0],
            fatherId: line_arr[2],
            motherId: line_arr[3],
            sex: line_arr[4],
            phenotype: line_arr[5]
          };
        }

        this.plinkLoaded = true;
        return this;
      }
      /**
       * Return the attributes for the given sample as a map-like object (key-value pairs)
       * @param sample
       */


      getAttributes(sample) {
        return this.attributes[sample];
      }

      getAttributeNames() {
        if (this.hasAttributes()) {
          return Object.keys(this.attributes[Object.keys(this.attributes)[0]]);
        } else return [];
      }

      hasAttributes() {
        return Object.keys(this.attributes).length > 0;
      }

    }

    function loadPlinkFile(url, config) {
      const si = new SampleInformation();
      return si.loadPlinkFile(url, config);
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    class GtexSelection {
      constructor(gene, snp) {
        this.geneColors = {};
        this.gene = null;
        this.snp = null;
        this.genesCount = 0;

        if (gene) {
          this.gene = gene.toUpperCase();
          this.geneColors[this.gene] = brewer[this.genesCount++];
        }

        if (snp) {
          this.snp = snp.toUpperCase();
        }
      }

      addGene(geneName) {
        if (!this.geneColors[geneName.toUpperCase()]) {
          this.geneColors[geneName.toUpperCase()] = brewer[this.genesCount++];
        }
      }

      colorForGene(geneName) {
        return this.geneColors[geneName.toUpperCase()];
      }

    }

    var brewer = []; // Set +!

    brewer.push("rgb(228,26,28)");
    brewer.push("rgb(55,126,184)");
    brewer.push("rgb(77,175,74)");
    brewer.push("rgb(166,86,40)");
    brewer.push("rgb(152,78,163)");
    brewer.push("rgb(255,127,0)");
    brewer.push("rgb(247,129,191)");
    brewer.push("rgb(153,153,153)");
    brewer.push("rgb(255,255,51)"); // #Set 2

    brewer.push("rgb(102, 194, 165");
    brewer.push("rgb(252, 141, 98");
    brewer.push("rgb(141, 160, 203");
    brewer.push("rgb(231, 138, 195");
    brewer.push("rgb(166, 216, 84");
    brewer.push("rgb(255, 217, 47");
    brewer.push("rgb(229, 196, 148");
    brewer.push("rgb(179, 179, 179"); //#Set 3

    brewer.push("rgb( 141, 211, 199");
    brewer.push("rgb(255, 255, 179");
    brewer.push("rgb(190, 186, 218");
    brewer.push("rgb(251, 128, 114");
    brewer.push("rgb(128, 177, 211");
    brewer.push("rgb(253, 180, 98");
    brewer.push("rgb(179, 222, 105");
    brewer.push("rgb(252, 205, 229");
    brewer.push("rgb(217, 217, 217");
    brewer.push("rgb(188, 128, 189");
    brewer.push("rgb(204, 235, 197");
    brewer.push("rgb(255, 237, 111");

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class ReferenceFrame {
      constructor(genome, chr, start, end, bpPerPixel) {
        this.genome = genome;
        this.chr = chr;
        this.start = start; // TODO WARNING THIS IS NOT UPDATED !!!

        this.end = end;
        this.bpPerPixel = bpPerPixel;
        this.id = guid$2();
      }

      extend(locus) {
        const newStart = Math.min(locus.start, this.start);
        const newEnd = Math.max(locus.end, this.end);
        const ratio = (newEnd - newStart) / (this.end - this.start);
        this.start = newStart;
        this.end = newEnd;
        this.bpPerPixel *= ratio;
      }

      calculateEnd(pixels) {
        return this.start + this.bpPerPixel * pixels;
      }

      calculateBPP(end, pixels) {
        return (end - this.start) / pixels;
      }

      set(json) {
        this.chr = json.chr;
        this.start = json.start;
        this.bpPerPixel = json.bpPerPixel;
      }

      toPixels(bp) {
        return bp / this.bpPerPixel;
      }

      toBP(pixels) {
        return this.bpPerPixel * pixels;
      }
      /**
       * Shift frame by stated pixels.  Return true if view changed, false if not.
       * @param pixels
       * @param viewportWidth
       */


      shiftPixels(pixels, viewportWidth) {
        const currentStart = this.start;
        const deltaBP = pixels * this.bpPerPixel;
        this.start += deltaBP;
        this.clampStart(viewportWidth);
        this.end += deltaBP;
        const {
          bpLength
        } = this.genome.getChromosome(this.chr);
        this.end = Math.min(bpLength, this.end);
        return currentStart !== this.start;
      }

      clampStart(viewportWidth) {
        // clamp left
        const min = this.genome.getChromosome(this.chr).bpStart || 0;
        this.start = Math.max(min, this.start); // clamp right

        if (viewportWidth) {
          const {
            bpLength
          } = this.genome.getChromosome(this.chr);
          const maxStart = bpLength - viewportWidth * this.bpPerPixel;

          if (this.start > maxStart) {
            this.start = maxStart;
          }
        }
      }

      async zoomWithScaleFactor(browser, scaleFactor, viewportWidth, centerBPOrUndefined) {
        const centerBP = undefined === centerBPOrUndefined ? this.start + this.toBP(viewportWidth / 2.0) : centerBPOrUndefined; // save initial start and bpp

        const {
          start,
          bpPerPixel
        } = this.start;
        const {
          bpLength
        } = this.getChromosome();
        const bppThreshold = scaleFactor < 1.0 ? browser.minimumBases() / viewportWidth : bpLength / viewportWidth; // update bpp

        if (scaleFactor < 1.0) {
          this.bpPerPixel = Math.max(this.bpPerPixel * scaleFactor, bppThreshold);
        } else {
          this.bpPerPixel = Math.min(this.bpPerPixel * scaleFactor, bppThreshold);
        } // update start and end


        const widthBP = this.bpPerPixel * viewportWidth;
        this.start = centerBP - 0.5 * widthBP;
        this.clampStart(viewportWidth);
        this.end = this.start + widthBP;
        const viewChanged = start !== this.start || bpPerPixel !== this.bpPerPixel;

        if (viewChanged) {
          await browser.updateViews(true);
        }
      }

      getChromosome() {
        return this.genome.getChromosome(this.chr);
      }

      getMultiLocusLabel(pixels) {
        const margin = '&nbsp';
        const space = '&nbsp &nbsp &nbsp';
        const ss = Math.floor(this.start) + 1;
        const ee = Math.round(this.start + this.bpPerPixel * pixels);
        return `${margin}${this.chr}${space}${prettyBasePairNumber(ee - ss)}${margin}`;
      }

      getPresentationLocusComponents(pixels) {
        if ('all' === this.chr) {
          return {
            chr: this.chr
          };
        } else {
          const ss = numberFormatter$1(Math.floor(this.start) + 1);
          const ee = numberFormatter$1(Math.round(this.start + this.bpPerPixel * pixels));
          return {
            chr: this.chr,
            start: ss,
            end: ee
          };
        }
      }

      getLocusString() {
        if ('all' === this.chr) {
          return 'all';
        } else {
          const ss = numberFormatter$1(Math.floor(this.start) + 1);
          const ee = numberFormatter$1(Math.round(this.end));
          return `${this.chr}:${ss}-${ee}`;
        }
      }

      description(blurb) {
        console.log(` ${blurb || ''} referenceFrame - ${this.chr} bpp ${this.bpPerPixel.toFixed(3)} start ${numberFormatter$1(Math.round(this.start))} end ${numberFormatter$1(Math.round(this.end))} `);
      }

    }

    function createReferenceFrameList(loci, genome, browserFlanking, minimumBases, viewportWidth) {
      return loci.map(locus => {
        // If a flanking region is defined, and the search object is a symbol ("gene") type, adjust start and end
        if (browserFlanking && locus.gene) {
          locus.start = Math.max(0, locus.start - browserFlanking);
          locus.end += browserFlanking;
        } // Validate the range.  This potentionally modifies start & end of locus.


        const chromosome = genome.getChromosome(locus.chr);
        validateLocusExtent(chromosome.bpLength, locus, minimumBases);
        const referenceFrame = new ReferenceFrame(genome, locus.chr, locus.start, locus.end, (locus.end - locus.start) / viewportWidth);
        referenceFrame.locusSearchString = locus.locusSearchString; // GTEX hack

        if (locus.gene || locus.snp) {
          referenceFrame.selection = new GtexSelection(locus.gene, locus.snp);
        }

        return referenceFrame;
      });
    }

    const defaultNucleotideColors = {
      "A": "rgb(  0, 200,   0)",
      "C": "rgb(  0,0,200)",
      "T": "rgb(255,0,0)",
      "G": "rgb(209,113,  5)",
      "N": "rgb(80,80,80)"
    };

    const DEFAULT_SEARCH_CONFIG = {
      timeout: 5000,
      type: "plain",
      // Legacy plain text support -- deprecated
      url: 'https://igv.org/genomes/locus.php?genome=$GENOME$&name=$FEATURE$',
      coords: 0,
      chromosomeField: "chromosome",
      startField: "start",
      endField: "end",
      geneField: "gene",
      snpField: "snp"
    };
    /**
     * Return an object representing the locus of the given string.  Object is of the form
     * {
     *   chr,
     *   start,
     *   end,
     *   locusSearchString,
     *   gene,
     *   snp
     * }
     * @param browser
     * @param string
     * @returns {Promise<*>}
     */

    async function search(browser, string) {
      if (undefined === string || '' === string.trim()) {
        return;
      }

      if (string && string.trim().toLowerCase() === "all" || string === "*") {
        string = "all";
      }

      const loci = string.split(' ');
      let searchConfig = browser.searchConfig || DEFAULT_SEARCH_CONFIG;
      let list = [];

      const searchLocus = async locus => {
        let locusObject = parseLocusString(browser, locus);

        if (!locusObject) {
          const feature = browser.genome.featureDB[locus.toUpperCase()];

          if (feature) {
            locusObject = {
              chr: feature.chr,
              start: feature.start,
              end: feature.end,
              gene: feature.name,
              locusSearchString: string
            };
          }
        }

        if (!locusObject && browser.config && false !== browser.config.search) {
          try {
            locusObject = await searchWebService(browser, locus, searchConfig);
          } catch (error) {
            console.error(error);
            throw Error("Search service currently unavailable.");
          }
        }

        return locusObject;
      };

      for (let locus of loci) {
        const locusObject = await searchLocus(locus);

        if (locusObject) {
          locusObject.locusSearchString = locus;
          list.push(locusObject);
        }
      } // If nothing is found, consider possibility that loci name itself has spaces


      if (list.length === 0) {
        const locusObject = await searchLocus(string);

        if (locusObject) {
          locusObject.locusSearchString = string;
          list.push(locusObject);
        }
      }

      return 0 === list.length ? undefined : list;
    }

    function parseLocusString(browser, locus) {
      const a = locus.split(':');
      const chr = a[0];

      if ('all' === chr && browser.genome.getChromosome(chr)) {
        return {
          chr,
          start: 0,
          end: browser.genome.getChromosome(chr).bpLength
        };
      } else if (undefined === browser.genome.getChromosome(chr)) {
        return undefined;
      } else {
        const queryChr = browser.genome.getChromosomeName(chr);
        const extent = {
          chr: queryChr,
          start: 0,
          end: browser.genome.getChromosome(chr).bpLength
        };

        if (a.length > 1) {
          const b = a[1].split('-');

          if (b.length > 2) {
            return undefined;
          } else {
            let numeric;
            numeric = b[0].replace(/,/g, '');

            if (isNaN(numeric)) {
              return undefined;
            }

            extent.start = parseInt(numeric, 10) - 1;
            extent.end = extent.start + 1;

            if (1 === b.length) {
              extent.start -= 20;
              extent.end += 20;
            }

            if (2 === b.length) {
              numeric = b[1].replace(/,/g, '');

              if (isNaN(numeric)) {
                return undefined;
              } else {
                extent.end = parseInt(numeric, 10);
              }
            }
          }
        }

        return extent;
      }
    }

    async function searchWebService(browser, locus, searchConfig) {
      let path = searchConfig.url.replace("$FEATURE$", locus.toUpperCase());

      if (path.indexOf("$GENOME$") > -1) {
        path = path.replace("$GENOME$", browser.genome.id ? browser.genome.id : "hg19");
      }

      const options = searchConfig.timeout ? {
        timeout: searchConfig.timeout
      } : undefined;
      const result = await igvxhr.loadString(path, options);
      const locusObject = processSearchResult(browser, result, searchConfig);

      if (locusObject) {
        locusObject.locusSearchString = locus;
      }

      return locusObject;
    }

    function processSearchResult(browser, result, searchConfig) {
      let results;

      if ('plain' === searchConfig.type) {
        results = parseSearchResults(browser, result);
      } else {
        results = JSON.parse(result);
      }

      if (searchConfig.resultsField) {
        results = results[searchConfig.resultsField];
      }

      if (!results || 0 === results.length) {
        return undefined;
      } else {
        const chromosomeField = searchConfig.chromosomeField || "chromosome";
        const startField = searchConfig.startField || "start";
        const endField = searchConfig.endField || "end";
        const coords = searchConfig.coords || 1;
        let result;

        if (Array.isArray(results)) {
          // Ignoring all but first result for now
          // TODO -- present all and let user select if results.length > 1
          result = results[0];
        } else {
          // When processing search results from Ensembl REST API
          // Example: https://rest.ensembl.org/lookup/symbol/macaca_fascicularis/BRCA2?content-type=application/json
          result = results;
        }

        if (!(result.hasOwnProperty(chromosomeField) && result.hasOwnProperty(startField))) {
          console.error("Search service results must include chromosome and start fields: " + result);
        }

        const chrResult = result[chromosomeField];
        const chromosome = browser.genome.getChromosome(chrResult);

        if (!chromosome) {
          return undefined;
        }

        const chr = chromosome.name;
        let start = result[startField] - coords;
        let end = result[endField];

        if (undefined === end) {
          end = start + 1;
        }

        const locusObject = {
          chr,
          start,
          end
        }; // Some GTEX hacks

        const type = result.type ? result.type : "gene";

        if (searchConfig.geneField && type === "gene") {
          locusObject.gene = result[searchConfig.geneField];
        }

        if (searchConfig.snpField && type === "snp") {
          locusObject.snp = result[searchConfig.snpField];
        }

        return locusObject;
      }
    }
    /**
     * Parse the igv line-oriented (non json) search results.
     * Example
     *    EGFR    chr7:55,086,724-55,275,031    refseq
     *
     */


    function parseSearchResults(browser, data) {
      const linesTrimmed = [];
      const results = [];
      const lines = splitLines$5(data);
      lines.forEach(function (item) {
        if ("" === item) ; else {
          linesTrimmed.push(item);
        }
      });
      linesTrimmed.forEach(function (line) {
        var tokens = line.split("\t"),
            source,
            locusTokens,
            rangeTokens,
            obj;

        if (tokens.length >= 3) {
          locusTokens = tokens[1].split(":");
          rangeTokens = locusTokens[1].split("-");
          source = tokens[2].trim();
          obj = {
            gene: tokens[0],
            chromosome: browser.genome.getChromosomeName(locusTokens[0].trim()),
            start: parseInt(rangeTokens[0].replace(/,/g, '')),
            end: parseInt(rangeTokens[1].replace(/,/g, '')),
            type: "gtex" === source ? "snp" : "gene"
          };
          results.push(obj);
        }
      });
      return results;
    } // Export some functions for unit testing

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class NavbarManager {
      constructor(browser) {
        this.browser = browser;
      }

      navbarDidResize(width, isWholeGenomeView) {
        this.updateNavbar(this.createResponsiveClassSchedule(width, isWholeGenomeView));
      }

      updateNavbar(responsiveClassSchedule) {
        this.browser.$toggle_button_container.removeClass();
        this.browser.$toggle_button_container.addClass(responsiveClassSchedule.$toggle_button_container);
        $$1(this.browser.zoomWidget.zoomContainer).removeClass();
        $$1(this.browser.zoomWidget.zoomContainer).addClass(responsiveClassSchedule.zoomContainer);
      }

      createResponsiveClassSchedule(navbarWidth, isWholeGenomeView) {
        let candidates = {};

        if (isWholeGenomeView) {
          this.browser.windowSizePanel.hide();
        } else {
          this.browser.windowSizePanel.show();
        }

        if (navbarWidth > 990) {
          candidates.$toggle_button_container = 'igv-navbar-toggle-button-container';
          candidates.zoomContainer = 'igv-zoom-widget';
        } else if (navbarWidth > 860) {
          candidates.$toggle_button_container = 'igv-navbar-toggle-button-container';
          candidates.zoomContainer = 'igv-zoom-widget-900';
        } else if (navbarWidth > 540) {
          candidates.$toggle_button_container = 'igv-navbar-toggle-button-container-750';
          candidates.zoomContainer = 'igv-zoom-widget-900';
        } else {
          candidates.$toggle_button_container = 'igv-navbar-toggle-button-container-750';
          candidates.zoomContainer = 'igv-zoom-widget-900';
          this.browser.windowSizePanel.hide();
        }

        if (isWholeGenomeView) {
          candidates['zoomContainer'] = 'igv-zoom-widget-hidden';
        }

        return candidates;
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016-2017 The Regents of the University of California
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    const ChromosomeSelectWidget = function (browser, parent) {
      this.container = div$1({
        class: 'igv-chromosome-select-widget-container'
      });
      parent.appendChild(this.container);
      this.select = document.createElement('select');
      this.select.setAttribute('name', 'chromosome-select-widget');
      this.container.appendChild(this.select);
      this.select.addEventListener('change', () => {
        this.select.blur();

        if (this.select.value !== '') {
          browser.search(this.select.value);
        }
      });
      this.showAllChromosomes = browser.config.showAllChromosomes !== false; // i.e. default to true
    };

    ChromosomeSelectWidget.prototype.show = function () {
      this.container.style.display = 'flex';
    };

    ChromosomeSelectWidget.prototype.hide = function () {
      this.container.style.display = 'none';
    };

    ChromosomeSelectWidget.prototype.update = function (genome) {
      const list = this.showAllChromosomes ? genome.chromosomeNames.slice() : genome.wgChromosomeNames.slice(); // console.log(`${ this.showAllChromosomes ? 'Do' : 'Do not'} show all chromosomes. List ${ list }`)

      if (genome.showWholeGenomeView()) {
        list.unshift('all');
        list.unshift('');
      }

      this.select.innerHTML = '';

      for (let name of list) {
        const option = document.createElement('option');
        option.setAttribute('value', name);
        option.innerText = name;
        this.select.appendChild(option);
      }
    };

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class WindowSizePanel {
      constructor(parent, browser) {
        this.container = div$1({
          class: 'igv-windowsize-panel-container'
        });
        parent.appendChild(this.container);
        browser.on('locuschange', referenceFrameList => {
          this.updatePanel(referenceFrameList);
        });
        this.browser = browser;
      }

      show() {
        this.container.style.display = 'block';
      }

      hide() {
        this.container.style.display = 'none';
      }

      updatePanel(referenceFrameList) {
        const width = this.browser.calculateViewportWidth(this.browser.referenceFrameList.length);
        this.container.innerText = 1 === referenceFrameList.length ? prettyBasePairNumber(Math.round(width * referenceFrameList[0].bpPerPixel)) : '';
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016 University of California San Diego
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class CursorGuide {
      constructor(columnContainer, browser) {
        this.browser = browser;
        this.columnContainer = columnContainer;
        this.horizontalGuide = div$1({
          class: 'igv-cursor-guide-horizontal'
        });
        columnContainer.appendChild(this.horizontalGuide);
        this.verticalGuide = div$1({
          class: 'igv-cursor-guide-vertical'
        });
        columnContainer.appendChild(this.verticalGuide);
        this.addMouseHandler(browser);
        this.setVisibility(browser.config.showCursorGuide);
      }

      addMouseHandler(browser) {
        this.boundMouseMoveHandler = mouseMoveHandler.bind(this);
        this.columnContainer.addEventListener('mousemove', this.boundMouseMoveHandler);

        function mouseMoveHandler(event) {
          const {
            x,
            y
          } = translateMouseCoordinates$1(event, this.columnContainer);
          this.horizontalGuide.style.top = `${y}px`;
          const target = document.elementFromPoint(event.clientX, event.clientY);
          let viewport = undefined;

          if (target.parentElement) {
            if (target.parentElement.classList.contains('igv-viewport-content')) {
              viewport = target.parentElement.parentElement;
            } else if (target.parentElement.classList.contains('igv-viewport') && target.classList.contains('igv-viewport-content')) {
              viewport = target.parentElement;
            }

            if (viewport && browser.getRulerTrackView()) {
              this.verticalGuide.style.left = `${x}px`;
              const columns = browser.root.querySelectorAll('.igv-column');
              let index = undefined;
              const viewportParent = viewport.parentElement;

              for (let i = 0; i < columns.length; i++) {
                if (undefined === index && viewportParent === columns[i]) {
                  index = i;
                }
              }

              const rulerViewport = browser.getRulerTrackView().viewports[index];
              const result = rulerViewport.mouseMove(event);

              if (result) {
                const {
                  start,
                  bp,
                  end
                } = result;
                const interpolant = (bp - start) / (end - start);

                if (this.customMouseHandler) {
                  this.customMouseHandler({
                    start,
                    bp,
                    end,
                    interpolant
                  });
                }
              }
            }
          }
        }
      }

      removeMouseHandler() {
        this.columnContainer.removeEventListener('mousemove', this.boundMouseMoveHandler);
      }

      setVisibility(showCursorGuide) {
        if (true === showCursorGuide) {
          this.show();
        } else {
          this.hide();
        }
      }

      show() {
        this.verticalGuide.style.display = 'block';
        this.horizontalGuide.style.display = 'block';
      }

      hide() {
        this.verticalGuide.style.display = 'none';
        this.horizontalGuide.style.display = 'none';

        if (this.browser.getRulerTrackView()) {
          for (let viewport of this.browser.getRulerTrackView().viewports) {
            viewport.$tooltip.hide();
          }
        }
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016 University of California San Diego
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class CursorGuideButton {
      constructor(browser, parent) {
        this.browser = browser;
        this.button = div$1({
          class: 'igv-navbar-button'
        });
        parent.appendChild(this.button);
        this.button.textContent = 'cursor guide';
        this.button.addEventListener('click', () => {
          browser.cursorGuideVisible = !browser.cursorGuideVisible;
          browser.setCursorGuideVisibility(browser.cursorGuideVisible);
          this.setButtonState(browser.cursorGuideVisible);
        });
        this.setButtonState(browser.cursorGuideVisible);

        if (browser.config.showCursorTrackingGuideButton) {
          this.show();
        } else {
          this.hide();
        }
      }

      setButtonState(cursorGuideVisible) {
        if (true === cursorGuideVisible) {
          this.button.classList.add('igv-navbar-button-clicked');
        } else {
          this.button.classList.remove('igv-navbar-button-clicked');
        }
      }

      show() {
        this.button.style.display = 'block';
        this.setButtonState(this.browser.cursorGuideVisible);
      }

      hide() {
        this.button.style.display = 'none';
      }

    }

    class CenterLineButton {
      constructor(browser, parent) {
        this.browser = browser;
        this.button = div$1({
          class: 'igv-navbar-button'
        });
        parent.appendChild(this.button);
        this.button.textContent = 'center line';
        this.button.addEventListener('click', () => {
          browser.isCenterLineVisible = !browser.isCenterLineVisible;
          browser.setCenterLineVisibility(browser.isCenterLineVisible);
          this.setButtonState(browser.isCenterLineVisible);
        });
        this.setButtonState(browser.isCenterLineVisible);

        if (browser.config.showCenterGuideButton) {
          this.show();
        } else {
          this.hide();
        }
      }

      setButtonState(isCenterLineVisible) {
        if (true === isCenterLineVisible) {
          this.button.classList.add('igv-navbar-button-clicked');
        } else {
          this.button.classList.remove('igv-navbar-button-clicked');
        }
      }

      show() {
        this.isVisible = true;
        this.button.style.display = 'block';
        this.setButtonState(this.browser.isCenterLineVisible);
      }

      hide() {
        this.isVisible = false;
        this.button.style.display = 'none';
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016 University of California San Diego
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class TrackLabelControl {
      constructor(parent, browser) {
        this.button = div$1({
          class: 'igv-navbar-button'
        });
        parent.appendChild(this.button);
        this.button.textContent = 'track labels';
        this.button.addEventListener('click', () => {
          browser.trackLabelsVisible = !browser.trackLabelsVisible;
          this.setState(browser.trackLabelsVisible);
          browser.setTrackLabelVisibility(browser.trackLabelsVisible);
        });
        this.browser = browser;
        this.setVisibility(browser.config.showTrackLabelButton);
        this.setState(browser.trackLabelsVisible);
      }

      setVisibility(showTrackLabelButton) {
        if (true === showTrackLabelButton) {
          this.show();
        } else {
          this.hide();
        }
      }

      setState(trackLabelsVisible) {
        if (true === trackLabelsVisible) {
          this.button.classList.add('igv-navbar-button-clicked');
        } else {
          this.button.classList.remove('igv-navbar-button-clicked');
        }
      }

      show() {
        this.button.style.display = 'block';
        this.setState(this.browser.trackLabelsVisible);
      }

      hide() {
        this.button.style.display = 'none';
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016 University of California San Diego
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class SampleNameControl {
      constructor(parent, browser) {
        this.button = div$1({
          class: 'igv-navbar-button'
        });
        parent.appendChild(this.button);
        this.button.innerText = 'Sample Names';
        this.setState(browser.showSampleNames);
        this.setVisibility(browser.showSampleNameButton);
        this.button.addEventListener('click', () => {
          browser.showSampleNames = !browser.showSampleNames;
          this.setState(browser.showSampleNames);

          for (let {
            sampleNameViewport
          } of browser.trackViews) {
            if (false === browser.showSampleNames) {
              sampleNameViewport.hide();
            } else {
              sampleNameViewport.show();
            }
          }

          browser.layoutChange();
        });
      }

      setVisibility(showSampleNameButton) {
        if (true === showSampleNameButton) {
          this.show();
        } else {
          this.hide();
        }
      }

      setState(showSampleNames) {
        if (true === showSampleNames) {
          this.button.classList.add('igv-navbar-button-clicked');
        } else {
          this.button.classList.remove('igv-navbar-button-clicked');
        }
      }

      hide() {
        this.button.style.display = 'none';
      }

      show() {
        this.button.style.display = 'block';
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016 University of California San Diego
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const sliderMin = 0;
    let sliderMax = 23;
    let sliderValueRaw = 0;

    const ZoomWidget = function (browser, parent) {
      this.browser = browser;
      this.zoomContainer = div$1({
        class: 'igv-zoom-widget'
      });
      parent.appendChild(this.zoomContainer); // zoom out

      this.zoomOutButton = div$1();
      this.zoomContainer.appendChild(this.zoomOutButton);
      this.zoomOutButton.appendChild(createIcon$1('minus-circle'));
      this.zoomOutButton.addEventListener('click', () => {
        // browser.zoomWithScaleFactor(2.0)
        browser.zoomOut();
      }); // Range slider

      const el = div$1();
      this.zoomContainer.appendChild(el);
      this.slider = document.createElement('input');
      this.slider.type = 'range';
      this.slider.min = `${sliderMin}`;
      this.slider.max = `${sliderMax}`;
      el.appendChild(this.slider);
      this.slider.addEventListener('change', e => {
        e.preventDefault();
        e.stopPropagation();
        const referenceFrame = browser.referenceFrameList[0];
        const {
          bpLength
        } = referenceFrame.genome.getChromosome(referenceFrame.chr);
        const {
          end,
          start
        } = referenceFrame;
        const extent = end - start; // bpLength/(end - start)

        const scaleFactor = Math.pow(2, e.target.valueAsNumber); // (end - start) = bpLength/scaleFactor

        const zoomedExtent = bpLength / scaleFactor; // console.log(`zoom-widget - slider ${ e.target.value } scaleFactor ${ scaleFactor } extent-zoomed ${ StringUtils.numberFormatter(Math.round(zoomedExtent)) }`)

        browser.zoomWithScaleFactor(zoomedExtent / extent);
      }); // zoom in

      this.zoomInButton = div$1();
      this.zoomContainer.appendChild(this.zoomInButton);
      this.zoomInButton.appendChild(createIcon$1('plus-circle'));
      this.zoomInButton.addEventListener('click', () => {
        // browser.zoomWithScaleFactor(0.5)
        browser.zoomIn();
      });
      browser.on('locuschange', referenceFrameList => {
        if (this.browser.isMultiLocusMode()) {
          this.disable();
        } else {
          this.enable();
          this.update(referenceFrameList);
        }
      });
    };

    ZoomWidget.prototype.update = function (referenceFrameList) {
      const referenceFrame = referenceFrameList[0];
      const {
        bpLength
      } = referenceFrame.genome.getChromosome(referenceFrame.chr);
      const {
        start,
        end
      } = referenceFrame;
      sliderMax = Math.ceil(Math.log2(bpLength / this.browser.minimumBases()));
      this.slider.max = `${sliderMax}`;
      const scaleFactor = bpLength / (end - start);
      sliderValueRaw = Math.log2(scaleFactor);
      this.slider.value = `${Math.round(sliderValueRaw)}`;
      // console.log(`${ Date.now() } update - slider ${ this.slider.value } scaleFactor ${ Math.round(scaleFactor) } extent ${ StringUtils.numberFormatter(Math.round(extent)) }`)
      // console.log(`update - sliderMin ${ sliderMin } sliderValue ${ this.slider.value } sliderMax ${ sliderMax } scaleFactor ${ scaleFactor.toFixed(3) } derived-scaleFactor ${ derivedScalefactor.toFixed(3) }`)
    };

    ZoomWidget.prototype.enable = function () {
      // this.zoomInButton.style.color = appleCrayonPalette[ 'steel' ];
      // this.zoomInButton.style.pointerEvents = 'auto';
      //
      // this.zoomOutButton.style.color = appleCrayonPalette[ 'steel' ];
      // this.zoomOutButton.style.pointerEvents = 'auto';
      this.slider.disabled = false;
    };

    ZoomWidget.prototype.disable = function () {
      // this.zoomInButton.style.color = appleCrayonPalette[ 'silver' ];
      // this.zoomInButton.style.pointerEvents = 'none';
      //
      // this.zoomOutButton.style.color = appleCrayonPalette[ 'silver' ];
      // this.zoomOutButton.style.pointerEvents = 'none';
      this.slider.disabled = true;
    };

    ZoomWidget.prototype.hide = function () {
      this.zoomContainer.style.display = 'none';
    };

    ZoomWidget.prototype.show = function () {
      this.zoomContainer.style.display = 'block';
    };

    ZoomWidget.prototype.hideSlider = function () {
      this.slider.style.display = 'none';
    };

    ZoomWidget.prototype.showSlider = function () {
      this.slider.style.display = 'block';
    };

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016 University of California San Diego
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    const SVGSaveControl = function (parent, browser) {
      const button = div$1({
        class: 'igv-navbar-button'
      });
      parent.append(button);
      button.textContent = 'Save SVG';
      button.addEventListener('click', () => browser.saveSVGtoFile({}));
    };

    const viewportColumnManager = {
      createColumns: (columnContainer, count) => {
        for (let i = 0; i < count; i++) {
          if (0 === i) {
            createColumn(columnContainer, 'igv-column');
          } else {
            columnContainer.appendChild(div$1({
              class: 'igv-column-shim'
            }));
            createColumn(columnContainer, 'igv-column');
          }
        }
      },
      removeColumnAtIndex: (i, column) => {
        const shim = 0 === i ? column.nextElementSibling : column.previousElementSibling;
        column.remove();
        shim.remove();
      },
      insertAfter: referenceElement => {
        const shim = div$1({
          class: 'igv-column-shim'
        });
        insertElementAfter(shim, referenceElement);
        const column = div$1({
          class: 'igv-column'
        });
        insertElementAfter(column, shim);
        return column;
      },
      insertBefore: (referenceElement, count) => {
        for (let i = 0; i < count; i++) {
          const column = div$1({
            class: 'igv-column'
          });
          insertElementBefore(column, referenceElement);

          if (count > 1 && i > 0) {
            const columnShim = div$1({
              class: 'igv-column-shim'
            });
            insertElementBefore(columnShim, column);
          }
        }
      },
      indexOfColumn: (columnContainer, column) => {
        const allColumns = columnContainer.querySelectorAll('.igv-column');

        for (let i = 0; i < allColumns.length; i++) {
          const c = allColumns[i];

          if (c === column) {
            return i;
          }
        }

        return undefined;
      }
    };

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016 University of California San Diego
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    class ViewportCenterLine {
      constructor(browser, referenceFrame, column) {
        this.browser = browser;
        this.referenceFrame = referenceFrame;
        this.column = column;
        this.container = div$1({
          class: 'igv-center-line'
        });
        column.appendChild(this.container);

        if (browser.isCenterLineVisible) {
          this.show();
        } else {
          this.hide();
        }
      }

      repaint() {
        const left = Math.floor(this.browser.calculateViewportWidth(this.browser.referenceFrameList.length) / 2);

        if (this.referenceFrame) {
          const ppb = 1.0 / this.referenceFrame.bpPerPixel;

          if (ppb > 1) {
            this.container.style.left = `${left}px`;
            this.container.style.width = `${Math.floor(this.referenceFrame.toPixels(1))}px`;
            this.container.classList.remove('igv-center-line-thin');
            this.container.classList.add('igv-center-line-wide');
          } else {
            this.container.style.left = `${left}px`;
            this.container.style.width = '1px';
            this.container.classList.remove('igv-center-line-wide');
            this.container.classList.add('igv-center-line-thin');
          }
        }
      }

      show() {
        this.isVisible = true;
        this.container.style.display = 'block';
        this.repaint();
      }

      hide() {
        this.isVisible = false;
        this.container.style.display = 'none';
      }

      resize() {
        this.repaint();
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    const numberFormatter = numberFormatter$1;

    class RulerTrack {
      constructor(browser) {
        this.browser = browser;
        this.height = 40;
        this.name = "";
        this.id = "ruler";
        this.disableButtons = true;
        this.ignoreTrackMenu = true;
        this.order = Number.MIN_SAFE_INTEGER * 1e-2;
        this.removable = false;
        this.type = 'ruler';
      }

      async getFeatures(chr, start, end) {
        return [];
      }

      computePixelHeight(ignore) {
        return this.height;
      }

      draw(_ref) {
        let {
          context,
          referenceFrame,
          pixelWidth,
          pixelHeight,
          bpPerPixel,
          bpStart
        } = _ref;

        if (GenomeUtils.isWholeGenomeView(referenceFrame.chr)) {
          this.drawWholeGenome({
            context,
            pixelWidth,
            pixelHeight,
            bpPerPixel
          });
        } else {
          this.doDraw({
            context,
            referenceFrame,
            pixelWidth,
            pixelHeight,
            bpStart
          });
        }
      }

      drawWholeGenome(_ref2) {
        let {
          context,
          pixelWidth,
          pixelHeight,
          bpPerPixel
        } = _ref2;
        context.save();
        IGVGraphics.fillRect(context, 0, 0, pixelWidth, pixelHeight, {
          'fillStyle': 'white'
        });

        for (let name of this.browser.genome.wgChromosomeNames) {
          let xBP = this.browser.genome.getCumulativeOffset(name);
          let wBP = this.browser.genome.getChromosome(name).bpLength;
          let x = Math.round(xBP / bpPerPixel);
          let w = Math.round(wBP / bpPerPixel);
          this.renderChromosomeRect(context, x, 0, w, pixelHeight, name);
        }

        context.restore();
      }

      doDraw(_ref3) {
        let {
          context,
          referenceFrame,
          pixelWidth,
          pixelHeight,
          bpStart
        } = _ref3;
        context.clearRect(0, 0, pixelWidth, pixelHeight);
        const tickHeight = 6;
        const shim = 2;
        const bpLength = Math.floor(referenceFrame.toBP(pixelWidth));
        const tick = findSpacing(bpLength, context.isSVG);
        let nTick = Math.floor(bpStart / tick.majorTick) - 1;
        const {
          tickDelta,
          labelLength
        } = calculateDeltas(context, referenceFrame, bpStart, nTick, tick);
        this.browser.referenceFrameList.indexOf(referenceFrame); // console.log(`ruler(${ index }) label-length ${ labelLength > tickDelta ? 'clobbers' : 'less than' } tick-delta ${ StringUtils.numberFormatter(tickDelta)} `)

        let xTick;
        let bp;
        let accumulatedTickDelta = tickDelta;
        const labelLengthShim = 0.25 * labelLength;

        do {
          bp = Math.floor(nTick * tick.majorTick);
          const rulerLabel = `${numberFormatter$1(Math.floor(bp / tick.unitMultiplier))} ${tick.majorUnit}`;
          xTick = Math.round(referenceFrame.toPixels(bp - 1 - bpStart + 0.5));
          const xLabel = Math.round(xTick - context.measureText(rulerLabel).width / 2);

          if (xLabel > 0 && labelLengthShim + labelLength <= accumulatedTickDelta) {
            IGVGraphics.fillText(context, rulerLabel, xLabel, this.height - tickHeight / 0.75);
            accumulatedTickDelta = 0;
          }

          if (xTick > 0) {
            IGVGraphics.strokeLine(context, xTick, this.height - tickHeight, xTick, this.height - shim);
          }

          bp = Math.floor((1 + nTick) * tick.majorTick);
          let pixel = Math.round(referenceFrame.toPixels(bp - 1 - bpStart + 0.5));
          let delta = (pixel - xTick) / 2;
          let xx = xTick + delta;

          if (xx > 0) {
            IGVGraphics.strokeLine(context, xx, this.height - tickHeight, xx, this.height - shim);
          }

          ++nTick;
          accumulatedTickDelta += tickDelta;
        } while (xTick < pixelWidth);

        IGVGraphics.strokeLine(context, 0, this.height - shim, pixelWidth, this.height - shim);
      }

      renderChromosomeRect(ctx, x, y, w, h, name) {
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.font = '12px sans-serif';
        IGVGraphics.strokeLine(ctx, x + w, y, x + w, y + h, {
          strokeStyle: IGVColor.greyScale(191)
        });
        const shortName = name.startsWith("chr") ? name.substring(3) : name;

        if (w > ctx.measureText(shortName).width) {
          IGVGraphics.fillText(ctx, shortName, x + w / 2, y + h / 2, {
            fillStyle: IGVColor.greyScale(68)
          });
        }
      }

      get supportsWholeGenome() {
        return true;
      }

      dispose() {// do stuff
      }

    }

    function findSpacing(bpLength, isSVG) {
      if (bpLength < 10) {
        return new Tick(1, 'bp', 1);
      }

      const nZeroes = Math.floor(Math.log10(bpLength));
      let majorUnit = 'bp';
      let unitMultiplier = 1;

      if (nZeroes > 9) {
        majorUnit = 'gb';
        unitMultiplier = 1e9;
      } else if (nZeroes > 6) {
        majorUnit = 'mb';
        unitMultiplier = 1e6;
      } else if (nZeroes > 3) {
        majorUnit = 'kb';
        unitMultiplier = 1e3;
      }

      const denom = Math.pow(10, nZeroes - 1);
      const nMajorTicks = bpLength / denom; // const threshold = 25

      const threshold = 3 * 25;
      const belowThresholdTick = Math.pow(10, nZeroes - 1);
      const aboveThresholdTick = Math.pow(10, nZeroes) / 2; // console.log(`zeros ${ nZeroes } tick-threshold ${ threshold } ticks ${ nMajorTicks } belowTick ${ StringUtils.numberFormatter(belowThresholdTick) } aboveTick ${ StringUtils.numberFormatter(aboveThresholdTick) }`)

      const majorTick = nMajorTicks < threshold && isSVG !== true ? belowThresholdTick : aboveThresholdTick;
      return new Tick(majorTick, majorUnit, unitMultiplier);
    }

    function calculateDeltas(context, referenceFrame, bpStart, nTick, tick) {
      const tickDelta = getX(referenceFrame, getBP(1 + nTick, tick), bpStart) - getX(referenceFrame, getBP(nTick, tick), bpStart);
      const label = `${numberFormatter$1(Math.floor(getBP(nTick, tick) / tick.unitMultiplier))} ${tick.majorUnit}`;
      const labelLength = Math.floor(context.measureText(label).width);
      return {
        tickDelta,
        labelLength
      };

      function getBP(nTick, tick) {
        return Math.floor(nTick * tick.majorTick);
      }

      function getX(referenceFrame, bp, bpStart) {
        return Math.round(referenceFrame.toPixels(bp - 1 - bpStart + 0.5));
      }
    }

    class Tick {
      constructor(majorTick, majorUnit, unitMultiplier) {
        this.majorTick = majorTick;
        this.minorTick = majorTick / 10.0;
        this.majorUnit = majorUnit;
        this.unitMultiplier = unitMultiplier;
      }

      description(blurb) {
        console.log((blurb || '') + ' tick ' + numberFormatter(this.majorTick) + ' label width ' + numberFormatter(this.labelWidthBP) + ' multiplier ' + this.unitMultiplier);
      }

    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2016 University of California San Diego
     * Author: Jim Robinson
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    const CircularViewControl = function (parent, browser) {
      this.button = div$1({
        class: 'igv-navbar-button'
      });
      parent.appendChild(this.button);
      this.button.textContent = 'circular view';
      this.button.addEventListener('click', () => {
        browser.circularViewVisible = !browser.circularViewVisible; //this.setState(browser.circularViewVisible)
      });
      this.browser = browser;
      this.setVisibility(browser.config.showCircularViewButton);
      this.setState(browser.circularViewVisible);
    };

    CircularViewControl.prototype.setVisibility = function (showCircularViewButton) {
      if (true === showCircularViewButton) {
        this.show();
      } else {
        this.hide();
      }
    };

    CircularViewControl.prototype.setState = function (circularViewVisible) {
      if (true === circularViewVisible) {
        this.button.classList.add('igv-navbar-button-clicked');
      } else {
        this.button.classList.remove('igv-navbar-button-clicked');
      }
    };

    CircularViewControl.prototype.show = function () {
      this.button.style.display = 'block';
      this.setState(this.browser.circularViewVisible);
    };

    CircularViewControl.prototype.hide = function () {
      this.button.style.display = 'none';
    };

    /**
     * User supplied button for the navbar
     */

    const CustomButton = function (parent, browser, b) {
      const button = div$1({
        class: 'igv-navbar-button'
      });
      parent.append(button);
      button.textContent = b.label;
      button.addEventListener('click', () => b.callback(browser));
    };

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */

    const igv_scrollbar_outer_width = 14; // css - $igv-track-drag-column-width: 12px;

    const igv_track_manipulation_handle_width = 12; // css - $igv-track-gear-menu-column-width: 28px;

    const igv_track_gear_menu_column_width = 28; // $igv-column-shim-width: 1px;
    // $igv-column-shim-margin: 2px;

    const column_multi_locus_shim_width = 2 + 1 + 2;
    const defaultSampleNameViewportWidth = 200;

    class Browser {
      constructor(config, parentDiv) {
        this.config = config;
        this.guid = guid$2();
        this.namespace = '.browser_' + this.guid;
        this.parent = parentDiv;
        this.root = div$1({
          class: 'igv-container'
        });
        parentDiv.appendChild(this.root);
        Alert.init(this.root);
        this.columnContainer = div$1({
          class: 'igv-column-container'
        });
        this.root.appendChild(this.columnContainer);
        this.menuPopup = new MenuPopup(this.columnContainer);
        this.initialize(config);
        this.trackViews = [];
        this.constants = {
          dragThreshold: 3,
          scrollThreshold: 5,
          defaultColor: "rgb(0,0,150)",
          doubleClickDelay: config.doubleClickDelay || 500
        }; // Map of event name -> [ handlerFn, ... ]

        this.eventHandlers = {};
        this.addMouseHandlers();
        this.setControls(config);
      }

      initialize(config) {
        if (config.gtex) {
          GtexUtils.gtexLoaded = true;
        }

        this.flanking = config.flanking;
        this.crossDomainProxy = config.crossDomainProxy;
        this.formats = config.formats;
        this.trackDefaults = config.trackDefaults;
        this.nucleotideColors = config.nucleotideColors || defaultNucleotideColors;

        for (let key of Object.keys(this.nucleotideColors)) {
          this.nucleotideColors[key.toLowerCase()] = this.nucleotideColors[key];
        }

        this.trackLabelsVisible = config.showTrackLabels;
        this.isCenterLineVisible = config.showCenterGuide;
        this.cursorGuideVisible = config.showCursorGuide;
        this.showSampleNames = config.showSampleNames;
        this.showSampleNameButton = config.showSampleNameButton;
        this.sampleNameViewportWidth = config.sampleNameViewportWidth || defaultSampleNameViewportWidth;

        if (config.search) {
          this.searchConfig = {
            type: "json",
            url: config.search.url,
            coords: config.search.coords === undefined ? 1 : config.search.coords,
            chromosomeField: config.search.chromosomeField || "chromosome",
            startField: config.search.startField || "start",
            endField: config.search.endField || "end",
            geneField: config.search.geneField || "gene",
            snpField: config.search.snpField || "snp",
            resultsField: config.search.resultsField
          };
        }
      }

      setControls(config) {
        const $navBar = this.createStandardControls(config);
        $navBar.insertBefore($$1(this.columnContainer));
        this.$navigation = $navBar;

        if (false === config.showControls) {
          $navBar.hide();
        }
      }

      createStandardControls(config) {
        this.navbarManager = new NavbarManager(this);
        const $navBar = $$1('<div>', {
          class: 'igv-navbar'
        });
        this.$navigation = $navBar;
        const $navbarLeftContainer = $$1('<div>', {
          class: 'igv-navbar-left-container'
        });
        $navBar.append($navbarLeftContainer); // IGV logo

        const $logo = $$1('<div>', {
          class: 'igv-logo'
        });
        $navbarLeftContainer.append($logo);
        const logoSvg = logo();
        logoSvg.css("width", "34px");
        logoSvg.css("height", "32px");
        $logo.append(logoSvg);
        this.$current_genome = $$1('<div>', {
          class: 'igv-current-genome'
        });
        $navbarLeftContainer.append(this.$current_genome);
        this.$current_genome.text('');
        const $genomicLocation = $$1('<div>', {
          class: 'igv-navbar-genomic-location'
        });
        $navbarLeftContainer.append($genomicLocation); // chromosome select widget

        this.chromosomeSelectWidget = new ChromosomeSelectWidget(this, $genomicLocation.get(0));

        if (undefined === config.showChromosomeWidget) {
          config.showChromosomeWidget = true; // Default to true
        }

        if (true === config.showChromosomeWidget) {
          this.chromosomeSelectWidget.show();
        } else {
          this.chromosomeSelectWidget.hide();
        }

        const $locusSizeGroup = $$1('<div>', {
          class: 'igv-locus-size-group'
        });
        $genomicLocation.append($locusSizeGroup);
        const $searchContainer = $$1('<div>', {
          class: 'igv-search-container'
        });
        $locusSizeGroup.append($searchContainer); // browser.$searchInput = $('<input type="text" placeholder="Locus Search">');

        this.$searchInput = $$1('<input>', {
          class: 'igv-search-input',
          type: 'text',
          placeholder: 'Locus Search'
        });
        $searchContainer.append(this.$searchInput);
        this.$searchInput.change(() => this.doSearch(this.$searchInput.val()));
        const searchIconContainer = div$1({
          class: 'igv-search-icon-container'
        });
        $searchContainer.append($$1(searchIconContainer));
        searchIconContainer.appendChild(createIcon$1("search"));
        searchIconContainer.addEventListener('click', () => this.doSearch(this.$searchInput.val()));
        this.windowSizePanel = new WindowSizePanel($locusSizeGroup.get(0), this);
        const $navbarRightContainer = $$1('<div>', {
          class: 'igv-navbar-right-container'
        });
        $navBar.append($navbarRightContainer);
        const $toggle_button_container = $$1('<div class="igv-navbar-toggle-button-container">');
        $navbarRightContainer.append($toggle_button_container);
        this.$toggle_button_container = $toggle_button_container;
        this.cursorGuide = new CursorGuide(this.columnContainer, this);
        this.cursorGuideButton = new CursorGuideButton(this, $toggle_button_container.get(0));
        this.centerLineButton = new CenterLineButton(this, $toggle_button_container.get(0));
        this.setTrackLabelVisibility(config.showTrackLabels);
        this.trackLabelControl = new TrackLabelControl($toggle_button_container.get(0), this);
        this.sampleNameControl = new SampleNameControl($toggle_button_container.get(0), this);

        if (true === config.showSVGButton) {
          this.svgSaveControl = new SVGSaveControl($toggle_button_container.get(0), this);
        }

        if (config.customButtons) {
          for (let b of config.customButtons) {
            new CustomButton($toggle_button_container.get(0), this, b);
          }
        }

        this.zoomWidget = new ZoomWidget(this, $navbarRightContainer.get(0));

        if (false === config.showNavigation) {
          this.$navigation.hide();
        }

        this.inputDialog = new InputDialog(this.root);
        this.inputDialog.container.id = `igv-input-dialog-${guid$2()}`;
        this.dataRangeDialog = new DataRangeDialog($$1(this.root));
        this.dataRangeDialog.$container.get(0).id = `igv-data-range-dialog-${guid$2()}`;
        this.genericColorPicker = new GenericColorPicker({
          parent: this.columnContainer,
          width: 432
        });
        this.genericColorPicker.container.id = `igv-track-color-picker-${guid$2()}`;
        return $navBar;
      }

      getSampleNameViewportWidth() {
        return false === this.showSampleNames ? 0 : this.sampleNameViewportWidth;
      }

      isMultiLocusMode() {
        return this.referenceFrameList && this.referenceFrameList.length > 1;
      }

      addTrackToFactory(name, track) {
        TrackFactory.addTrack(name, track);
      }

      isMultiLocusWholeGenomeView() {
        if (undefined === this.referenceFrameList || 1 === this.referenceFrameList.length) {
          return false;
        }

        for (let referenceFrame of this.referenceFrameList) {
          if ('all' === referenceFrame.chr.toLowerCase()) {
            return true;
          }
        }

        return false;
      }

      /**
       * Render browse display as SVG
       * @returns {string}
       */
      toSVG() {
        let {
          x,
          y,
          width,
          height
        } = this.columnContainer.getBoundingClientRect();
        const h_render = 8000;
        let context = new ctx({
          width,
          height: h_render,
          backdropColor: 'white',
          multiLocusGap: 0,
          viewbox: {
            x: 0,
            y: 0,
            width,
            height: h_render
          }
        });

        for (let trackView of this.trackViews) {
          trackView.renderSVGContext(context, {
            deltaX: 0,
            deltaY: -y
          });
        } // reset height to trim away unneeded svg canvas real estate. Yes, a bit of a hack.


        context.setHeight(height);
        return context.getSerializedSvg(true);
      }

      renderSVG($container) {
        const svg = this.toSVG();
        $container.empty();
        $container.append(svg);
        return svg;
      }

      saveSVGtoFile(config) {
        let svg = this.toSVG();

        if (config.$container) {
          config.$container.empty();
          config.$container.append(svg);
        }

        const path = config.filename || 'igvjs.svg';
        const data = URL.createObjectURL(new Blob([svg], {
          type: "application/octet-stream"
        }));
        download(path, data);
      }
      /**
       * Initialize a session from an object, json, or by loading from a file.
       *
       * TODO Really should be split into at least 2 functions, load from file and load from object/json
       *
       * @param options
       * @returns {*}
       */


      async loadSession(options) {
        this.roi = [];
        let session;

        if (options.url || options.file) {
          session = await loadSessionFile(options);
        } else {
          session = options;
        }

        return this.loadSessionObject(session);

        async function loadSessionFile(options) {
          const urlOrFile = options.url || options.file;

          if (options.url && (options.url.startsWith("blob:") || options.url.startsWith("data:"))) {
            const json = Browser.uncompressSession(options.url);
            return JSON.parse(json);
          } else {
            let filename = options.filename;

            if (!filename) {
              filename = options.url ? await getFilename(options.url) : options.file.name;
            }

            if (filename.endsWith(".xml")) {
              const knownGenomes = GenomeUtils.KNOWN_GENOMES;
              const string = await igvxhr.loadString(urlOrFile);
              return new XMLSession(string, knownGenomes);
            } else if (filename.endsWith(".json")) {
              return igvxhr.loadJson(urlOrFile);
            } else {
              return undefined;
            }
          }
        }
      }
      /**
       * Note:  public API function
       * @param session
       * @returns {Promise<void>}
       */


      async loadSessionObject(session) {
        // prepare to load a new session, discarding DOM and state
        this.cleanHouseForSession();
        this.showSampleNames = session.showSampleNames || false;
        this.sampleNameControl.setState(this.showSampleNames === true);

        if (session.sampleNameViewportWidth) {
          this.sampleNameViewportWidth = session.sampleNameViewportWidth;
        } // axis column


        createColumn(this.columnContainer, 'igv-axis-column'); // SampleName column

        createColumn(this.columnContainer, 'igv-sample-name-column'); // Track scrollbar column

        createColumn(this.columnContainer, 'igv-scrollbar-column'); // Track drag/reorder column

        createColumn(this.columnContainer, 'igv-track-drag-column'); // Track gear column

        createColumn(this.columnContainer, 'igv-gear-menu-column');
        const genomeConfig = await GenomeUtils.expandReference(session.reference || session.genome);
        await this.loadReference(genomeConfig, session.locus);
        this.centerLineList = this.createCenterLineList(this.columnContainer); // Create ideogram and ruler track.  Really this belongs in browser initialization, but creation is
        // deferred because ideogram and ruler are treated as "tracks", and tracks require a reference frame

        if (false !== session.showIdeogram) {
          const ideogramTrack = new IdeogramTrack(this);
          ideogramTrack.id = 'ideogram';
          this.trackViews.push(new TrackView(this, this.columnContainer, ideogramTrack));
        }

        if (false !== session.showRuler) {
          this.trackViews.push(new TrackView(this, this.columnContainer, new RulerTrack(this)));
        } // Restore gtex selections.


        if (session.gtexSelections) {
          for (let referenceFrame of this.referenceFrameList) {
            for (let s of Object.keys(session.gtexSelections)) {
              const gene = session.gtexSelections[s].gene;
              const snp = session.gtexSelections[s].snp;
              referenceFrame.selection = new GtexSelection(gene, snp);
            }
          }
        }

        if (session.roi) {
          this.roi = [];

          for (let r of session.roi) {
            this.roi.push(new ROI(r, this.genome));
          }
        } // Tracks.  Start with genome tracks, if any, then append session tracks


        const genomeTracks = genomeConfig.tracks || [];
        const trackConfigurations = session.tracks ? genomeTracks.concat(session.tracks) : genomeTracks; // Insure that we always have a sequence track

        const pushSequenceTrack = trackConfigurations.filter(track => track.type === 'sequence').length === 0;

        if (pushSequenceTrack
        /*&& false !== this.config.showSequence*/
        ) {
          trackConfigurations.push({
            type: "sequence",
            order: defaultSequenceTrackOrder
          });
        } // Maintain track order unless explicitly set


        let trackOrder = 1;

        for (let t of trackConfigurations) {
          if (undefined === t.order) {
            t.order = trackOrder++;
          }
        }

        await this.loadTrackList(trackConfigurations); // The ruler and ideogram tracks are not explicitly loaded, but needs updated nonetheless.

        for (let rtv of this.trackViews.filter(tv => tv.track.type === 'ruler' || tv.track.type === 'ideogram')) {
          rtv.updateViews();
        }

        this.updateUIWithReferenceFrameList();
      }

      createCenterLineList(columnContainer) {
        const centerLines = columnContainer.querySelectorAll('.igv-center-line');

        for (let i = 0; i < centerLines.length; i++) {
          centerLines[i].remove();
        }

        const centerLineList = [];
        const viewportColumns = columnContainer.querySelectorAll('.igv-column');

        for (let i = 0; i < viewportColumns.length; i++) {
          centerLineList.push(new ViewportCenterLine(this, this.referenceFrameList[i], viewportColumns[i]));
        }

        return centerLineList;
      }
      /**
       * Load a reference genome object.  This includes the fasta, and optional cytoband, but no tracks.  This method
       * is used by loadGenome and loadSession.
       *
       * @param genomeConfig
       * @param initialLocus
       */


      async loadReference(genomeConfig, initialLocus) {
        const genome = await GenomeUtils.loadGenome(genomeConfig);
        const genomeChange = undefined === this.genome || this.genome.id !== genome.id;
        this.genome = genome;
        this.updateNavbarDOMWithGenome(genome);

        if (genomeChange) {
          this.removeAllTracks();
        }

        let locus = getInitialLocus(initialLocus, genome);
        const locusFound = await this.search(locus, true);

        if (!locusFound) {
          console.log("Initial locus not found: " + locus);
          locus = genome.getHomeChromosomeName();
          const locusFound = await this.search(locus, true);

          if (!locusFound) {
            throw new Error("Cannot set initial locus");
          }
        }

        if (genomeChange && this.circularView) {
          this.circularView.setAssembly({
            name: this.genome.id,
            id: this.genome.id,
            chromosomes: makeCircViewChromosomes(this.genome)
          });
        }
      }

      cleanHouseForSession() {
        for (let trackView of this.trackViews) {
          // empty axis column, viewport columns, sampleName column, scroll column, drag column, gear column
          trackView.removeDOMFromColumnContainer();
        } // discard all columns


        const elements = this.columnContainer.querySelectorAll('.igv-axis-column, .igv-column-shim, .igv-column, .igv-sample-name-column, .igv-scrollbar-column, .igv-track-drag-column, .igv-gear-menu-column');
        elements.forEach(column => column.remove());
        this.trackViews = [];

        if (this.circularView) {
          this.circularView.clearChords();
        }
      }

      updateNavbarDOMWithGenome(genome) {
        this.$current_genome.text(genome.id || '');
        this.$current_genome.attr('title', genome.id || '');
        this.chromosomeSelectWidget.update(genome);
      }
      /**
       * Load a genome, defined by a string ID or a json-like configuration object. This includes a fasta reference
       * as well as optional cytoband and annotation tracks.
       *
       * @param idOrConfig
       * @returns genome
       */


      async loadGenome(idOrConfig) {
        const genomeConfig = await GenomeUtils.expandReference(idOrConfig);
        await this.loadReference(genomeConfig, undefined);
        const tracks = genomeConfig.tracks || []; // Insure that we always have a sequence track

        const pushSequenceTrack = tracks.filter(track => track.type === 'sequence').length === 0;

        if (pushSequenceTrack) {
          tracks.push({
            type: "sequence",
            order: defaultSequenceTrackOrder
          });
        }

        await this.loadTrackList(tracks);
        await this.updateViews();
        return this.genome;
      }
      /**
       * Called after a session load, search, pan (horizontal drag), or resize
       *
       * @param referenceFrameList
       */


      updateUIWithReferenceFrameList() {
        const referenceFrameList = this.referenceFrameList;
        this.updateLocusSearchWidget();
        const isWGV = this.isMultiLocusWholeGenomeView() || GenomeUtils.isWholeGenomeView(referenceFrameList[0].chr);
        this.navbarManager.navbarDidResize(this.$navigation.width(), isWGV);
        toggleTrackLabels(this.trackViews, this.trackLabelsVisible);
        this.setCenterLineAndCenterLineButtonVisibility(!GenomeUtils.isWholeGenomeView(referenceFrameList[0].chr));
      }

      setTrackLabelVisibility(isVisible) {
        toggleTrackLabels(this.trackViews, isVisible);
      } // cursor guide


      setCursorGuideVisibility(cursorGuideVisible) {
        if (cursorGuideVisible) {
          this.cursorGuide.show();
        } else {
          this.cursorGuide.hide();
        }
      }

      setCustomCursorGuideMouseHandler(mouseHandler) {
        this.cursorGuide.customMouseHandler = mouseHandler;
      } // center line


      setCenterLineVisibility(isCenterLineVisible) {
        for (let centerLine of this.centerLineList) {
          if (true === isCenterLineVisible) {
            centerLine.show();
            centerLine.repaint();
          } else {
            centerLine.hide();
          }
        }
      }

      setCenterLineAndCenterLineButtonVisibility(isCenterLineVisible) {
        for (let centerLine of this.centerLineList) {
          const isShown = isCenterLineVisible && centerLine.isVisible;
          isShown ? centerLine.show() : centerLine.container.style.display = 'none';
        }

        const isShown = isCenterLineVisible && this.centerLineButton.isVisible;
        isShown ? this.centerLineButton.show() : this.centerLineButton.button.style.display = 'none';
      }

      async loadTrackList(configList) {
        const promises = [];

        for (let config of configList) {
          promises.push(this.loadTrack(config));
        }

        const loadedTracks = await Promise.all(promises);
        const groupAutoscaleViews = this.trackViews.filter(function (trackView) {
          return trackView.track.autoscaleGroup;
        });

        if (groupAutoscaleViews.length > 0) {
          this.updateViews();
        }

        return loadedTracks;
      }

      async loadROI(config) {
        if (!this.roi) {
          this.roi = [];
        }

        if (Array.isArray(config)) {
          for (let c of config) {
            this.roi.push(new ROI(c, this.genome));
          }
        } else {
          this.roi.push(new ROI(config, this.genome));
        } // Force reload all views (force = true) to insure ROI features are loaded.  Wasteful but this function is
        // rarely called.


        await this.updateViews(true);
      }

      removeROI(roiToRemove) {
        for (let i = 0; i < this.roi.length; i++) {
          if (this.roi[i].name === roiToRemove.name) {
            this.roi.splice(i, 1);
            break;
          }
        }

        for (let tv of this.trackViews) {
          tv.repaintViews();
        }
      }

      clearROIs() {
        this.roi = [];

        for (let tv of this.trackViews) {
          tv.repaintViews();
        }
      }

      getRulerTrackView() {
        const list = this.trackViews.filter(_ref => {
          let {
            track
          } = _ref;
          return 'ruler' === track.id;
        });
        return list.length > 0 ? list[0] : undefined;
      }
      /**
       * Return a promise to load a track.
       *
       * @param config
       * @param doResize - undefined by default
       * @returns {*}
       */


      async loadTrack(config) {
        // config might be json
        if (isString$3(config)) {
          config = JSON.parse(config);
        }

        try {
          const newTrack = await this.createTrack(config);

          if (undefined === newTrack) {
            Alert.presentAlert(new Error(`Unknown file type: ${config.url || config}`), undefined);
            return newTrack;
          } // Set order field of track here.  Otherwise track order might get shuffled during asynchronous load


          if (undefined === newTrack.order) {
            newTrack.order = this.trackViews.length;
          }

          const trackView = new TrackView(this, this.columnContainer, newTrack);
          this.trackViews.push(trackView);
          toggleTrackLabels(this.trackViews, this.trackLabelsVisible);
          this.reorderTracks();
          this.fireEvent('trackorderchanged', [this.getTrackOrder()]);

          if (typeof newTrack.postInit === 'function') {
            try {
              trackView.startSpinner();
              await newTrack.postInit();
            } finally {
              trackView.stopSpinner();
            }
          }

          if (!newTrack.autoscaleGroup) {
            // Group autoscale will get updated later (as a group)
            if (config.sync) {
              await trackView.updateViews();
            } else {
              trackView.updateViews();
            }
          }

          if (typeof newTrack.hasSamples === 'function' && newTrack.hasSamples()) {
            if (this.config.showSampleNameButton !== false) {
              this.sampleNameControl.show(); // If not explicitly set
            }
          }

          return newTrack;
        } catch (error) {
          const httpMessages = {
            "401": "Access unauthorized",
            "403": "Access forbidden",
            "404": "Not found"
          };
          console.error(error);
          let msg = error.message || error.error || error.toString();

          if (httpMessages.hasOwnProperty(msg)) {
            msg = httpMessages[msg];
          }

          msg += ": " + config.url;
          Alert.presentAlert(new Error(msg), undefined);
        }
      }
      /**
       * Create a Track object.
       * @param config
       * @returns {Promise<*>}
       */


      async createTrack(config) {
        // Resolve function and promise urls
        let url = await resolveURL(config.url);

        if (isString$3(url)) {
          url = url.trim();
        }

        if (url) {
          if (config.format) {
            config.format = config.format.toLowerCase();
          } else {
            let filename = config.filename;

            if (!filename) {
              filename = await getFilename(url);
            }

            config.format = inferFileFormat(filename);

            if (!config.format && (config.sourceType === undefined || config.sourceType === "htsget")) {
              // Check for htsget URL.  This is a longshot
              await HtsgetReader.inferFormat(config);
            }
          }
        }

        let type = config.type ? config.type.toLowerCase() : undefined;

        if (!type) {
          type = inferTrackType(config);

          if ("bedtype" === type) {
            // Bed files must be read to determine track type
            const featureSource = FeatureSource(config, this.genome);
            config._featureSource = featureSource; // This is a temp variable, bit of a hack

            const trackType = await featureSource.trackType();

            if (trackType) {
              type = trackType;
            } else {
              type = "annotation";
            }
          } // Record in config to make type persistent in session


          config.type = type;
        } // Set defaults if specified


        if (this.trackDefaults && type) {
          const settings = this.trackDefaults[type];

          if (settings) {
            for (let property in settings) {
              if (settings.hasOwnProperty(property) && config[property] === undefined) {
                config[property] = settings[property];
              }
            }
          }
        }

        const track = TrackFactory.getTrack(type, config, this);

        if (track && config.roi) {
          track.roi = [];

          for (let r of config.roi) {
            track.roi.push(new ROI(r, this.genome));
          }
        }

        return track;
      }

      reorderTracks() {
        this.trackViews.sort(function (a, b) {
          const firstSortOrder = tv => {
            return 'ideogram' === tv.track.id ? 1 : 'ruler' === tv.track.id ? 2 : 3;
          };

          const aOrder1 = firstSortOrder(a);
          const bOrder1 = firstSortOrder(b);

          if (aOrder1 === bOrder1) {
            const aOrder2 = a.track.order || 0;
            const bOrder2 = b.track.order || 0;
            return aOrder2 - bOrder2;
          } else {
            return aOrder1 - bOrder1;
          }
        }); // discard current track order

        for (let {
          axis,
          viewports,
          sampleNameViewport,
          outerScroll,
          dragHandle,
          gearContainer
        } of this.trackViews) {
          axis.remove();

          for (let {
            $viewport
          } of viewports) {
            $viewport.detach();
          }

          sampleNameViewport.viewport.remove();
          outerScroll.remove();
          dragHandle.remove();
          gearContainer.remove();
        } // Reattach the divs to the dom in the correct order


        const viewportColumns = this.columnContainer.querySelectorAll('.igv-column');

        for (let {
          axis,
          viewports,
          sampleNameViewport,
          outerScroll,
          dragHandle,
          gearContainer
        } of this.trackViews) {
          this.columnContainer.querySelector('.igv-axis-column').appendChild(axis);

          for (let i = 0; i < viewportColumns.length; i++) {
            const {
              $viewport
            } = viewports[i];
            viewportColumns[i].appendChild($viewport.get(0));
          }

          this.columnContainer.querySelector('.igv-sample-name-column').appendChild(sampleNameViewport.viewport);
          this.columnContainer.querySelector('.igv-scrollbar-column').appendChild(outerScroll);
          this.columnContainer.querySelector('.igv-track-drag-column').appendChild(dragHandle);
          this.columnContainer.querySelector('.igv-gear-menu-column').appendChild(gearContainer);
        }
      }

      getTrackOrder() {
        return this.trackViews.filter(tv => tv.track && tv.track.name).map(tv => tv.track.name);
      }

      removeTrackByName(name) {
        const copy = this.trackViews.slice();

        for (let trackView of copy) {
          if (name === trackView.track.name) {
            this.removeTrack(trackView.track);
          }
        }
      }

      removeTrack(track) {
        this.trackViews.splice(this.trackViews.indexOf(track.trackView), 1);
        this.fireEvent('trackremoved', [track]);
        this.fireEvent('trackorderchanged', [this.getTrackOrder()]);
        track.trackView.dispose();
      }
      /**
       * API function
       */


      removeAllTracks() {
        const remainingTrackViews = [];

        for (let trackView of this.trackViews) {
          if (trackView.track.id !== 'ruler' && trackView.track.id !== 'ideogram') {
            this.fireEvent('trackremoved', [trackView.track]);
            trackView.dispose();
          } else {
            remainingTrackViews.push(trackView);
          }
        }

        this.trackViews = remainingTrackViews;
      }
      /**
       *
       * @param property
       * @param value
       * @returns {Array}  tracks with given property value.  e.g. findTracks("type", "annotation")
       */


      findTracks(property, value) {
        let f = typeof property === 'function' ? trackView => property(trackView.track) : trackView => value === trackView.track[property];
        return this.trackViews.filter(f).map(tv => tv.track);
      }
      /**
       * Set the track height globally for all tracks.  (Note: Its not clear why this is useful).
       * @param newHeight
       */


      setTrackHeight(newHeight) {
        this.trackHeight = newHeight;
        this.trackViews.forEach(function (trackView) {
          trackView.setTrackHeight(newHeight);
        });
      }
      /**
       * API function to signal that this browser visibility has changed, e.g. from hiding/showing in a tab interface.
       *
       * @returns {Promise<void>}
       */


      async visibilityChange() {
        this.layoutChange();
      }

      async layoutChange() {
        const status = this.referenceFrameList.find(referenceFrame => referenceFrame.bpPerPixel < 0);

        if (status) {
          const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);

          for (let referenceFrame of this.referenceFrameList) {
            referenceFrame.bpPerPixel = (referenceFrame.end - referenceFrame.start) / viewportWidth;
          }
        }

        if (this.referenceFrameList) {
          const isWGV = this.isMultiLocusWholeGenomeView() || GenomeUtils.isWholeGenomeView(this.referenceFrameList[0].chr);
          this.navbarManager.navbarDidResize(this.$navigation.width(), isWGV);
        }

        resize.call(this);
        await this.updateViews();
      }

      async updateViews() {
        const trackViews = this.trackViews;
        this.updateLocusSearchWidget();

        for (let centerGuide of this.centerLineList) {
          centerGuide.repaint();
        } // Don't autoscale while dragging.


        if (this.dragObject) {
          for (let trackView of trackViews) {
            await trackView.updateViews();
          }
        } else {
          // Group autoscale
          const groupAutoscaleTracks = {};
          const otherTracks = [];

          for (let trackView of trackViews) {
            const group = trackView.track.autoscaleGroup;

            if (group) {
              var l = groupAutoscaleTracks[group];

              if (!l) {
                l = [];
                groupAutoscaleTracks[group] = l;
              }

              l.push(trackView);
            } else {
              otherTracks.push(trackView);
            }
          }

          if (Object.entries(groupAutoscaleTracks).length > 0) {
            const keys = Object.keys(groupAutoscaleTracks);

            for (let group of keys) {
              const groupTrackViews = groupAutoscaleTracks[group];
              const promises = [];

              for (let trackView of groupTrackViews) {
                promises.push(trackView.getInViewFeatures());
              }

              const featureArray = await Promise.all(promises);
              var allFeatures = [],
                  dataRange;

              for (let features of featureArray) {
                allFeatures = allFeatures.concat(features);
              }

              dataRange = doAutoscale(allFeatures);
              const p = [];

              for (let trackView of groupTrackViews) {
                trackView.track.dataRange = dataRange;
                trackView.track.autoscale = false;
                p.push(trackView.updateViews());
              }

              await Promise.all(p);
            }
          }

          await Promise.all(otherTracks.map(tv => tv.updateViews())); // for (let trackView of otherTracks) {
          //     await trackView.updateViews(force);
          // }
        }
      }

      repaintViews() {
        for (let trackView of this.trackViews) {
          trackView.repaintViews();
        }
      }

      updateLocusSearchWidget() {
        const referenceFrameList = this.referenceFrameList; // Update end position of reference frames based on pixel widths.  This is hacky, but its been done here
        // for a long time, although indirectly.

        const width = this.calculateViewportWidth(this.referenceFrameList.length);

        for (let referenceFrame of referenceFrameList) {
          referenceFrame.end = referenceFrame.start + referenceFrame.bpPerPixel * width;
        }

        this.chromosomeSelectWidget.select.value = referenceFrameList.length === 1 ? this.referenceFrameList[0].chr : '';
        const loc = this.referenceFrameList.map(rf => rf.getLocusString()).join(' '); //const loc = this.referenceFrameList.length === 1 ?   this.referenceFrameList[0].getLocusString() : '';

        this.$searchInput.val(loc);
        this.fireEvent('locuschange', [this.referenceFrameList]);
      }

      calculateViewportWidth(columnCount) {
        let {
          width
        } = this.columnContainer.getBoundingClientRect();
        const sampleNameViewportWidth = this.getSampleNameViewportWidth();
        width -= igv_axis_column_width + sampleNameViewportWidth + igv_scrollbar_outer_width + igv_track_manipulation_handle_width + igv_track_gear_menu_column_width;
        width -= column_multi_locus_shim_width * (columnCount - 1);
        return Math.floor(width / columnCount);
      }

      getCenterLineXOffset() {
        let {
          width: columnContainerWidth
        } = this.columnContainer.getBoundingClientRect();
        columnContainerWidth -= igv_axis_column_width + this.getSampleNameViewportWidth() + igv_scrollbar_outer_width + igv_track_manipulation_handle_width + igv_track_gear_menu_column_width;
        return Math.floor(columnContainerWidth / 2 + igv_axis_column_width);
      }

      minimumBases() {
        return this.config.minimumBases;
      } // Zoom in by a factor of 2, keeping the same center location


      zoomIn() {
        this.zoomWithScaleFactor(0.5);
      }

      // Zoom out by a factor of 2, keeping the same center location if possible
      zoomOut() {
        this.zoomWithScaleFactor(2.0);
      }

      async zoomWithScaleFactor(scaleFactor, centerBPOrUndefined, referenceFrameOrUndefined) {
        const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);
        let referenceFrames = referenceFrameOrUndefined ? [referenceFrameOrUndefined] : this.referenceFrameList;

        for (let referenceFrame of referenceFrames) {
          referenceFrame.zoomWithScaleFactor(this, scaleFactor, viewportWidth, centerBPOrUndefined);
        }
      }
      /**
       * Add a new multi-locus panel for the specified region
       * @param chr
       * @param start
       * @param end
       * @param referenceFrameLeft - optional, if supplied new panel should be placed to the immediate right
       */


      async addMultiLocusPanel(chr, start, end, referenceFrameLeft) {
        // account for reduced viewport width as a result of adding right mate pair panel
        const viewportWidth = this.calculateViewportWidth(1 + this.referenceFrameList.length);
        const scaleFactor = this.calculateViewportWidth(this.referenceFrameList.length) / this.calculateViewportWidth(1 + this.referenceFrameList.length);

        for (let refFrame of this.referenceFrameList) {
          refFrame.bpPerPixel *= scaleFactor;
        }

        const bpp = (end - start) / viewportWidth;
        const newReferenceFrame = new ReferenceFrame(this.genome, chr, start, end, bpp);
        const indexLeft = referenceFrameLeft ? this.referenceFrameList.indexOf(referenceFrameLeft) : this.referenceFrameList.length - 1;
        const indexRight = 1 + indexLeft; // TODO -- this is really ugly

        const {
          $viewport
        } = this.trackViews[0].viewports[indexLeft];
        const viewportColumn = viewportColumnManager.insertAfter($viewport.get(0).parentElement);

        if (indexRight === this.referenceFrameList.length) {
          this.referenceFrameList.push(newReferenceFrame);

          for (let trackView of this.trackViews) {
            const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
            trackView.viewports.push(viewport);
          }
        } else {
          this.referenceFrameList.splice(indexRight, 0, newReferenceFrame);

          for (let trackView of this.trackViews) {
            const viewport = createViewport(trackView, viewportColumn, newReferenceFrame);
            trackView.viewports.splice(indexRight, 0, viewport);
          }
        }

        this.centerLineList = this.createCenterLineList(this.columnContainer);
        resize.call(this);
        await this.updateViews(true);
      }

      async removeMultiLocusPanel(referenceFrame) {
        // find the $column corresponding to this referenceFrame and remove it
        const index = this.referenceFrameList.indexOf(referenceFrame);
        const {
          $viewport
        } = this.trackViews[0].viewports[index];
        viewportColumnManager.removeColumnAtIndex(index, $viewport.parent().get(0));

        for (let {
          viewports
        } of this.trackViews) {
          viewports[index].dispose();
          viewports.splice(index, 1);
        }

        this.referenceFrameList.splice(index, 1);

        if (1 === this.referenceFrameList.length && this.getRulerTrackView()) {
          for (let rulerViewport of this.getRulerTrackView().viewports) {
            rulerViewport.dismissLocusLabel();
          }
        }

        const scaleFactor = this.calculateViewportWidth(1 + this.referenceFrameList.length) / this.calculateViewportWidth(this.referenceFrameList.length);
        await this.rescaleForMultiLocus(scaleFactor);
      }
      /**
       * Goto the locus represented by the selected referenceFrame, discarding all other panels
       *
       * @param referenceFrame
       * @returns {Promise<void>}
       */


      async gotoMultilocusPanel(referenceFrame) {
        const referenceFrameIndex = this.referenceFrameList.indexOf(referenceFrame); // Remove columns for unselected panels

        this.columnContainer.querySelectorAll('.igv-column').forEach((column, c) => {
          if (c === referenceFrameIndex) ; else {
            column.remove();
          }
        }); // Remove all column shims

        this.columnContainer.querySelectorAll('.igv-column-shim').forEach(shim => shim.remove()); // Discard viewports

        for (let trackView of this.trackViews) {
          const retain = trackView.viewports[referenceFrameIndex];
          trackView.viewports.filter((viewport, i) => i !== referenceFrameIndex).forEach(viewport => viewport.dispose());
          trackView.viewports = [retain];
        }

        const viewportWidth = this.calculateViewportWidth(1);
        referenceFrame.bpPerPixel = (referenceFrame.end - referenceFrame.start) / viewportWidth;
        this.referenceFrameList = [referenceFrame];
        this.trackViews.forEach(_ref2 => {
          let {
            viewports
          } = _ref2;
          return viewports.forEach(viewport => viewport.setWidth(viewportWidth));
        });
        this.centerLineList = this.createCenterLineList(this.columnContainer);
        this.updateUIWithReferenceFrameList();
        await this.updateViews(true);
      }

      async rescaleForMultiLocus(scaleFactor) {
        const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);

        for (let referenceFrame of this.referenceFrameList) {
          referenceFrame.bpPerPixel *= scaleFactor;
        }

        for (let {
          viewports
        } of this.trackViews) {
          for (let viewport of viewports) {
            viewport.setWidth(viewportWidth);
          }
        }

        this.centerLineList = this.createCenterLineList(this.columnContainer);
        this.updateUIWithReferenceFrameList();
        await this.updateViews();
      }
      /**
       * @deprecated  This is a deprecated method with no known usages.  To be removed in a future release.
       */


      async goto(chr, start, end) {
        await this.search(chr + ":" + start + "-" + end);
      }
      /**
        * Search for the locus string -- this function is called from various igv.js GUI elements, and is not part of the
       * API.  Wraps ```search``` and presents an error dialog if false.
       *
       * @param string
       * @param init
       * @returns {Promise<void>}
       */


      async doSearch(string, init) {
        const success = await this.search(string, init);

        if (!success) {
          Alert.presentAlert(new Error(`Unrecognized locus: <b> ${string} </b>`));
        }

        return success;
      }
      /**
       * Search for the locus string
       * NOTE: This is part of the API
       * @param string
       * @param init  true if called during browser initialization
       * @returns {Promise<boolean>}  true if found, false if not
       */


      async search(string, init) {
        const loci = await search(this, string);

        if (loci && loci.length > 0) {
          // create reference frame list based on search loci
          this.referenceFrameList = createReferenceFrameList(loci, this.genome, this.flanking, this.minimumBases(), this.calculateViewportWidth(loci.length)); // discard viewport DOM elements

          for (let trackView of this.trackViews) {
            // empty axis column, viewport columns, sampleName column, scroll column, drag column, gear column
            trackView.removeDOMFromColumnContainer();
          } // discard ONLY viewport columns


          this.columnContainer.querySelectorAll('.igv-column-shim, .igv-column').forEach(el => el.remove()); // Insert viewport columns preceding the sample-name column

          viewportColumnManager.insertBefore(this.columnContainer.querySelector('.igv-sample-name-column'), this.referenceFrameList.length);
          this.centerLineList = this.createCenterLineList(this.columnContainer); // Populate the columns

          for (let trackView of this.trackViews) {
            trackView.addDOMToColumnContainer(this, this.columnContainer, this.referenceFrameList);
          }

          this.updateUIWithReferenceFrameList();

          if (!init) {
            await this.updateViews();
          }

          return true;
        } else {
          return false;
        }
      }

      async loadSampleInformation(url) {
        var name = url;

        if (isFile(url)) {
          name = url.name;
        }

        var ext = name.substr(name.lastIndexOf('.') + 1);

        if (ext === 'fam') {
          this.sampleInformation = await loadPlinkFile(url);
        }
      }

      // EVENTS
      on(eventName, fn) {
        if (!this.eventHandlers[eventName]) {
          this.eventHandlers[eventName] = [];
        }

        this.eventHandlers[eventName].push(fn);
      }

      /**
       * @deprecated use off()
       * @param eventName
       * @param fn
       */
      un(eventName, fn) {
        this.off(eventName, fn);
      }

      off(eventName, fn) {
        if (!eventName) {
          this.eventHandlers = {}; // Remove all event handlers
        } else if (!fn) {
          this.eventHandlers[eventName] = []; // Remove all eventhandlers matching name
        } else {
          // Remove specific event handler
          const handlers = this.eventHandlers[eventName];

          if (!handlers || handlers.length === 0) {
            console.warn("No handlers to remove for event: " + eventName);
          } else {
            const callbackIndex = handlers.indexOf(fn);

            if (callbackIndex !== -1) {
              this.eventHandlers[eventName].splice(callbackIndex, 1);
            }
          }
        }
      }

      fireEvent(eventName, args, thisObj) {
        const handlers = this.eventHandlers[eventName];

        if (undefined === handlers || handlers.length === 0) {
          return undefined;
        }

        const scope = thisObj || window;
        const results = handlers.map(function (event) {
          return event.apply(scope, args);
        });
        return results[0];
      }

      dispose() {
        this.removeMouseHandlers();

        for (let trackView of this.trackViews) {
          trackView.dispose();
        }
      }

      toJSON() {
        const json = {
          "version": version()
        };

        if (this.showSampleNames !== undefined) {
          json['showSampleNames'] = this.showSampleNames;
        }

        if (this.sampleNameViewportWidth !== defaultSampleNameViewportWidth) {
          json['sampleNameViewportWidth'] = this.sampleNameViewportWidth;
        }

        json["reference"] = this.genome.toJSON();

        if (isFilePath(json.reference.fastaURL)) {
          throw new Error(`Error. Sessions cannot include local file references ${json.reference.fastaURL.name}.`);
        } else if (isFilePath(json.reference.indexURL)) {
          throw new Error(`Error. Sessions cannot include local file references ${json.reference.indexURL.name}.`);
        } // Build locus array (multi-locus view).  Use the first track to extract the loci, any track could be used.


        const locus = [];
        const gtexSelections = {};
        let hasGtexSelections = false;
        let anyTrackView = this.trackViews[0];

        for (let {
          referenceFrame
        } of anyTrackView.viewports) {
          const locusString = referenceFrame.getLocusString();
          locus.push(locusString);

          if (referenceFrame.selection) {
            const selection = {
              gene: referenceFrame.selection.gene,
              snp: referenceFrame.selection.snp
            };
            gtexSelections[locusString] = selection;
            hasGtexSelections = true;
          }
        }

        json["locus"] = locus.length === 1 ? locus[0] : locus;

        if (hasGtexSelections) {
          json["gtexSelections"] = gtexSelections;
        }

        const trackJson = [];
        const errors = [];

        for (let {
          track
        } of this.trackViews) {
          try {
            let config;

            if (typeof track.getState === "function") {
              config = track.getState();
            } else {
              config = track.config;
            }

            if (config) {
              // null backpointer to browser
              if (config.browser) {
                delete config.browser;
              }

              config.order = track.order; //order++;

              trackJson.push(config);
            }
          } catch (e) {
            console.error(`Track: ${track.name}: ${e}`);
            errors.push(`Track: ${track.name}: ${e}`);
          }
        }

        if (errors.length > 0) {
          let n = 1;
          let message = 'Errors encountered saving session: </br>';

          for (let e of errors) {
            message += ` (${n++}) ${e.toString()} <br/>`;
          }

          throw Error(message);
        }

        json["tracks"] = trackJson;
        return json; // This is an object, not a json string
      }

      compressedSession() {
        const json = JSON.stringify(this.toJSON());
        return compressString(json);
      }

      sessionURL() {
        const path = window.location.href.slice();
        const idx = path.indexOf("?");
        const surl = (idx > 0 ? path.substring(0, idx) : path) + "?sessionURL=blob:" + this.compressedSession();
        return surl;
      }
      /**
       * Record a mouse click on a specific viewport.   This might be the start of a drag operation.   Dragging
       * (panning) is handled here so that the mouse can move out of a specific viewport (e.g. stray into another
       * track) without halting the drag.
       *
       * @param e
       * @param viewport
       */


      mouseDownOnViewport(e, viewport) {
        var coords;
        coords = pageCoordinates$1(e);
        this.vpMouseDown = {
          viewport,
          lastMouseX: coords.x,
          mouseDownX: coords.x,
          lastMouseY: coords.y,
          mouseDownY: coords.y,
          referenceFrame: viewport.referenceFrame
        };
      }

      cancelTrackPan() {
        const dragObject = this.dragObject;
        this.dragObject = undefined;
        this.isScrolling = false;
        this.vpMouseDown = undefined;

        if (dragObject && dragObject.viewport.referenceFrame.start !== dragObject.start) {
          this.updateViews();
          this.fireEvent('trackdragend');
        }
      }
      /**
       * Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
       *
       * @param trackView
       */


      startTrackDrag(trackView) {
        this.dragTrack = trackView;
      }
      /**
       * Track drag here refers to vertical dragging to reorder tracks, not horizontal panning.
       *
       * @param dragDestination
       */


      updateTrackDrag(dragDestination) {
        if (dragDestination && this.dragTrack) {
          const dragged = this.dragTrack;
          const indexDestination = this.trackViews.indexOf(dragDestination);
          const indexDragged = this.trackViews.indexOf(dragged);
          const trackViews = this.trackViews;
          trackViews[indexDestination] = dragged;
          trackViews[indexDragged] = dragDestination;
          const newOrder = this.trackViews[indexDestination].track.order;
          this.trackViews[indexDragged].track.order = newOrder;
          const nTracks = trackViews.length;
          let lastOrder = newOrder;

          if (indexDestination < indexDragged) {
            // Displace tracks below
            for (let i = indexDestination + 1; i < nTracks; i++) {
              const track = trackViews[i].track;

              if (track.order <= lastOrder) {
                track.order = Math.min(Number.MAX_SAFE_INTEGER, lastOrder + 1);
                lastOrder = track.order;
              } else {
                break;
              }
            }
          } else {
            // Displace tracks above.  First track (index 0) is "ruler"
            for (let i = indexDestination - 1; i > 0; i--) {
              const track = trackViews[i].track;

              if (track.order >= lastOrder) {
                track.order = Math.max(-Number.MAX_SAFE_INTEGER, lastOrder - 1);
                lastOrder = track.order;
              } else {
                break;
              }
            }
          }

          this.reorderTracks();
        }
      }

      endTrackDrag() {
        if (this.dragTrack) {
          // this.dragTrack.$trackDragScrim.hide();
          this.dragTrack = undefined;
          this.fireEvent('trackorderchanged', [this.getTrackOrder()]);
        } else {
          this.dragTrack = undefined;
        }
      }
      /**
       * Mouse handlers to support drag (pan)
       */


      addMouseHandlers() {
        this.addWindowResizeHandler();
        this.addRootMouseUpHandler();
        this.addRootMouseLeaveHandler();
        this.addColumnContainerEventHandlers();
      }

      removeMouseHandlers() {
        this.removeWindowResizeHandler();
        this.removeRootMouseUpHandler();
        this.removeRootMouseLeaveHandler();
        this.removeColumnContainerEventHandlers();
      }

      addWindowResizeHandler() {
        // Create a copy of the prototype "resize" function bound to this instance.  Neccessary to support removing.
        this.boundWindowResizeHandler = resize.bind(this);
        window.addEventListener('resize', this.boundWindowResizeHandler);
      }

      removeWindowResizeHandler() {
        window.removeEventListener('resize', this.boundWindowResizeHandler);
      }

      addRootMouseUpHandler() {
        this.boundRootMouseUpHandler = mouseUpOrLeave.bind(this);
        this.root.addEventListener('mouseup', this.boundRootMouseUpHandler);
      }

      removeRootMouseUpHandler() {
        this.root.removeEventListener('mouseup', this.boundRootMouseUpHandler);
      }

      addRootMouseLeaveHandler() {
        this.boundRootMouseLeaveHandler = mouseUpOrLeave.bind(this);
        this.root.addEventListener('mouseleave', this.boundRootMouseLeaveHandler);
      }

      removeRootMouseLeaveHandler() {
        this.root.removeEventListener('mouseleave', this.boundRootMouseLeaveHandler);
      }

      addColumnContainerEventHandlers() {
        this.boundColumnContainerMouseMoveHandler = handleMouseMove.bind(this);
        this.boundColumnContainerTouchMoveHandler = handleMouseMove.bind(this);
        this.boundColumnContainerMouseLeaveHandler = mouseUpOrLeave.bind(this);
        this.boundColumnContainerMouseUpHandler = mouseUpOrLeave.bind(this);
        this.boundColumnContainerTouchEndHandler = mouseUpOrLeave.bind(this);
        this.columnContainer.addEventListener('mousemove', this.boundColumnContainerMouseMoveHandler);
        this.columnContainer.addEventListener('touchmove', this.boundColumnContainerTouchMoveHandler);
        this.columnContainer.addEventListener('mouseleave', this.boundColumnContainerMouseLeaveHandler);
        this.columnContainer.addEventListener('mouseup', this.boundColumnContainerMouseUpHandler);
        this.columnContainer.addEventListener('touchend', this.boundColumnContainerTouchEndHandler);
      }

      removeColumnContainerEventHandlers() {
        this.columnContainer.removeEventListener('mousemove', this.boundColumnContainerMouseMoveHandler);
        this.columnContainer.removeEventListener('touchmove', this.boundColumnContainerTouchMoveHandler);
        this.columnContainer.removeEventListener('mouseleave', this.boundColumnContainerMouseLeaveHandler);
        this.columnContainer.removeEventListener('mouseup', this.boundColumnContainerMouseUpHandler);
        this.columnContainer.removeEventListener('touchend', this.boundColumnContainerTouchEndHandler);
      }

      async getDriveFileInfo(googleDriveURL) {
        const id = getGoogleDriveFileID(googleDriveURL);
        const endPoint = "https://www.googleapis.com/drive/v3/files/" + id + "?supportsTeamDrives=true";
        return igvxhr.loadJson(endPoint, buildOptions({}));
      }

      static uncompressSession(url) {
        let bytes;

        if (url.indexOf('/gzip;base64') > 0) {
          //Proper dataURI
          bytes = decodeDataURI$1(url);
          let json = '';

          for (let b of bytes) {
            json += String.fromCharCode(b);
          }

          return json;
        } else {
          let enc = url.substring(5);
          return uncompressString(enc);
        }
      }

      createCircularView(container, show) {
        show = show === true; // convert undefined to boolean

        this.circularView = createCircularView(container, this);
        this.circularViewControl = new CircularViewControl(this.$toggle_button_container.get(0), this);
        this.circularView.setAssembly({
          name: this.genome.id,
          id: this.genome.id,
          chromosomes: makeCircViewChromosomes(this.genome)
        });
        this.circularViewVisible = show;
        return this.circularView;
      }

      get circularViewVisible() {
        return this.circularView !== undefined && this.circularView.visible;
      }

      set circularViewVisible(isVisible) {
        if (this.circularView) {
          this.circularView.visible = isVisible;
          this.circularViewControl.setState(isVisible);
        }
      }

    }
    /**
     * Function called win window is resized, or visibility changed (e.g. "show" from a tab).  This is a function rather
     * than class method because it needs to be copied and bound to specific instances of browser to support listener
     * removal
     *
     * @returns {Promise<void>}
     */


    async function resize() {
      const viewportWidth = this.calculateViewportWidth(this.referenceFrameList.length);

      for (let referenceFrame of this.referenceFrameList) {
        const index = this.referenceFrameList.indexOf(referenceFrame);
        const {
          chr,
          genome
        } = referenceFrame;
        const {
          bpLength
        } = genome.getChromosome(referenceFrame.chr);
        const viewportWidthBP = referenceFrame.toBP(viewportWidth); // viewportWidthBP > bpLength occurs when locus is full chromosome and user widens browser

        if (GenomeUtils.isWholeGenomeView(chr) || viewportWidthBP > bpLength) {
          // console.log(`${ Date.now() } Recalc referenceFrame(${ index }) bpp. viewport ${ StringUtils.numberFormatter(viewportWidthBP) } > ${ StringUtils.numberFormatter(bpLength) }.`)
          referenceFrame.bpPerPixel = bpLength / viewportWidth;
        } else {
          // console.log(`${ Date.now() } Recalc referenceFrame(${ index }) end.`)
          referenceFrame.end = referenceFrame.start + referenceFrame.toBP(viewportWidth);
        }

        for (let {
          viewports
        } of this.trackViews) {
          viewports[index].setWidth(viewportWidth);
        }
      }

      this.updateUIWithReferenceFrameList(); //TODO -- update view only if needed.  Reducing size never needed.  Increasing size maybe

      await this.updateViews(true);
    }

    function handleMouseMove(e) {
      e.preventDefault();
      const {
        x,
        y
      } = pageCoordinates$1(e);

      if (this.vpMouseDown) {
        const {
          viewport,
          referenceFrame
        } = this.vpMouseDown; // Determine direction,  true == horizontal

        const horizontal = Math.abs(x - this.vpMouseDown.mouseDownX) > Math.abs(y - this.vpMouseDown.mouseDownY);

        if (!this.dragObject && !this.isScrolling) {
          if (horizontal) {
            if (this.vpMouseDown.mouseDownX && Math.abs(x - this.vpMouseDown.mouseDownX) > this.constants.dragThreshold) {
              this.dragObject = {
                viewport,
                start: referenceFrame.start
              };
            }
          } else {
            if (this.vpMouseDown.mouseDownY && Math.abs(y - this.vpMouseDown.mouseDownY) > this.constants.scrollThreshold) {
              this.isScrolling = true;
              const viewportHeight = viewport.$viewport.height();
              const contentHeight = maxViewportContentHeight(viewport.trackView.viewports);
              this.vpMouseDown.r = viewportHeight / contentHeight;
            }
          }
        }

        if (this.dragObject) {
          const viewChanged = referenceFrame.shiftPixels(this.vpMouseDown.lastMouseX - x, viewport.$viewport.width());

          if (viewChanged) {
            this.updateViews();
          }

          this.fireEvent('trackdrag');
        }

        if (this.isScrolling) {
          const delta = this.vpMouseDown.r * (this.vpMouseDown.lastMouseY - y);
          viewport.trackView.moveScroller(delta);
        }

        this.vpMouseDown.lastMouseX = x;
        this.vpMouseDown.lastMouseY = y;
      }
    }

    function mouseUpOrLeave(e) {
      this.cancelTrackPan();
      this.endTrackDrag();
    }

    function getInitialLocus(locus, genome) {
      if (locus) {
        return Array.isArray(locus) ? locus.join(' ') : locus;
      } else {
        return genome.getHomeChromosomeName();
      }
    }

    function logo() {
      return $$1('<svg width="690px" height="324px" viewBox="0 0 690 324" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">' + '<title>IGV</title>' + '<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">' + '<g id="IGV" fill="#666666">' + '<polygon id="Path" points="379.54574 8.00169252 455.581247 8.00169252 515.564813 188.87244 532.884012 253.529506 537.108207 253.529506 554.849825 188.87244 614.833392 8.00169252 689.60164 8.00169252 582.729511 320.722144 486.840288 320.722144"></polygon>' + '<path d="M261.482414,323.793286 C207.975678,323.793286 168.339046,310.552102 142.571329,284.069337 C116.803612,257.586572 103.919946,217.158702 103.919946,162.784513 C103.919946,108.410325 117.437235,67.8415913 144.472217,41.0770945 C171.507199,14.3125977 212.903894,0.930550071 268.663545,0.930550071 C283.025879,0.930550071 298.232828,1.84616386 314.284849,3.6774189 C330.33687,5.50867394 344.839793,7.97378798 357.794056,11.072835 L357.794056,68.968378 C339.48912,65.869331 323.578145,63.5450806 310.060654,61.9955571 C296.543163,60.4460336 284.574731,59.6712835 274.154998,59.6712835 C255.850062,59.6712835 240.502308,61.4320792 228.111274,64.9537236 C215.720241,68.4753679 205.793482,74.2507779 198.330701,82.2801269 C190.867919,90.309476 185.587729,100.87425 182.48997,113.974767 C179.392212,127.075284 177.843356,143.345037 177.843356,162.784513 C177.843356,181.942258 179.251407,198.000716 182.067551,210.960367 C184.883695,223.920018 189.671068,234.41436 196.429813,242.443709 C203.188559,250.473058 212.059279,256.178037 223.042241,259.558815 C234.025202,262.939594 247.683295,264.629958 264.01693,264.629958 C268.241146,264.629958 273.098922,264.489094 278.590403,264.207362 C284.081883,263.925631 289.643684,263.50304 295.275972,262.939577 L295.275972,159.826347 L361.595831,159.826347 L361.595831,308.579859 C344.698967,313.087564 327.239137,316.750019 309.215815,319.567334 C291.192494,322.38465 275.281519,323.793286 261.482414,323.793286 L261.482414,323.793286 L261.482414,323.793286 Z" id="Path"></path>;' + '<polygon id="Path" points="0.81355666 5.00169252 73.0472883 5.00169252 73.0472883 317.722144 0.81355666 317.722144"></polygon>' + '</g> </g> </svg>');
    }

    function toggleTrackLabels(trackViews, isVisible) {
      for (let {
        viewports
      } of trackViews) {
        for (let viewport of viewports) {
          if (viewport.$trackLabel) {
            if (0 === viewports.indexOf(viewport) && true === isVisible) {
              viewport.$trackLabel.show();
            } else {
              viewport.$trackLabel.hide();
            }
          }
        }
      }
    }

    /*
     * The MIT License (MIT)
     *
     * Copyright (c) 2014 Broad Institute
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    let allBrowsers = [];
    /**
     * Create an igv.browser instance.  This object defines the public API for interacting with the genome browser.
     *
     * @param parentDiv - DOM tree root
     * @param config - configuration options.
     *
     */

    async function createBrowser(parentDiv, config) {
      if (undefined === config) config = {}; // Initialize pre-defined genomes.  The genome list is shared among all browser instances

      if (!GenomeUtils.KNOWN_GENOMES) {
        await GenomeUtils.initializeGenomes(config);
      }

      setDefaults(config);

      if (config.queryParametersSupported) {
        extractQuery(config);
      }

      if (config.apiKey) {
        igvxhr.setApiKey(config.apiKey);
      }

      if (config.oauthToken) {
        oauth.setToken(config.oauthToken);
      }

      if (config.clientId && !isInitialized()) {
        await init$1({
          clientId: config.clientId,
          apiKey: config.apiKey,
          scope: 'https://www.googleapis.com/auth/userinfo.profile'
        });
      } // Create browser


      const browser = new Browser(config, parentDiv);
      allBrowsers.push(browser); // Load initial session

      if (config.sessionURL) {
        await browser.loadSession({
          url: config.sessionURL
        });
      } else {
        await browser.loadSessionObject(config);
      }

      const isWGV = browser.isMultiLocusWholeGenomeView() || GenomeUtils.isWholeGenomeView(browser.referenceFrameList[0].chr);
      browser.navbarManager.navbarDidResize(browser.$navigation.width(), isWGV);
      return browser;
    }

    function removeBrowser(browser) {
      browser.dispose();
      browser.root.remove();
      allBrowsers = allBrowsers.filter(item => item !== browser);
    }

    function removeAllBrowsers() {
      for (let browser of allBrowsers) {
        browser.dispose();
        browser.root.remove();
      }

      allBrowsers = [];
    }
    /**
     * This function provided so clients can inform igv of a visibility change, typically when an igv instance is
     * made visible from a tab, accordion, or similar widget.
     */


    async function visibilityChange() {
      for (let browser of allBrowsers) {
        await browser.visibilityChange();
      }
    }

    function setDefaults(config) {
      if (undefined === config.minimumBases) {
        config.minimumBases = 40;
      }

      if (undefined === config.showIdeogram) {
        config.showIdeogram = true;
      }

      if (undefined === config.showCircularView) {
        config.showCircularView = false;
      }

      if (undefined === config.showCircularViewButton) {
        config.showCircularViewButton = false;
      }

      if (undefined === config.showTrackLabelButton) {
        config.showTrackLabelButton = true;
      }

      if (undefined === config.showTrackLabels) {
        config.showTrackLabels = true;
      }

      if (undefined === config.showCursorTrackingGuideButton) {
        config.showCursorTrackingGuideButton = true;
      }

      if (undefined === config.showCursorGuide) {
        config.showCursorGuide = config.showCursorTrackingGuide || false; // showCursorTrackingGuide is a synonym
      }

      if (undefined === config.showCenterGuideButton) {
        config.showCenterGuideButton = true;
      }

      if (undefined === config.showCenterGuide) {
        config.showCenterGuide = false;
      }

      if (undefined === config.showSampleNames) {
        config.showSampleNames = false;
      }

      if (undefined === config.showSVGButton) {
        config.showSVGButton = true;
      }

      if (config.showControls === undefined) {
        config.showControls = true;
      }

      if (config.showNavigation === undefined) {
        config.showNavigation = true;
      }

      if (config.showRuler === undefined) {
        config.showRuler = true;
      }

      if (config.flanking === undefined) {
        config.flanking = 1000;
      }

      if (config.pairsSupported === undefined) {
        config.pairsSupported = true;
      }

      if (!config.tracks) {
        config.tracks = [];
      }
    }

    function extractQuery(config) {
      var i1, i2, i, j, s, query, tokens, uri, key, value;
      uri = window.location.href;
      query = {};
      i1 = uri.indexOf("?");
      i2 = uri.lastIndexOf("#");
      let files;
      let indexURLs;
      let names;

      if (i1 >= 0) {
        if (i2 < 0) i2 = uri.length;

        for (i = i1 + 1; i < i2;) {
          j = uri.indexOf("&", i);
          if (j < 0) j = i2;
          s = uri.substring(i, j);
          tokens = s.split("=", 2);

          if (tokens.length === 2) {
            key = tokens[0];
            value = decodeURIComponent(tokens[1]);

            if ('file' === key) {
              // IGV desktop style file parameter
              files = value.split(',');
            } else if ('index' === key) {
              // IGV desktop style index parameter
              indexURLs = value.split(',');
            } else if ('name' === key) {
              // IGV desktop style index parameter
              names = value.split(',');
            } else if ('genome' === key && (value.startsWith("https://") || value.startsWith("http://")) && !value.endsWith(".json")) {
              // IGV desktop compatibility -- assuming url to fasta
              config['reference'] = {
                fastaURL: value,
                indexURL: value + ".fai"
              };
            } else {
              config[key] = value;
            }

            i = j + 1;
          } else {
            i++;
          }
        }
      }

      if (files) {
        if (!config.tracks) config.tracks = [];

        for (let i = 0; i < files.length; i++) {
          if (files[i].endsWith(".xml") || files[i].endsWith(".json")) {
            config.sessionURL = files[i];
            break;
          }

          const trackConfig = {
            url: files[i]
          };

          if (indexURLs && indexURLs.length > i) {
            trackConfig.indexURL = indexURLs[i];
          }

          if (names && names.length > i) {
            trackConfig.name = names[i];
          }

          config.tracks.push(trackConfig);
        }
      }

      return query;
    }

    async function createTrack(config, browser) {
      return await Browser.prototype.createTrack.call(browser, config);
    }

    function embedCSS() {
      var css = '.igv-navbar {\n  display: flex;\n  flex-flow: row;\n  flex-wrap: nowrap;\n  justify-content: space-between;\n  align-items: center;\n  box-sizing: border-box;\n  width: 100%;\n  color: #444;\n  font-size: 12px;\n  font-family: \"Open Sans\", sans-serif;\n  font-weight: 400;\n  line-height: 32px;\n  padding-left: 8px;\n  padding-right: 8px;\n  margin-top: 2px;\n  margin-bottom: 6px;\n  height: 32px;\n  border-style: solid;\n  border-radius: 3px;\n  border-width: thin;\n  border-color: #bfbfbf;\n  background-color: #f3f3f3; }\n  .igv-navbar .igv-navbar-left-container {\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: space-between;\n    align-items: center;\n    height: 32px;\n    line-height: 32px; }\n    .igv-navbar .igv-navbar-left-container .igv-logo {\n      width: 34px;\n      height: 32px;\n      margin-right: 8px; }\n    .igv-navbar .igv-navbar-left-container .igv-current-genome {\n      height: 32px;\n      margin-left: 4px;\n      margin-right: 4px;\n      user-select: none;\n      line-height: 32px;\n      vertical-align: middle;\n      text-align: center; }\n    .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location {\n      display: flex;\n      flex-flow: row;\n      flex-wrap: nowrap;\n      justify-content: space-between;\n      align-items: center;\n      height: 100%; }\n      .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-chromosome-select-widget-container {\n        display: flex;\n        flex-flow: column;\n        flex-wrap: nowrap;\n        justify-content: space-around;\n        align-items: center;\n        height: 100%;\n        width: 125px;\n        margin-right: 4px; }\n        .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-chromosome-select-widget-container select {\n          display: block;\n          cursor: pointer;\n          width: 100px;\n          height: 75%;\n          outline: none;\n          font-size: 12px;\n          font-family: \"Open Sans\", sans-serif;\n          font-weight: 400; }\n      .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group {\n        display: flex;\n        flex-flow: row;\n        flex-wrap: nowrap;\n        justify-content: space-between;\n        align-items: center;\n        margin-left: 8px;\n        height: 22px; }\n        .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container {\n          display: flex;\n          flex-flow: row;\n          flex-wrap: nowrap;\n          justify-content: flex-start;\n          align-items: center;\n          width: 210px;\n          height: 22px;\n          line-height: 22px; }\n          .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container input.igv-search-input {\n            cursor: text;\n            width: 85%;\n            height: 22px;\n            line-height: 22px;\n            font-size: 12px;\n            font-family: \"Open Sans\", sans-serif;\n            font-weight: 400;\n            text-align: left;\n            padding-left: 8px;\n            margin-right: 8px;\n            outline: none;\n            border-style: solid;\n            border-radius: 3px;\n            border-width: thin;\n            border-color: #bfbfbf;\n            background-color: white; }\n          .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-search-container .igv-search-icon-container {\n            cursor: pointer;\n            height: 16px;\n            width: 16px; }\n        .igv-navbar .igv-navbar-left-container .igv-navbar-genomic-location .igv-locus-size-group .igv-windowsize-panel-container {\n          margin-left: 4px;\n          user-select: none; }\n  .igv-navbar .igv-navbar-right-container {\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: space-between;\n    align-items: center;\n    height: 32px;\n    line-height: 32px; }\n    .igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container {\n      display: flex;\n      flex-flow: row;\n      flex-wrap: nowrap;\n      justify-content: space-between;\n      align-items: center;\n      height: 100%; }\n      .igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container div {\n        margin-left: 0;\n        margin-right: 4px; }\n      .igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container div:last-child {\n        margin-left: 0;\n        margin-right: 0; }\n    .igv-navbar .igv-navbar-right-container .igv-navbar-toggle-button-container-750 {\n      display: none; }\n    .igv-navbar .igv-navbar-right-container .igv-zoom-widget {\n      color: #737373;\n      font-size: 18px;\n      height: 32px;\n      line-height: 32px;\n      margin-left: 8px;\n      user-select: none;\n      display: flex;\n      flex-flow: row;\n      flex-wrap: nowrap;\n      justify-content: flex-end;\n      align-items: center; }\n      .igv-navbar .igv-navbar-right-container .igv-zoom-widget div {\n        cursor: pointer;\n        margin-left: unset;\n        margin-right: unset; }\n      .igv-navbar .igv-navbar-right-container .igv-zoom-widget div:first-child {\n        height: 24px;\n        width: 24px;\n        margin-left: unset;\n        margin-right: 8px; }\n      .igv-navbar .igv-navbar-right-container .igv-zoom-widget div:last-child {\n        height: 24px;\n        width: 24px;\n        margin-left: 8px;\n        margin-right: unset; }\n      .igv-navbar .igv-navbar-right-container .igv-zoom-widget div:nth-child(even) {\n        display: block;\n        height: fit-content; }\n      .igv-navbar .igv-navbar-right-container .igv-zoom-widget input {\n        display: block;\n        width: 125px; }\n      .igv-navbar .igv-navbar-right-container .igv-zoom-widget svg {\n        display: block; }\n    .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 {\n      color: #737373;\n      font-size: 18px;\n      height: 32px;\n      line-height: 32px;\n      margin-left: 8px;\n      user-select: none;\n      display: flex;\n      flex-flow: row;\n      flex-wrap: nowrap;\n      justify-content: flex-end;\n      align-items: center; }\n      .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div {\n        cursor: pointer;\n        margin-left: unset;\n        margin-right: unset; }\n      .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:first-child {\n        height: 24px;\n        width: 24px;\n        margin-left: unset;\n        margin-right: 8px; }\n      .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:last-child {\n        height: 24px;\n        width: 24px;\n        margin-left: 8px;\n        margin-right: unset; }\n      .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 div:nth-child(even) {\n        width: 0;\n        height: 0;\n        display: none; }\n      .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 input {\n        width: 0;\n        height: 0;\n        display: none; }\n      .igv-navbar .igv-navbar-right-container .igv-zoom-widget-900 svg {\n        display: block; }\n    .igv-navbar .igv-navbar-right-container .igv-zoom-widget-hidden {\n      display: none; }\n\n.igv-navbar-button {\n  display: block;\n  box-sizing: unset;\n  padding-left: 6px;\n  padding-right: 6px;\n  height: 18px;\n  text-transform: capitalize;\n  user-select: none;\n  line-height: 18px;\n  text-align: center;\n  vertical-align: middle;\n  font-family: \"Open Sans\", sans-serif;\n  font-size: 11px;\n  font-weight: 200;\n  color: #737373;\n  background-color: #f3f3f3;\n  border-color: #737373;\n  border-style: solid;\n  border-width: thin;\n  border-radius: 6px; }\n\n.igv-navbar-button-clicked {\n  color: white;\n  background-color: #737373; }\n\n.igv-navbar-button:hover {\n  cursor: pointer; }\n\n.igv-zoom-in-notice-container {\n  z-index: 1024;\n  position: absolute;\n  top: 8px;\n  left: 50%;\n  transform: translate(-50%, 0%);\n  display: flex;\n  flex-direction: row;\n  flex-wrap: nowrap;\n  justify-content: center;\n  align-items: center;\n  background-color: white; }\n  .igv-zoom-in-notice-container > div {\n    padding-left: 4px;\n    padding-right: 4px;\n    padding-top: 2px;\n    padding-bottom: 2px;\n    width: 100%;\n    height: 100%;\n    font-family: \"Open Sans\", sans-serif;\n    font-size: 14px;\n    font-weight: 400;\n    color: #3f3f3f; }\n\n.igv-zoom-in-notice {\n  position: absolute;\n  top: 10px;\n  left: 50%; }\n  .igv-zoom-in-notice div {\n    position: relative;\n    left: -50%;\n    font-family: \"Open Sans\", sans-serif;\n    font-size: medium;\n    font-weight: 400;\n    color: #3f3f3f;\n    background-color: rgba(255, 255, 255, 0.51);\n    z-index: 64; }\n\n.igv-container-spinner {\n  position: absolute;\n  top: 90%;\n  left: 50%;\n  transform: translate(-50%, -50%);\n  z-index: 1024;\n  width: 24px;\n  height: 24px;\n  pointer-events: none;\n  color: #737373; }\n\n.igv-multi-locus-close-button {\n  position: absolute;\n  top: 2px;\n  right: 0;\n  padding-left: 2px;\n  padding-right: 2px;\n  width: 18px;\n  height: 18px;\n  color: #666666;\n  background-color: white;\n  z-index: 1000; }\n  .igv-multi-locus-close-button > svg {\n    vertical-align: top; }\n\n.igv-multi-locus-close-button:hover {\n  cursor: pointer;\n  color: #434343; }\n\n.igv-multi-locus-ruler-label {\n  z-index: 64;\n  position: absolute;\n  top: 2px;\n  left: 0;\n  width: 100%;\n  height: 14px;\n  display: flex;\n  flex-flow: row;\n  flex-wrap: nowrap;\n  justify-content: center;\n  align-items: center; }\n  .igv-multi-locus-ruler-label div {\n    font-family: \"Open Sans\", sans-serif;\n    font-size: 14px;\n    font-weight: 400;\n    color: #101010;\n    background-color: white; }\n\n.igv-multi-locus-ruler-label-square-dot {\n  z-index: 64;\n  position: absolute;\n  left: 50%;\n  top: 5%;\n  transform: translate(-50%, 0%);\n  background-color: white;\n  display: flex;\n  flex-flow: row;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: center; }\n  .igv-multi-locus-ruler-label-square-dot > div:first-child {\n    width: 14px;\n    height: 14px; }\n  .igv-multi-locus-ruler-label-square-dot > div:last-child {\n    margin-left: 16px;\n    font-family: \"Open Sans\", sans-serif;\n    font-size: 14px;\n    font-weight: 400;\n    color: #101010; }\n\n.igv-multi-locus-ruler-label:hover {\n  cursor: pointer; }\n\n.igv-ruler-sweeper {\n  display: none;\n  pointer-events: none;\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 0;\n  height: 100%;\n  z-index: 99999;\n  background-color: rgba(68, 134, 247, 0.25); }\n\n.igv-ruler-tooltip {\n  pointer-events: none;\n  z-index: 128;\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 1px;\n  height: 32px;\n  background-color: transparent;\n  display: flex;\n  flex-flow: row;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: center; }\n  .igv-ruler-tooltip > div {\n    pointer-events: none;\n    width: 128px;\n    height: auto;\n    padding: 1px;\n    color: #373737;\n    font-size: 10px;\n    font-family: \"Open Sans\", sans-serif;\n    font-weight: 400;\n    background-color: white;\n    border-style: solid;\n    border-width: thin;\n    border-color: #373737; }\n\n.igv-track-label {\n  position: absolute;\n  left: 8px;\n  top: 8px;\n  width: auto;\n  height: auto;\n  max-width: 200px;\n  padding-left: 4px;\n  padding-right: 4px;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  font-family: \"Open Sans\", sans-serif;\n  font-size: small;\n  font-weight: 400;\n  text-align: center;\n  user-select: none;\n  -moz-user-select: none;\n  -webkit-user-select: none;\n  border-color: #444;\n  border-radius: 2px;\n  border-style: solid;\n  border-width: thin;\n  background-color: white;\n  z-index: 128;\n  cursor: pointer; }\n\n.igv-track-label:hover,\n.igv-track-label:focus,\n.igv-track-label:active {\n  background-color: #e8e8e8; }\n\n.igv-track-label-popup-shim {\n  padding-left: 8px;\n  padding-right: 8px;\n  padding-top: 4px; }\n\n.igv-center-line {\n  display: none;\n  pointer-events: none;\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  transform: translateX(-50%);\n  z-index: 8;\n  user-select: none;\n  -moz-user-select: none;\n  -webkit-user-select: none;\n  border-left-style: dashed;\n  border-left-width: thin;\n  border-right-style: dashed;\n  border-right-width: thin; }\n\n.igv-center-line-wide {\n  background-color: rgba(0, 0, 0, 0);\n  border-left-color: rgba(127, 127, 127, 0.51);\n  border-right-color: rgba(127, 127, 127, 0.51); }\n\n.igv-center-line-thin {\n  background-color: rgba(0, 0, 0, 0);\n  border-left-color: rgba(127, 127, 127, 0.51);\n  border-right-color: rgba(0, 0, 0, 0); }\n\n.igv-cursor-guide-horizontal {\n  display: none;\n  pointer-events: none;\n  user-select: none;\n  -moz-user-select: none;\n  -webkit-user-select: none;\n  position: absolute;\n  left: 0;\n  right: 0;\n  top: 50%;\n  height: 1px;\n  z-index: 1;\n  margin-left: 50px;\n  margin-right: 54px;\n  border-top-style: dotted;\n  border-top-width: thin;\n  border-top-color: rgba(127, 127, 127, 0.76); }\n\n.igv-cursor-guide-vertical {\n  pointer-events: none;\n  user-select: none;\n  -moz-user-select: none;\n  -webkit-user-select: none;\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 50%;\n  width: 1px;\n  z-index: 1;\n  border-left-style: dotted;\n  border-left-width: thin;\n  border-left-color: rgba(127, 127, 127, 0.76);\n  display: none; }\n\n.igv-user-feedback {\n  position: fixed;\n  top: 50%;\n  left: 50%;\n  transform: translate(-50%, -50%);\n  width: 512px;\n  height: 360px;\n  z-index: 2048;\n  background-color: white;\n  border-color: #a2a2a2;\n  border-style: solid;\n  border-width: thin;\n  font-family: \"Open Sans\", sans-serif;\n  font-size: medium;\n  font-weight: 400;\n  color: #444;\n  display: flex;\n  flex-direction: column;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: center; }\n  .igv-user-feedback div:first-child {\n    position: relative;\n    height: 24px;\n    width: 100%;\n    background-color: white;\n    border-bottom-color: #a2a2a2;\n    border-bottom-style: solid;\n    border-bottom-width: thin; }\n    .igv-user-feedback div:first-child div {\n      position: absolute;\n      top: 2px;\n      width: 16px;\n      height: 16px;\n      background-color: transparent; }\n    .igv-user-feedback div:first-child div:first-child {\n      left: 8px; }\n    .igv-user-feedback div:first-child div:last-child {\n      cursor: pointer;\n      right: 8px; }\n  .igv-user-feedback div:last-child {\n    width: 100%;\n    height: calc(100% - 24px);\n    border-width: 0; }\n    .igv-user-feedback div:last-child div {\n      width: auto;\n      height: auto;\n      margin: 8px; }\n\n.igv-generic-dialog-container {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 300px;\n  height: 200px;\n  border-color: #7F7F7F;\n  border-radius: 4px;\n  border-style: solid;\n  border-width: thin;\n  font-family: \"Open Sans\", sans-serif;\n  font-size: medium;\n  font-weight: 400;\n  z-index: 2048;\n  background-color: white;\n  display: flex;\n  flex-flow: column;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: center; }\n  .igv-generic-dialog-container .igv-generic-dialog-header {\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: flex-end;\n    align-items: center;\n    width: 100%;\n    height: 24px;\n    cursor: move;\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n    border-bottom-color: #7F7F7F;\n    border-bottom-style: solid;\n    border-bottom-width: thin;\n    background-color: #eee; }\n    .igv-generic-dialog-container .igv-generic-dialog-header div {\n      margin-right: 4px;\n      margin-bottom: 2px;\n      height: 12px;\n      width: 12px;\n      color: #7F7F7F; }\n    .igv-generic-dialog-container .igv-generic-dialog-header div:hover {\n      cursor: pointer;\n      color: #444; }\n  .igv-generic-dialog-container .igv-generic-dialog-one-liner {\n    color: #373737;\n    width: 95%;\n    height: 24px;\n    line-height: 24px;\n    text-align: left;\n    margin-top: 8px;\n    padding-left: 8px;\n    overflow-wrap: break-word;\n    background-color: white; }\n  .igv-generic-dialog-container .igv-generic-dialog-label-input {\n    margin-top: 8px;\n    width: 95%;\n    height: 24px;\n    color: #373737;\n    line-height: 24px;\n    padding-left: 8px;\n    background-color: white;\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: flex-start;\n    align-items: center; }\n    .igv-generic-dialog-container .igv-generic-dialog-label-input div {\n      width: 30%;\n      height: 100%;\n      font-size: 16px;\n      text-align: right;\n      padding-right: 8px;\n      background-color: white; }\n    .igv-generic-dialog-container .igv-generic-dialog-label-input input {\n      display: block;\n      height: 100%;\n      width: 100%;\n      padding-left: 4px;\n      font-family: \"Open Sans\", sans-serif;\n      font-weight: 400;\n      color: #373737;\n      text-align: left;\n      outline: none;\n      border-style: solid;\n      border-width: thin;\n      border-color: #7F7F7F;\n      background-color: white; }\n    .igv-generic-dialog-container .igv-generic-dialog-label-input input {\n      width: 50%;\n      font-size: 16px; }\n  .igv-generic-dialog-container .igv-generic-dialog-input {\n    margin-top: 8px;\n    width: calc(100% - 16px);\n    height: 24px;\n    color: #373737;\n    line-height: 24px;\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: space-around;\n    align-items: center; }\n    .igv-generic-dialog-container .igv-generic-dialog-input input {\n      display: block;\n      height: 100%;\n      width: 100%;\n      padding-left: 4px;\n      font-family: \"Open Sans\", sans-serif;\n      font-weight: 400;\n      color: #373737;\n      text-align: left;\n      outline: none;\n      border-style: solid;\n      border-width: thin;\n      border-color: #7F7F7F;\n      background-color: white; }\n    .igv-generic-dialog-container .igv-generic-dialog-input input {\n      font-size: 16px; }\n  .igv-generic-dialog-container .igv-generic-dialog-ok-cancel {\n    width: 100%;\n    height: 28px;\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: space-around;\n    align-items: center; }\n    .igv-generic-dialog-container .igv-generic-dialog-ok-cancel div {\n      margin-top: 32px;\n      color: white;\n      font-family: \"Open Sans\", sans-serif;\n      font-size: 14px;\n      font-weight: 400;\n      width: 75px;\n      height: 28px;\n      line-height: 28px;\n      text-align: center;\n      border-color: transparent;\n      border-style: solid;\n      border-width: thin;\n      border-radius: 2px; }\n    .igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child {\n      margin-left: 32px;\n      margin-right: 0;\n      background-color: #5ea4e0; }\n    .igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child {\n      margin-left: 0;\n      margin-right: 32px;\n      background-color: #c4c4c4; }\n    .igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:first-child:hover {\n      cursor: pointer;\n      background-color: #3b5c7f; }\n    .igv-generic-dialog-container .igv-generic-dialog-ok-cancel div:last-child:hover {\n      cursor: pointer;\n      background-color: #7f7f7f; }\n  .igv-generic-dialog-container .igv-generic-dialog-ok {\n    width: 100%;\n    height: 36px;\n    margin-top: 32px;\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: space-around;\n    align-items: center; }\n    .igv-generic-dialog-container .igv-generic-dialog-ok div {\n      width: 98px;\n      height: 36px;\n      line-height: 36px;\n      text-align: center;\n      color: white;\n      font-family: \"Open Sans\", sans-serif;\n      font-size: medium;\n      font-weight: 400;\n      border-color: white;\n      border-style: solid;\n      border-width: thin;\n      border-radius: 4px;\n      background-color: #2B81AF; }\n    .igv-generic-dialog-container .igv-generic-dialog-ok div:hover {\n      cursor: pointer;\n      background-color: #25597f; }\n\n.igv-generic-container {\n  position: absolute;\n  top: 0;\n  left: 0;\n  z-index: 2048;\n  background-color: white;\n  cursor: pointer;\n  display: flex;\n  flex-direction: row;\n  flex-wrap: wrap;\n  justify-content: flex-start;\n  align-items: center; }\n  .igv-generic-container div:first-child {\n    cursor: move;\n    display: flex;\n    flex-flow: row;\n    flex-wrap: nowrap;\n    justify-content: flex-end;\n    align-items: center;\n    height: 24px;\n    width: 100%;\n    background-color: #dddddd; }\n    .igv-generic-container div:first-child i {\n      display: block;\n      color: #5f5f5f;\n      cursor: pointer;\n      width: 14px;\n      height: 14px;\n      margin-right: 8px;\n      margin-bottom: 4px; }\n\n.igv-menu-popup {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: max-content;\n  z-index: 4096;\n  cursor: pointer;\n  font-family: \"Open Sans\", sans-serif;\n  font-size: small;\n  font-weight: 400;\n  color: #4b4b4b;\n  background: white;\n  border-radius: 4px;\n  border-color: #7F7F7F;\n  border-style: solid;\n  border-width: thin;\n  display: flex;\n  flex-flow: column;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: flex-end;\n  text-align: left; }\n  .igv-menu-popup > div:not(:first-child) {\n    width: 100%; }\n    .igv-menu-popup > div:not(:first-child) > div {\n      background: white; }\n    .igv-menu-popup > div:not(:first-child) > div.context-menu {\n      padding-left: 4px;\n      padding-right: 4px; }\n    .igv-menu-popup > div:not(:first-child) > div:last-child {\n      border-bottom-left-radius: 4px;\n      border-bottom-right-radius: 4px;\n      border-bottom-color: transparent;\n      border-bottom-style: solid;\n      border-bottom-width: thin; }\n    .igv-menu-popup > div:not(:first-child) > div:hover {\n      background: #efefef; }\n\n.igv-menu-popup-shim {\n  padding-left: 8px;\n  padding-right: 8px;\n  padding-bottom: 1px;\n  padding-top: 1px; }\n\n.igv-menu-popup-header {\n  position: relative;\n  width: 100%;\n  height: 24px;\n  cursor: move;\n  border-top-color: transparent;\n  border-top-left-radius: 4px;\n  border-top-right-radius: 4px;\n  border-bottom-color: #7F7F7F;\n  border-bottom-style: solid;\n  border-bottom-width: thin;\n  background-color: #eee;\n  display: flex;\n  flex-flow: row;\n  flex-wrap: nowrap;\n  justify-content: flex-end;\n  align-items: center; }\n  .igv-menu-popup-header div {\n    margin-right: 4px;\n    height: 12px;\n    width: 12px;\n    color: #7F7F7F; }\n  .igv-menu-popup-header div:hover {\n    cursor: pointer;\n    color: #444; }\n\n.igv-menu-popup-check-container {\n  display: flex;\n  flex-flow: row;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: center;\n  width: 100%;\n  height: 20px;\n  margin-right: 4px;\n  background-color: transparent; }\n  .igv-menu-popup-check-container div {\n    padding-top: 2px;\n    padding-left: 8px; }\n  .igv-menu-popup-check-container div:first-child {\n    position: relative;\n    width: 12px;\n    height: 12px; }\n    .igv-menu-popup-check-container div:first-child svg {\n      position: absolute;\n      width: 12px;\n      height: 12px; }\n\n.igv-user-feedback {\n  position: fixed;\n  top: 50%;\n  left: 50%;\n  transform: translate(-50%, -50%);\n  width: 512px;\n  height: 360px;\n  z-index: 2048;\n  background-color: white;\n  border-color: #a2a2a2;\n  border-style: solid;\n  border-width: thin;\n  font-family: \"Open Sans\", sans-serif;\n  font-size: medium;\n  font-weight: 400;\n  color: #444;\n  display: flex;\n  flex-direction: column;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: center; }\n  .igv-user-feedback div:first-child {\n    position: relative;\n    height: 24px;\n    width: 100%;\n    background-color: white;\n    border-bottom-color: #a2a2a2;\n    border-bottom-style: solid;\n    border-bottom-width: thin; }\n    .igv-user-feedback div:first-child div {\n      position: absolute;\n      top: 2px;\n      width: 16px;\n      height: 16px;\n      background-color: transparent; }\n    .igv-user-feedback div:first-child div:first-child {\n      left: 8px; }\n    .igv-user-feedback div:first-child div:last-child {\n      cursor: pointer;\n      right: 8px; }\n  .igv-user-feedback div:last-child {\n    width: 100%;\n    height: calc(100% - 24px);\n    border-width: 0; }\n    .igv-user-feedback div:last-child div {\n      width: auto;\n      height: auto;\n      margin: 8px; }\n\n.igv-loading-spinner-container {\n  z-index: 1024;\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  transform: translate(-50%, -50%);\n  width: 32px;\n  height: 32px;\n  display: flex;\n  flex-direction: row;\n  flex-wrap: nowrap;\n  justify-content: center;\n  align-items: center; }\n  .igv-loading-spinner-container > div {\n    box-sizing: border-box;\n    width: 100%;\n    height: 100%;\n    border-radius: 50%;\n    border: 4px solid rgba(128, 128, 128, 0.5);\n    border-top-color: white;\n    animation: spin 1s ease-in-out infinite;\n    -webkit-animation: spin 1s ease-in-out infinite; }\n\n@keyframes spin {\n  to {\n    -webkit-transform: rotate(360deg);\n    transform: rotate(360deg); } }\n@-webkit-keyframes spin {\n  to {\n    -webkit-transform: rotate(360deg);\n    transform: rotate(360deg); } }\n.igv-container {\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: flex-start;\n  padding-top: 4px;\n  user-select: none;\n  -webkit-user-select: none;\n  -ms-user-select: none; }\n\n.igv-viewport {\n  position: relative;\n  margin-top: 5px;\n  overflow-x: hidden;\n  overflow-y: hidden; }\n\n.igv-viewport-content {\n  position: relative;\n  width: 100%; }\n  .igv-viewport-content > canvas {\n    position: relative;\n    display: block; }\n\n.igv-column-container {\n  position: relative;\n  display: flex;\n  flex-direction: row;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: stretch;\n  width: 100%; }\n\n.igv-column-shim {\n  width: 1px;\n  margin-left: 2px;\n  margin-right: 2px;\n  background-color: #545453; }\n\n.igv-column {\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: flex-start;\n  box-sizing: border-box;\n  height: 100%; }\n\n.igv-axis-column {\n  display: flex;\n  flex-direction: column;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: flex-start;\n  box-sizing: border-box;\n  height: 100%;\n  width: 50px; }\n  .igv-axis-column > div {\n    margin-top: 5px;\n    width: 100%; }\n\n.igv-sample-name-column {\n  display: flex;\n  flex-direction: column;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: flex-start;\n  box-sizing: border-box;\n  height: 100%; }\n\n.igv-scrollbar-column {\n  display: flex;\n  flex-direction: column;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: flex-start;\n  box-sizing: border-box;\n  height: 100%;\n  width: 14px; }\n  .igv-scrollbar-column > div {\n    position: relative;\n    margin-top: 5px;\n    width: 14px; }\n    .igv-scrollbar-column > div > div {\n      cursor: pointer;\n      position: absolute;\n      top: 0;\n      left: 2px;\n      width: 8px;\n      border-width: 1px;\n      border-style: solid;\n      border-color: #c4c4c4;\n      border-top-left-radius: 4px;\n      border-top-right-radius: 4px;\n      border-bottom-left-radius: 4px;\n      border-bottom-right-radius: 4px; }\n    .igv-scrollbar-column > div > div:hover {\n      background-color: #c4c4c4; }\n\n.igv-track-drag-column {\n  display: flex;\n  flex-direction: column;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: flex-start;\n  box-sizing: border-box;\n  height: 100%;\n  width: 12px;\n  background-color: white; }\n  .igv-track-drag-column > .igv-track-drag-handle {\n    z-index: 512;\n    position: relative;\n    cursor: pointer;\n    margin-top: 5px;\n    width: 100%;\n    border-style: solid;\n    border-width: 0;\n    border-top-right-radius: 6px;\n    border-bottom-right-radius: 6px;\n    background-color: #c4c4c4; }\n  .igv-track-drag-column .igv-track-drag-handle-hover {\n    background-color: #787878; }\n  .igv-track-drag-column > .igv-track-drag-shim {\n    position: relative;\n    margin-top: 5px;\n    width: 100%;\n    border-style: solid;\n    border-width: 0; }\n\n.igv-gear-menu-column {\n  display: flex;\n  flex-direction: column;\n  flex-wrap: nowrap;\n  justify-content: flex-start;\n  align-items: flex-start;\n  box-sizing: border-box;\n  height: 100%;\n  width: 28px; }\n  .igv-gear-menu-column > div {\n    display: flex;\n    flex-direction: column;\n    flex-wrap: nowrap;\n    justify-content: flex-start;\n    align-items: center;\n    margin-top: 5px;\n    width: 100%;\n    background: white; }\n    .igv-gear-menu-column > div > div {\n      position: relative;\n      margin-top: 4px;\n      width: 16px;\n      height: 16px;\n      color: #7F7F7F; }\n    .igv-gear-menu-column > div > div:hover {\n      cursor: pointer;\n      color: #444; }\n\n/*# sourceMappingURL=dom.css.map */\n';
      var style = document.createElement('style');
      style.setAttribute('type', 'text/css');
      style.innerHTML = css;
      document.head.insertBefore(style, document.head.childNodes[document.head.childNodes.length - 1]);
    }

    // Defines the top-level API for the igv module
    const setApiKey = igvxhr.setApiKey;
    embedCSS();

    function setGoogleOauthToken(accessToken) {
      return oauth.setToken(accessToken);
    }

    function setOauthToken(accessToken, host) {
      return oauth.setToken(accessToken, host);
    }

    var index = {
      IGVGraphics,
      MenuUtils,
      DataRangeDialog,
      createTrack,
      createBrowser,
      removeBrowser,
      removeAllBrowsers,
      visibilityChange,
      setGoogleOauthToken,
      setOauthToken,
      oauth,
      version,
      setApiKey,
      doAutoscale,
      TrackView
    };

    return index;

}));
